forked from Mirror/frr
bgpd: implement retain route-target all behaviour
A new command is available under SAFI_MPLS_VPN: With this command, the BGP vpnvx prefixes received are not kept, if there are no VRF interested in importing those vpn entries. A soft refresh is performed if there is a change of configuration: retain cmd, vrf import settings, or route-map change. Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
parent
1004137bf3
commit
a486300b26
|
@ -1472,7 +1472,7 @@ void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||||
struct bgp *from_bgp, /* from */
|
struct bgp *from_bgp, /* from */
|
||||||
struct bgp_path_info *path_vpn) /* route */
|
struct bgp_path_info *path_vpn) /* route */
|
||||||
|
@ -1498,7 +1498,7 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||||
if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) {
|
if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) {
|
||||||
if (debug)
|
if (debug)
|
||||||
zlog_debug("%s: skipping: %s", __func__, debugmsg);
|
zlog_debug("%s: skipping: %s", __func__, debugmsg);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for intersection of route targets */
|
/* Check for intersection of route targets */
|
||||||
|
@ -1509,7 +1509,7 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||||
zlog_debug(
|
zlog_debug(
|
||||||
"from vpn (%s) to vrf (%s), skipping after no intersection of route targets",
|
"from vpn (%s) to vrf (%s), skipping after no intersection of route targets",
|
||||||
from_bgp->name_pretty, to_bgp->name_pretty);
|
from_bgp->name_pretty, to_bgp->name_pretty);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
|
@ -1604,7 +1604,7 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||||
to_bgp->vpn_policy[afi]
|
to_bgp->vpn_policy[afi]
|
||||||
.rmap[BGP_VPN_POLICY_DIR_FROMVPN]
|
.rmap[BGP_VPN_POLICY_DIR_FROMVPN]
|
||||||
->name);
|
->name);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* if route-map changed nexthop, don't nexthop-self on output
|
* if route-map changed nexthop, don't nexthop-self on output
|
||||||
|
@ -1674,13 +1674,15 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||||
leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels,
|
leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels,
|
||||||
num_labels, src_vrf, &nexthop_orig, nexthop_self_flag,
|
num_labels, src_vrf, &nexthop_orig, nexthop_self_flag,
|
||||||
debug);
|
debug);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
|
bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
|
||||||
struct bgp_path_info *path_vpn) /* route */
|
struct bgp_path_info *path_vpn) /* route */
|
||||||
{
|
{
|
||||||
struct listnode *mnode, *mnnode;
|
struct listnode *mnode, *mnnode;
|
||||||
struct bgp *bgp;
|
struct bgp *bgp;
|
||||||
|
bool leak_success = false;
|
||||||
|
|
||||||
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
|
int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
|
||||||
|
|
||||||
|
@ -1692,9 +1694,11 @@ void vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
|
||||||
|
|
||||||
if (!path_vpn->extra
|
if (!path_vpn->extra
|
||||||
|| path_vpn->extra->bgp_orig != bgp) { /* no loop */
|
|| path_vpn->extra->bgp_orig != bgp) { /* no loop */
|
||||||
vpn_leak_to_vrf_update_onevrf(bgp, from_bgp, path_vpn);
|
leak_success |= vpn_leak_to_vrf_update_onevrf(
|
||||||
|
bgp, from_bgp, path_vpn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return leak_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, /* from */
|
void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, /* from */
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "bgpd/bgp_route.h"
|
#include "bgpd/bgp_route.h"
|
||||||
#include "bgpd/bgp_rd.h"
|
#include "bgpd/bgp_rd.h"
|
||||||
#include "bgpd/bgp_zebra.h"
|
#include "bgpd/bgp_zebra.h"
|
||||||
|
#include "bgpd/bgp_vty.h"
|
||||||
|
|
||||||
#define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)
|
#define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)
|
||||||
#define MPLS_LABEL_IS_NULL(label) \
|
#define MPLS_LABEL_IS_NULL(label) \
|
||||||
|
@ -70,7 +71,7 @@ extern void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi);
|
||||||
extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
|
extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
|
||||||
afi_t afi);
|
afi_t afi);
|
||||||
|
|
||||||
extern void vpn_leak_to_vrf_update(struct bgp *from_bgp,
|
extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
|
||||||
struct bgp_path_info *path_vpn);
|
struct bgp_path_info *path_vpn);
|
||||||
|
|
||||||
extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp,
|
extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp,
|
||||||
|
@ -233,8 +234,14 @@ static inline void vpn_leak_postchange(enum vpn_policy_direction direction,
|
||||||
if (!bgp_vpn)
|
if (!bgp_vpn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (direction == BGP_VPN_POLICY_DIR_FROMVPN)
|
if (direction == BGP_VPN_POLICY_DIR_FROMVPN) {
|
||||||
vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi);
|
/* trigger a flush to re-sync with ADJ-RIB-in */
|
||||||
|
if (!CHECK_FLAG(bgp_vpn->af_flags[afi][SAFI_MPLS_VPN],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL))
|
||||||
|
bgp_clear_soft_in(bgp_vpn, afi, SAFI_MPLS_VPN);
|
||||||
|
else
|
||||||
|
vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi);
|
||||||
|
}
|
||||||
if (direction == BGP_VPN_POLICY_DIR_TOVPN) {
|
if (direction == BGP_VPN_POLICY_DIR_TOVPN) {
|
||||||
|
|
||||||
if (bgp_vrf->vpn_policy[afi].tovpn_label !=
|
if (bgp_vrf->vpn_policy[afi].tovpn_label !=
|
||||||
|
|
|
@ -3785,6 +3785,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||||
uint8_t pi_sub_type = 0;
|
uint8_t pi_sub_type = 0;
|
||||||
bool force_evpn_import = false;
|
bool force_evpn_import = false;
|
||||||
safi_t orig_safi = safi;
|
safi_t orig_safi = safi;
|
||||||
|
bool leak_success = true;
|
||||||
|
|
||||||
if (frrtrace_enabled(frr_bgp, process_update)) {
|
if (frrtrace_enabled(frr_bgp, process_update)) {
|
||||||
char pfxprint[PREFIX2STR_BUFFER];
|
char pfxprint[PREFIX2STR_BUFFER];
|
||||||
|
@ -4410,7 +4411,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||||
if ((SAFI_MPLS_VPN == safi)
|
if ((SAFI_MPLS_VPN == safi)
|
||||||
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
||||||
|
|
||||||
vpn_leak_to_vrf_update(bgp, pi);
|
leak_success = vpn_leak_to_vrf_update(bgp, pi);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_BGP_VNC
|
#ifdef ENABLE_BGP_VNC
|
||||||
|
@ -4425,7 +4426,13 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||||
type, sub_type, NULL);
|
type, sub_type, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if ((safi == SAFI_MPLS_VPN) &&
|
||||||
|
!CHECK_FLAG(bgp->af_flags[afi][safi],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
|
||||||
|
!leak_success) {
|
||||||
|
bgp_unlink_nexthop(pi);
|
||||||
|
bgp_path_info_delete(dest, pi);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
} // End of implicit withdraw
|
} // End of implicit withdraw
|
||||||
|
|
||||||
|
@ -4559,8 +4566,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||||
}
|
}
|
||||||
if ((SAFI_MPLS_VPN == safi)
|
if ((SAFI_MPLS_VPN == safi)
|
||||||
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
||||||
|
leak_success = vpn_leak_to_vrf_update(bgp, new);
|
||||||
vpn_leak_to_vrf_update(bgp, new);
|
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_BGP_VNC
|
#ifdef ENABLE_BGP_VNC
|
||||||
if (SAFI_MPLS_VPN == safi) {
|
if (SAFI_MPLS_VPN == safi) {
|
||||||
|
@ -4574,6 +4580,13 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||||
sub_type, NULL);
|
sub_type, NULL);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if ((safi == SAFI_MPLS_VPN) &&
|
||||||
|
!CHECK_FLAG(bgp->af_flags[afi][safi],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
|
||||||
|
!leak_success) {
|
||||||
|
bgp_unlink_nexthop(new);
|
||||||
|
bgp_path_info_delete(dest, new);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -951,14 +951,24 @@ static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi,
|
||||||
{
|
{
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case BGP_ERR_AF_UNCONFIGURED:
|
case BGP_ERR_AF_UNCONFIGURED:
|
||||||
vty_out(vty,
|
if (vty)
|
||||||
"%% BGP: Enable %s address family for the neighbor %s\n",
|
vty_out(vty,
|
||||||
get_afi_safi_str(afi, safi, false), peer->host);
|
"%% BGP: Enable %s address family for the neighbor %s\n",
|
||||||
|
get_afi_safi_str(afi, safi, false), peer->host);
|
||||||
|
else
|
||||||
|
zlog_warn(
|
||||||
|
"%% BGP: Enable %s address family for the neighbor %s\n",
|
||||||
|
get_afi_safi_str(afi, safi, false), peer->host);
|
||||||
break;
|
break;
|
||||||
case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:
|
case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:
|
||||||
vty_out(vty,
|
if (vty)
|
||||||
"%% BGP: Inbound soft reconfig for %s not possible as it\n has neither refresh capability, nor inbound soft reconfig\n",
|
vty_out(vty,
|
||||||
peer->host);
|
"%% BGP: Inbound soft reconfig for %s not possible as it\n has neither refresh capability, nor inbound soft reconfig\n",
|
||||||
|
peer->host);
|
||||||
|
else
|
||||||
|
zlog_warn(
|
||||||
|
"%% BGP: Inbound soft reconfig for %s not possible as it\n has neither refresh capability, nor inbound soft reconfig\n",
|
||||||
|
peer->host);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1274,6 +1284,11 @@ static void bgp_clear_star_soft_out(struct vty *vty, const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void bgp_clear_soft_in(struct bgp *bgp, afi_t afi, safi_t safi)
|
||||||
|
{
|
||||||
|
bgp_clear(NULL, bgp, afi, safi, clear_all, BGP_CLEAR_SOFT_IN, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef VTYSH_EXTRACT_PL
|
#ifndef VTYSH_EXTRACT_PL
|
||||||
#include "bgpd/bgp_vty_clippy.c"
|
#include "bgpd/bgp_vty_clippy.c"
|
||||||
#endif
|
#endif
|
||||||
|
@ -16315,6 +16330,34 @@ DEFUN(no_neighbor_tcp_mss, no_neighbor_tcp_mss_cmd,
|
||||||
return peer_tcp_mss_vty(vty, argv[peer_index]->arg, NULL);
|
return peer_tcp_mss_vty(vty, argv[peer_index]->arg, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFPY(bgp_retain_route_target, bgp_retain_route_target_cmd,
|
||||||
|
"[no$no] bgp retain route-target all",
|
||||||
|
NO_STR BGP_STR
|
||||||
|
"Retain BGP updates\n"
|
||||||
|
"Retain BGP updates based on route-target values\n"
|
||||||
|
"Retain all BGP updates\n")
|
||||||
|
{
|
||||||
|
bool check;
|
||||||
|
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
|
||||||
|
|
||||||
|
check = CHECK_FLAG(bgp->af_flags[bgp_node_afi(vty)][bgp_node_safi(vty)],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
|
||||||
|
if (check != !no) {
|
||||||
|
if (!no)
|
||||||
|
SET_FLAG(bgp->af_flags[bgp_node_afi(vty)]
|
||||||
|
[bgp_node_safi(vty)],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
|
||||||
|
else
|
||||||
|
UNSET_FLAG(bgp->af_flags[bgp_node_afi(vty)]
|
||||||
|
[bgp_node_safi(vty)],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
|
||||||
|
/* trigger a flush to re-sync with ADJ-RIB-in */
|
||||||
|
bgp_clear(vty, bgp, bgp_node_afi(vty), bgp_node_safi(vty),
|
||||||
|
clear_all, BGP_CLEAR_SOFT_IN, NULL);
|
||||||
|
}
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
|
static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
|
||||||
afi_t afi, safi_t safi)
|
afi_t afi, safi_t safi)
|
||||||
{
|
{
|
||||||
|
@ -17197,6 +17240,14 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bgp_vpn_config_write(struct vty *vty, struct bgp *bgp, afi_t afi,
|
||||||
|
safi_t safi)
|
||||||
|
{
|
||||||
|
if (!CHECK_FLAG(bgp->af_flags[afi][safi],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL))
|
||||||
|
vty_out(vty, " no bgp retain route-target all\n");
|
||||||
|
}
|
||||||
|
|
||||||
/* Address family based peer configuration display. */
|
/* Address family based peer configuration display. */
|
||||||
static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
|
static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
|
||||||
safi_t safi)
|
safi_t safi)
|
||||||
|
@ -17267,6 +17318,9 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
|
||||||
if (safi == SAFI_FLOWSPEC)
|
if (safi == SAFI_FLOWSPEC)
|
||||||
bgp_fs_config_write_pbr(vty, bgp, afi, safi);
|
bgp_fs_config_write_pbr(vty, bgp, afi, safi);
|
||||||
|
|
||||||
|
if (safi == SAFI_MPLS_VPN)
|
||||||
|
bgp_vpn_config_write(vty, bgp, afi, safi);
|
||||||
|
|
||||||
if (safi == SAFI_UNICAST) {
|
if (safi == SAFI_UNICAST) {
|
||||||
bgp_vpn_policy_config_write_afi(vty, bgp, afi);
|
bgp_vpn_policy_config_write_afi(vty, bgp, afi);
|
||||||
if (CHECK_FLAG(bgp->af_flags[afi][safi],
|
if (CHECK_FLAG(bgp->af_flags[afi][safi],
|
||||||
|
@ -19251,6 +19305,10 @@ void bgp_vty_init(void)
|
||||||
install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd);
|
install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd);
|
||||||
install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
|
install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
|
||||||
|
|
||||||
|
/* BGP retain all route-target */
|
||||||
|
install_element(BGP_VPNV4_NODE, &bgp_retain_route_target_cmd);
|
||||||
|
install_element(BGP_VPNV6_NODE, &bgp_retain_route_target_cmd);
|
||||||
|
|
||||||
/* "clear ip bgp commands" */
|
/* "clear ip bgp commands" */
|
||||||
install_element(ENABLE_NODE, &clear_ip_bgp_all_cmd);
|
install_element(ENABLE_NODE, &clear_ip_bgp_all_cmd);
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,7 @@ struct bgp;
|
||||||
"endOfRibSentAfterUpdate"); \
|
"endOfRibSentAfterUpdate"); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
extern void bgp_clear_soft_in(struct bgp *bgp, afi_t afi, safi_t safi);
|
||||||
extern void bgp_vty_init(void);
|
extern void bgp_vty_init(void);
|
||||||
extern void community_alias_vty(void);
|
extern void community_alias_vty(void);
|
||||||
extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
|
extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
|
||||||
|
|
|
@ -3240,6 +3240,8 @@ static struct bgp *bgp_create(as_t *as, const char *name,
|
||||||
bgp->vpn_policy[afi].export_vrf = list_new();
|
bgp->vpn_policy[afi].export_vrf = list_new();
|
||||||
bgp->vpn_policy[afi].export_vrf->del =
|
bgp->vpn_policy[afi].export_vrf->del =
|
||||||
bgp_vrf_string_name_delete;
|
bgp_vrf_string_name_delete;
|
||||||
|
SET_FLAG(bgp->af_flags[afi][SAFI_MPLS_VPN],
|
||||||
|
BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
|
||||||
}
|
}
|
||||||
if (name)
|
if (name)
|
||||||
bgp->name = XSTRDUP(MTYPE_BGP, name);
|
bgp->name = XSTRDUP(MTYPE_BGP, name);
|
||||||
|
|
|
@ -527,6 +527,8 @@ struct bgp {
|
||||||
/* vrf-route leaking flags */
|
/* vrf-route leaking flags */
|
||||||
#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
|
#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
|
||||||
#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
|
#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
|
||||||
|
/* vpnvx retain flag */
|
||||||
|
#define BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL (1 << 11)
|
||||||
|
|
||||||
/* BGP per AF peer count */
|
/* BGP per AF peer count */
|
||||||
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
|
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
|
||||||
|
|
|
@ -2827,6 +2827,14 @@ address-family:
|
||||||
The CLI will disallow attempts to configure incompatible leaking
|
The CLI will disallow attempts to configure incompatible leaking
|
||||||
modes.
|
modes.
|
||||||
|
|
||||||
|
.. clicmd:: bgp retain route-target all
|
||||||
|
|
||||||
|
It is possible to retain or not VPN prefixes that are not imported by local
|
||||||
|
VRF configuration. This can be done via the following command in the context
|
||||||
|
of the global VPNv4/VPNv6 family. This command defaults to on and is not
|
||||||
|
displayed.
|
||||||
|
The `no bgp retain route-target all` form of the command is displayed.
|
||||||
|
|
||||||
.. _bgp-l3vpn-srv6:
|
.. _bgp-l3vpn-srv6:
|
||||||
|
|
||||||
L3VPN SRv6
|
L3VPN SRv6
|
||||||
|
|
Loading…
Reference in a new issue