summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_bfd.c14
-rw-r--r--bgpd/bgp_bmp.c5
-rw-r--r--bgpd/bgp_damp.c6
-rw-r--r--bgpd/bgp_debug.c2
-rw-r--r--bgpd/bgp_evpn_vty.c15
-rw-r--r--bgpd/bgp_flowspec_vty.c7
-rw-r--r--bgpd/bgp_main.c15
-rw-r--r--bgpd/bgp_route.c211
-rw-r--r--bgpd/bgp_route.h5
-rw-r--r--bgpd/bgp_updgrp_adv.c9
-rw-r--r--bgpd/bgp_vpn.c2
-rw-r--r--bgpd/bgpd.c8
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--doc/manpages/frr-watchfrr.rst16
-rw-r--r--doc/user/bfd.rst11
-rw-r--r--doc/user/bgp.rst73
-rw-r--r--doc/user/ospf6d.rst7
-rw-r--r--doc/user/pbr.rst16
-rw-r--r--isisd/isis_bfd.c81
-rw-r--r--isisd/isis_bfd.h6
-rw-r--r--isisd/isis_cli.c42
-rw-r--r--isisd/isis_nb.c15
-rw-r--r--isisd/isis_nb.h9
-rw-r--r--isisd/isis_nb_config.c43
-rw-r--r--isisd/isis_vty_fabricd.c4
-rw-r--r--ldpd/l2vpn.c12
-rw-r--r--ldpd/lde.c2
-rw-r--r--ldpd/lde.h5
-rw-r--r--ldpd/lde_lib.c17
-rw-r--r--lib/bfd.c27
-rw-r--r--lib/bfd.h4
-rw-r--r--lib/command.h12
-rw-r--r--lib/libfrr.c10
-rw-r--r--lib/nexthop.c127
-rw-r--r--lib/nexthop.h25
-rw-r--r--lib/nexthop_group.c81
-rw-r--r--lib/nexthop_group.h6
-rw-r--r--lib/pbr.h7
-rwxr-xr-xlib/route_types.pl2
-rw-r--r--lib/stream.c14
-rw-r--r--lib/subdir.am3
-rw-r--r--lib/vrf.c6
-rw-r--r--lib/zclient.c37
-rw-r--r--lib/zclient.h5
-rw-r--r--lib/zlog.c6
-rw-r--r--ospf6d/ospf6_area.c16
-rw-r--r--ospf6d/ospf6_area.h15
-rw-r--r--ospf6d/ospf6_bfd.c2
-rw-r--r--ospf6d/ospf6_top.c23
-rw-r--r--ospfd/ospf_bfd.c2
-rw-r--r--ospfd/ospf_vty.c6
-rw-r--r--pbrd/pbr_map.c55
-rw-r--r--pbrd/pbr_map.h3
-rw-r--r--pbrd/pbr_vty.c107
-rw-r--r--pbrd/pbr_zebra.c1
-rw-r--r--pimd/pim_bfd.c2
-rw-r--r--pimd/pim_cmd.c8
-rw-r--r--pimd/subdir.am6
-rw-r--r--sharpd/sharp_vty.c3
-rw-r--r--sharpd/sharp_zebra.c4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bfdd.conf9
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r3/isisd.conf1
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json4
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/bgpd.conf2
-rw-r--r--tests/topotests/bfd-profiles-topo1/r4/isisd.conf1
-rwxr-xr-x[-rw-r--r--]tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_prefix_sid/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf1
-rw-r--r--tests/topotests/evpn_type5_test_topo1/__init__.py0
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json887
-rw-r--r--tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json887
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py1048
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py2117
-rw-r--r--tests/topotests/lib/bgp.py724
-rw-r--r--tests/topotests/lib/common_config.py571
-rw-r--r--tests/topotests/pbr-topo1/r1/pbr-map.json26
-rw-r--r--tests/topotests/pbr-topo1/r1/pbrd.conf10
-rw-r--r--tools/etc/frr/daemons5
-rwxr-xr-xtools/frr-reload.py44
-rwxr-xr-xtools/frr.in2
-rw-r--r--tools/frr@.service25
-rw-r--r--tools/frrcommon.sh.in22
-rw-r--r--tools/frrinit.sh.in5
-rw-r--r--tools/subdir.am1
-rw-r--r--vrrpd/vrrp.c4
-rw-r--r--vrrpd/vrrp_packet.c2
-rw-r--r--vrrpd/vrrp_vty.c2
-rwxr-xr-xvtysh/extract.pl.in4
-rw-r--r--watchfrr/watchfrr.c181
-rw-r--r--yang/frr-isisd.yang17
-rw-r--r--yang/frr-zebra.yang41
-rw-r--r--yang/subdir.am9
-rw-r--r--zebra/rib.h20
-rw-r--r--zebra/rule_netlink.c31
-rw-r--r--zebra/zapi_msg.c100
-rw-r--r--zebra/zebra_dplane.c86
-rw-r--r--zebra/zebra_dplane.h2
-rw-r--r--zebra/zebra_mpls.c69
-rw-r--r--zebra/zebra_nb.c34
-rw-r--r--zebra/zebra_nb.h9
-rw-r--r--zebra/zebra_nb_config.c239
-rw-r--r--zebra/zebra_nb_state.c35
-rw-r--r--zebra/zebra_netns_notify.c2
-rw-r--r--zebra/zebra_nhg.c10
-rw-r--r--zebra/zebra_pbr.c17
-rw-r--r--zebra/zebra_pbr.h2
-rw-r--r--zebra/zebra_pw.c104
-rw-r--r--zebra/zebra_rib.c35
-rw-r--r--zebra/zebra_rnh.c19
-rw-r--r--zebra/zebra_vty.c269
-rw-r--r--zebra/zebra_vxlan.c12
-rw-r--r--zebra/zebra_vxlan_private.h1
136 files changed, 8242 insertions, 819 deletions
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
index ba57db61cb..67b8018c8e 100644
--- a/bgpd/bgp_bfd.c
+++ b/bgpd/bgp_bfd.c
@@ -486,7 +486,7 @@ static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx,
int command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
/* This command overrides profile if it was previously applied. */
bi = peer->bfd_info;
@@ -497,8 +497,8 @@ static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx,
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
- min_rx, min_tx, detect_mult, defaults,
- &command);
+ min_rx, min_tx, detect_mult, NULL,
+ defaults, &command);
/*
* This command overrides profile if it was previously
@@ -564,7 +564,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer,
if (!peer->bfd_info)
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
bfd_info->type = type;
@@ -577,7 +577,7 @@ static int bgp_bfd_peer_param_type_set(struct peer *peer,
bfd_set_param(
(struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
bfd_info->type = type;
@@ -612,7 +612,7 @@ static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile)
struct bfd_info *bfd_info;
bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX,
- BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
@@ -628,7 +628,7 @@ static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile)
command = 0;
bfd_set_param((struct bfd_info **)&(peer->bfd_info),
BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
- BFD_DEF_DETECT_MULT, 1, &command);
+ BFD_DEF_DETECT_MULT, NULL, 1, &command);
bfd_info = (struct bfd_info *)peer->bfd_info;
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index db330f998c..af88547ca9 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -951,8 +951,11 @@ afibreak:
/* initialize syncrdpos to the first
* mid-layer table entry
*/
- if (!bmp->syncrdpos)
+ if (!bmp->syncrdpos) {
bmp->syncrdpos = bgp_table_top(table);
+ if (!bmp->syncrdpos)
+ goto eor;
+ }
/* look for a valid mid-layer table */
do {
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index 97d625493f..565d0b8e19 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -50,6 +50,12 @@ static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
unsigned int i;
int index;
+ /*
+ * reuse_limit can't be zero, this is for Coverity
+ * to bypass division by zero test.
+ */
+ assert(bdc->reuse_limit);
+
i = (int)(((double)penalty / bdc->reuse_limit - 1.0)
* bdc->scale_factor);
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 2ca9e5ee13..9f32d450b9 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -2144,7 +2144,7 @@ DEFUN_NOSH (show_debugging_bgp,
bgp_debug_zebra_prefixes);
if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
- vty_out(vty, " BGP graceful-restart debugging is on");
+ vty_out(vty, " BGP graceful-restart debugging is on\n");
if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
vty_out(vty, " BGP allow martian next hop debugging is on\n");
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 95e24eae12..2584939378 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -679,7 +679,8 @@ static void show_esi_routes(struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path);
+ route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path,
+ false);
if (json)
json_object_array_add(json_paths, json_path);
@@ -788,7 +789,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
json_path);
else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
- json_path);
+ json_path, false);
if (json)
json_object_array_add(json_paths, json_path);
@@ -1314,7 +1315,7 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
route_vty_out(vty,
bgp_dest_get_prefix(rm),
pi, no_display, SAFI_EVPN,
- json_array);
+ json_array, false);
no_display = 1;
}
@@ -2813,7 +2814,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
SAFI_EVPN, json_path);
} else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
- json_path);
+ json_path, false);
if (json)
json_object_array_add(json_paths,
@@ -3898,6 +3899,12 @@ DEFPY (bgp_evpn_advertise_pip_ip_mac,
struct listnode *node = NULL;
struct bgpevpn *vpn = NULL;
+ /*
+ * At this point if bgp_evpn is NULL and evpn is enabled
+ * something stupid has gone wrong
+ */
+ assert(bgp_evpn);
+
update_advertise_vrf_routes(bgp_vrf);
/* Update (svi) type-2 routes */
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
index 64a6c2ea8f..e309fa948e 100644
--- a/bgpd/bgp_flowspec_vty.c
+++ b/bgpd/bgp_flowspec_vty.c
@@ -376,11 +376,10 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
bpr->priority,
bpr->action->table_id);
}
- if (list_began)
- vty_out(vty, ")");
- vty_out(vty, "\n");
}
- if (!list_began)
+ if (list_began)
+ vty_out(vty, ")\n");
+ else
vty_out(vty, "\tnot installed in PBR\n");
}
}
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 33eaf9ae74..b082aa9c6a 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -133,19 +133,20 @@ void sighup(void)
/*
* This is turned off for the moment. There is all
* sorts of config turned off by bgp_terminate
- * that is not setup properly again in bgp_rest.
+ * that is not setup properly again in bgp_reset.
* I see no easy way to do this nor do I see that
* this is a desirable way to reload config
* given the yang work.
*/
/* Terminate all thread. */
- bgp_terminate();
- bgp_reset();
- zlog_info("bgpd restarting!");
-
- /* Reload config file. */
- vty_read_config(NULL, bgpd_di.config_file, config_default);
+ /*
+ * bgp_terminate();
+ * bgp_reset();
+ * zlog_info("bgpd restarting!");
+ * Reload config file.
+ * vty_read_config(NULL, bgpd_di.config_file, config_default);
+ */
/* Try to return to normal operation. */
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 545e140028..2f12c8e93e 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -7411,7 +7411,7 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,
/* Static function to display route. */
static void route_vty_out_route(const struct prefix *p, struct vty *vty,
- json_object *json)
+ json_object *json, bool wide)
{
int len = 0;
char buf[BUFSIZ];
@@ -7466,7 +7466,7 @@ static void route_vty_out_route(const struct prefix *p, struct vty *vty,
}
if (!json) {
- len = 17 - len;
+ len = wide ? (45 - len) : (17 - len);
if (len < 1)
vty_out(vty, "\n%*s", 20, " ");
else
@@ -7569,7 +7569,7 @@ static char *bgp_nexthop_hostname(struct peer *peer,
/* called from terminal list command */
void route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display, safi_t safi,
- json_object *json_paths)
+ json_object *json_paths, bool wide)
{
int len;
struct attr *attr = path->attr;
@@ -7596,11 +7596,11 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (!json_paths) {
/* print prefix and mask */
if (!display)
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, wide);
else
- vty_out(vty, "%*s", 17, " ");
+ vty_out(vty, "%*s", (wide ? 45 : 17), " ");
} else {
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, wide);
}
/*
@@ -7690,7 +7690,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
else
len = vty_out(vty, "%s%s", nexthop, vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7720,7 +7720,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
len = vty_out(vty, "%pI4%s", &attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7756,7 +7756,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7787,7 +7787,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
len = vty_out(vty, "%pI4%s", &attr->nexthop,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -7860,10 +7860,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (path->peer->conf_if) {
len = vty_out(vty, "%s",
path->peer->conf_if);
- len = 16 - len; /* len of IPv6
- addr + max
- len of def
- ifname */
+ /* len of IPv6 addr + max len of def
+ * ifname */
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7882,7 +7881,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->mp_nexthop_local,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7900,7 +7899,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
&attr->mp_nexthop_global,
vrf_id_str);
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
@@ -7914,10 +7913,16 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
if (json_paths)
json_object_int_add(json_path, "metric", attr->med);
+ else if (wide)
+ vty_out(vty, "%7u", attr->med);
else
vty_out(vty, "%10u", attr->med);
- else if (!json_paths)
- vty_out(vty, " ");
+ else if (!json_paths) {
+ if (wide)
+ vty_out(vty, "%*s", 7, " ");
+ else
+ vty_out(vty, "%*s", 10, " ");
+ }
/* Local Pref */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
@@ -8021,7 +8026,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
/* called from terminal list command */
void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
struct attr *attr, safi_t safi, bool use_json,
- json_object *json_ar)
+ json_object *json_ar, bool wide)
{
json_object *json_status = NULL;
json_object *json_net = NULL;
@@ -8053,7 +8058,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
json_object_string_add(json_net, "network", buff);
}
} else
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, wide);
/* Print attribute */
if (attr) {
@@ -8114,6 +8119,9 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
vty_out(vty, "%-16s",
inet_ntoa(
attr->mp_nexthop_global_in));
+ else if (wide)
+ vty_out(vty, "%-41s",
+ inet_ntoa(attr->nexthop));
else
vty_out(vty, "%-16s",
inet_ntoa(attr->nexthop));
@@ -8126,7 +8134,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
inet_ntop(AF_INET6,
&attr->mp_nexthop_global, buf,
BUFSIZ));
- len = 16 - len;
+ len = wide ? (41 - len) : (16 - len);
if (len < 1)
vty_out(vty, "\n%*s", 36, " ");
else
@@ -8134,7 +8142,12 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
}
if (attr->flag
& ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
- vty_out(vty, "%10u", attr->med);
+ if (wide)
+ vty_out(vty, "%7u", attr->med);
+ else
+ vty_out(vty, "%10u", attr->med);
+ else if (wide)
+ vty_out(vty, " ");
else
vty_out(vty, " ");
@@ -8185,7 +8198,7 @@ void route_vty_out_tag(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (json == NULL) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -8282,7 +8295,7 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!display)
- route_vty_out_route(p, vty, json_path);
+ route_vty_out_route(p, vty, json_path, false);
else
vty_out(vty, "%*s", 17, " ");
@@ -8397,7 +8410,7 @@ static void damp_route_vty_out(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!use_json) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -8468,7 +8481,7 @@ static void flap_route_vty_out(struct vty *vty, const struct prefix *p,
/* print prefix and mask */
if (!use_json) {
if (!display)
- route_vty_out_route(p, vty, NULL);
+ route_vty_out_route(p, vty, NULL, false);
else
vty_out(vty, "%*s", 17, " ");
}
@@ -9583,7 +9596,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
void *output_arg, bool use_json, char *rd,
int is_last, unsigned long *output_cum,
unsigned long *total_cum,
- unsigned long *json_header_depth)
+ unsigned long *json_header_depth, bool wide)
{
struct bgp_path_info *pi;
struct bgp_dest *dest;
@@ -9807,7 +9820,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
|| type == bgp_show_type_flap_neighbor)
vty_out(vty, BGP_SHOW_FLAP_HEADER);
else
- vty_out(vty, BGP_SHOW_HEADER);
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
header = 0;
}
if (rd != NULL && !display && !output_count) {
@@ -9828,7 +9842,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
json_paths);
else
route_vty_out(vty, dest_p, pi, display, safi,
- json_paths);
+ json_paths, wide);
display++;
}
@@ -9936,7 +9950,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
prefix_rd2str(&prd, rd, sizeof(rd));
bgp_show_table(vty, bgp, safi, itable, type, output_arg,
use_json, rd, next == NULL, &output_cum,
- &total_cum, &json_header_depth);
+ &total_cum, &json_header_depth, false);
if (next == NULL)
show_msg = false;
}
@@ -9953,7 +9967,8 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
return CMD_SUCCESS;
}
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
- enum bgp_show_type type, void *output_arg, bool use_json)
+ enum bgp_show_type type, void *output_arg, bool use_json,
+ bool wide)
{
struct bgp_table *table;
unsigned long json_header_depth = 0;
@@ -9987,11 +10002,12 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, use_json,
- NULL, 1, NULL, NULL, &json_header_depth);
+ NULL, 1, NULL, NULL, &json_header_depth, wide);
}
static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
- safi_t safi, bool use_json)
+ safi_t safi, bool use_json,
+ bool wide)
{
struct listnode *node, *nnode;
struct bgp *bgp;
@@ -10020,7 +10036,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
: bgp->name);
}
bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL,
- use_json);
+ use_json, wide);
}
if (use_json)
@@ -10507,8 +10523,8 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_lcommunity_exact
- : bgp_show_type_lcommunity),
- lcom, uj);
+ : bgp_show_type_lcommunity),
+ lcom, uj, false);
}
static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
@@ -10527,8 +10543,8 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_lcommunity_list_exact
- : bgp_show_type_lcommunity_list),
- list, uj);
+ : bgp_show_type_lcommunity_list),
+ list, uj, false);
}
DEFUN (show_ip_bgp_large_community_list,
@@ -10607,9 +10623,11 @@ DEFUN (show_ip_bgp_large_community,
exact_match, afi, safi, uj);
} else
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_lcommunity_all, NULL, uj);
+ bgp_show_type_lcommunity_all, NULL, uj, false);
}
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array);
static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
safi_t safi, struct json_object *json);
@@ -10629,7 +10647,7 @@ DEFUN(show_ip_bgp_statistics_all, show_ip_bgp_statistics_all_cmd,
bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
&bgp, false);
- if (!bgp)
+ if (!idx)
return CMD_WARNING;
if (uj)
@@ -10829,7 +10847,7 @@ DEFUN(show_ip_bgp, show_ip_bgp_cmd,
}
/* BGP route print out function with JSON */
-DEFUN (show_ip_bgp_json,
+DEFPY (show_ip_bgp_json,
show_ip_bgp_json_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\
[cidr-only\
@@ -10839,7 +10857,7 @@ DEFUN (show_ip_bgp_json,
|accept-own|accept-own-nexthop|route-filter-v6\
|route-filter-v4|route-filter-translated-v6\
|route-filter-translated-v4] [exact-match]\
- ] [json]",
+ ] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
@@ -10867,7 +10885,8 @@ DEFUN (show_ip_bgp_json,
"RT translated VPNv6 route filtering (well-known community)\n"
"RT translated VPNv4 route filtering (well-known community)\n"
"Exact match of the communities\n"
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
@@ -10875,7 +10894,6 @@ DEFUN (show_ip_bgp_json,
struct bgp *bgp = NULL;
int idx = 0;
int exact_match = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -10887,16 +10905,17 @@ DEFUN (show_ip_bgp_json,
if (argv_find(argv, argc, "cidr-only", &idx))
return bgp_show(vty, bgp, afi, safi, bgp_show_type_cidr_only,
- NULL, uj);
+ NULL, uj, wide);
if (argv_find(argv, argc, "dampening", &idx)) {
if (argv_find(argv, argc, "dampened-paths", &idx))
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_dampend_paths, NULL, uj);
+ bgp_show_type_dampend_paths, NULL, uj,
+ wide);
else if (argv_find(argv, argc, "flap-statistics", &idx))
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_flap_statistics, NULL,
- uj);
+ bgp_show_type_flap_statistics, NULL, uj,
+ wide);
}
if (argv_find(argv, argc, "community", &idx)) {
@@ -10922,11 +10941,11 @@ DEFUN (show_ip_bgp_json,
exact_match, afi, safi, uj);
else
return (bgp_show(vty, bgp, afi, safi,
- bgp_show_type_community_all, NULL,
- uj));
+ bgp_show_type_community_all, NULL, uj,
+ wide));
}
- return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj);
+ return bgp_show(vty, bgp, afi, safi, sh_type, NULL, uj, wide);
}
DEFUN (show_ip_bgp_route,
@@ -11037,22 +11056,22 @@ DEFUN (show_ip_bgp_regexp,
bgp_show_type_regexp, uj);
}
-DEFUN (show_ip_bgp_instance_all,
+DEFPY (show_ip_bgp_instance_all,
show_ip_bgp_instance_all_cmd,
- "show [ip] bgp <view|vrf> all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json]",
+ "show [ip] bgp <view|vrf> all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
BGP_INSTANCE_ALL_HELP_STR
BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP;
safi_t safi = SAFI_UNICAST;
struct bgp *bgp = NULL;
int idx = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -11062,7 +11081,7 @@ DEFUN (show_ip_bgp_instance_all,
if (!idx)
return CMD_WARNING;
- bgp_show_all_instances_routes_vty(vty, afi, safi, uj);
+ bgp_show_all_instances_routes_vty(vty, afi, safi, uj, wide);
return CMD_SUCCESS;
}
@@ -11085,7 +11104,7 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
return CMD_WARNING;
}
- rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json);
+ rc = bgp_show(vty, bgp, afi, safi, type, regex, use_json, false);
bgp_regex_free(regex);
return rc;
}
@@ -11103,7 +11122,7 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, plist, 0);
+ return bgp_show(vty, bgp, afi, safi, type, plist, 0, false);
}
static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
@@ -11119,7 +11138,7 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, as_list, 0);
+ return bgp_show(vty, bgp, afi, safi, type, as_list, 0, false);
}
static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
@@ -11134,7 +11153,7 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, rmap, 0);
+ return bgp_show(vty, bgp, afi, safi, type, rmap, 0, false);
}
static int bgp_show_community(struct vty *vty, struct bgp *bgp,
@@ -11153,7 +11172,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp,
ret = bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_exact
: bgp_show_type_community),
- com, use_json);
+ com, use_json, false);
community_free(&com);
return ret;
@@ -11174,7 +11193,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_list_exact
: bgp_show_type_community_list),
- list, 0);
+ list, 0, false);
}
static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
@@ -11192,7 +11211,7 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- ret = bgp_show(vty, bgp, afi, safi, type, p, 0);
+ ret = bgp_show(vty, bgp, afi, safi, type, p, 0, false);
prefix_free(&p);
return ret;
}
@@ -11387,8 +11406,18 @@ static int bgp_table_stats_walker(struct thread *t)
return 0;
}
-static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi, struct json_object *json_array)
+static void bgp_table_stats_all(struct vty *vty, afi_t afi, safi_t safi,
+ struct json_object *json_array)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
{
struct bgp_table_stats ts;
unsigned int i;
@@ -11416,8 +11445,10 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
}
if (!json)
- vty_out(vty, "BGP %s RIB statistics\n",
- get_afi_safi_str(afi, safi, false));
+ vty_out(vty, "BGP %s RIB statistics (%s)\n",
+ get_afi_safi_str(afi, safi, false), bgp->name_pretty);
+ else
+ json_object_string_add(json, "instance", bgp->name_pretty);
/* labeled-unicast routes live in the unicast table */
if (safi == SAFI_LABELED_UNICAST)
@@ -11606,6 +11637,17 @@ end_table_stats:
return ret;
}
+static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
+{
+ if (!bgp) {
+ bgp_table_stats_all(vty, afi, safi, json_array);
+ return CMD_SUCCESS;
+ }
+
+ return bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
enum bgp_pcounts {
PCOUNT_ADJ_IN = 0,
PCOUNT_DAMPED,
@@ -11934,7 +11976,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_adj_route_type type,
const char *rmap_name, bool use_json,
- json_object *json)
+ json_object *json, bool wide)
{
struct bgp_table *table;
struct bgp_adj_in *ain;
@@ -12090,7 +12132,9 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
}
if (header2) {
if (!use_json)
- vty_out(vty, BGP_SHOW_HEADER);
+ vty_out(vty,
+ (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
header2 = 0;
}
@@ -12123,7 +12167,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
filtered_count++;
route_vty_out_tmp(vty, rn_p, &attr, safi,
- use_json, json_ar);
+ use_json, json_ar, wide);
bgp_attr_undup(&attr, ain->attr);
output_count++;
}
@@ -12194,7 +12238,8 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
if (header2) {
if (!use_json)
vty_out(vty,
- BGP_SHOW_HEADER);
+ (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
header2 = 0;
}
@@ -12209,7 +12254,8 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
if (ret != RMAP_DENY) {
route_vty_out_tmp(
vty, rn_p, &attr, safi,
- use_json, json_ar);
+ use_json, json_ar,
+ wide);
output_count++;
} else {
filtered_count++;
@@ -12248,7 +12294,7 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi,
static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
safi_t safi, enum bgp_show_adj_route_type type,
- const char *rmap_name, bool use_json)
+ const char *rmap_name, bool use_json, bool wide)
{
json_object *json = NULL;
@@ -12285,14 +12331,15 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
return CMD_WARNING;
}
- show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json);
+ show_adj_route(vty, peer, afi, safi, type, rmap_name, use_json, json,
+ wide);
return CMD_SUCCESS;
}
-DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
+DEFPY (show_ip_bgp_instance_neighbor_advertised_route,
show_ip_bgp_instance_neighbor_advertised_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
@@ -12308,7 +12355,8 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
"Display the filtered routes received from neighbor\n"
"Route-map to modify the attributes\n"
"Name of the route map\n"
- JSON_STR)
+ JSON_STR
+ "Increase table width for longer prefixes\n")
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
@@ -12318,7 +12366,6 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
struct peer *peer;
enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised;
int idx = 0;
- bool uj = use_json(argc, argv);
if (uj)
argc--;
@@ -12346,7 +12393,7 @@ DEFUN (show_ip_bgp_instance_neighbor_advertised_route,
if (argv_find(argv, argc, "route-map", &idx))
rmap_name = argv[++idx]->arg;
- return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj);
+ return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide);
}
DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
@@ -12454,7 +12501,8 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
return CMD_WARNING;
}
- return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json);
+ return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, use_json,
+ false);
}
DEFUN (show_ip_bgp_flowspec_routes_detailed,
@@ -12483,7 +12531,8 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed,
if (!idx)
return CMD_WARNING;
- return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj);
+ return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, uj,
+ false);
}
DEFUN (show_ip_bgp_neighbor_routes,
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index ee52dcc8c6..0767d2912f 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -72,6 +72,7 @@ enum bgp_show_adj_route_type {
#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n"
#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n"
#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
+#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n"
/* Maximum number of labels we can process or send with a prefix. We
* really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN.
@@ -618,13 +619,13 @@ extern struct bgp_path_info *info_make(int type, int sub_type,
extern void route_vty_out(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display, safi_t safi,
- json_object *json_paths);
+ json_object *json_paths, bool wide);
extern void route_vty_out_tag(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
safi_t safi, json_object *json);
extern void route_vty_out_tmp(struct vty *vty, const struct prefix *p,
struct attr *attr, safi_t safi, bool use_json,
- json_object *json_ar);
+ json_object *json_ar, bool wide);
extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
struct bgp_path_info *path, int display,
json_object *json);
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index e8e9b33d4b..ecdcaa38b8 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -262,14 +262,15 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp,
route_vty_out_tmp(vty, dest_p,
adj->adv->baa->attr,
SUBGRP_SAFI(subgrp),
- 0, NULL);
+ 0, NULL, false);
output_count++;
}
if ((flags & UPDWALK_FLAGS_ADVERTISED)
&& adj->attr) {
- route_vty_out_tmp(
- vty, dest_p, adj->attr,
- SUBGRP_SAFI(subgrp), 0, NULL);
+ route_vty_out_tmp(vty, dest_p,
+ adj->attr,
+ SUBGRP_SAFI(subgrp),
+ 0, NULL, false);
output_count++;
}
}
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
index c8b8e2391d..0b5d156e6d 100644
--- a/bgpd/bgp_vpn.c
+++ b/bgpd/bgp_vpn.c
@@ -226,7 +226,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
rd_header = 0;
}
route_vty_out_tmp(vty, bgp_dest_get_prefix(rm), attr,
- safi, use_json, json_routes);
+ safi, use_json, json_routes, false);
output_count++;
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index e11ac459f2..f0ee800287 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2174,14 +2174,6 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi)
return ret;
}
-int peer_afc_set(struct peer *peer, afi_t afi, safi_t safi, int enable)
-{
- if (enable)
- return peer_activate(peer, afi, safi);
- else
- return peer_deactivate(peer, afi, safi);
-}
-
void peer_nsf_stop(struct peer *peer)
{
afi_t afi;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 4efc068dea..8eea2a5f60 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1838,7 +1838,6 @@ extern void peer_group_notify_unconfig(struct peer_group *group);
extern int peer_activate(struct peer *, afi_t, safi_t);
extern int peer_deactivate(struct peer *, afi_t, safi_t);
-extern int peer_afc_set(struct peer *, afi_t, safi_t, int);
extern int peer_group_bind(struct bgp *, union sockunion *, struct peer *,
struct peer_group *, as_t *);
diff --git a/doc/manpages/frr-watchfrr.rst b/doc/manpages/frr-watchfrr.rst
index dceb423f82..d8c82eafa9 100644
--- a/doc/manpages/frr-watchfrr.rst
+++ b/doc/manpages/frr-watchfrr.rst
@@ -35,6 +35,22 @@ OPTIONS
Set the VTY socket directory (the default value is "/var/run/frr").
+.. option:: -N <name>, --pathspace <name>
+
+ Insert the given name into paths used by the FRR daemons. This is appended
+ to the VTY socket directory and passed to the daemons which also add it to
+ their paths in /etc.
+
+.. option:: --netns[=<name>]
+
+ (Linux only.) Switch network namespaces when starting watchfrr. The name
+ defaults to the value passed with -N (which it should be used in conjunction
+ with.) If the name is not specified, the option has no effect.
+
+ If the network namespace does not exist, it is created in a manner
+ compatible with iproute2. Network namespaces are not removed by FRR, this
+ must be done with "ip netns delete".
+
.. option:: -l <level>, --loglevel <level>
Set the logging level (the default value is "6"). The value should range from 0 (LOG_EMERG) to 7 (LOG_DEBUG), but higher number can be supplied if extra debugging messages are required.
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index 618d90a85e..47792317ad 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -254,7 +254,7 @@ IS-IS BFD Configuration
The following commands are available inside the interface configuration node.
.. index:: isis bfd
-.. clicmd:: ip isis bfd
+.. clicmd:: isis bfd
Listen for BFD events on peers created on the interface. Every time
a new neighbor is found a BFD peer is created to monitor the link
@@ -269,6 +269,15 @@ The following commands are available inside the interface configuration node.
IPv4 and IPv6 support are configured then just a IPv6 based session is
created.
+.. index:: isis bfd profile BFDPROF
+.. clicmd:: isis bfd profile BFDPROF
+
+ Use a BFD profile BFDPROF as provided in the BFD configuration.
+
+.. index:: no isis bfd profile BFDPROF
+.. clicmd:: no isis bfd profile BFDPROF
+
+ Removes any BFD profile if present.
.. _bfd-ospf-peer-config:
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index bb968735b9..d776b32a88 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1191,6 +1191,13 @@ Defining Peers
``net.core.optmem_max`` to allow the kernel to allocate the necessary option
memory.
+.. index:: [no] coalesce-time (0-4294967295)
+.. clicmd:: [no] coalesce-time (0-4294967295)
+
+ The time in milliseconds that BGP will delay before deciding what peers
+ can be put into an update-group together in order to generate a single
+ update for them. The default time is 1000.
+
.. _bgp-configuring-peers:
Configuring Peers
@@ -2425,72 +2432,6 @@ This makes possible to separate not only layer 3 networks like VRF-lite networks
Also, VRF netns based make possible to separate layer 2 networks on separate VRF
instances.
-.. _bgp-cisco-compatibility:
-
-Cisco Compatibility
--------------------
-
-FRR has commands that change some configuration syntax and default behavior to
-behave more closely to Cisco conventions. These are deprecated and will be
-removed in a future version of FRR.
-
-.. deprecated:: 5.0
- Please transition to using the FRR specific syntax for your configuration.
-
-.. index:: bgp config-type cisco
-.. clicmd:: bgp config-type cisco
-
- Cisco compatible BGP configuration output.
-
- When this configuration line is specified:
-
- - ``no synchronization`` is displayed. This command does nothing and is for
- display purposes only.
- - ``no auto-summary`` is displayed.
- - The ``network`` and ``aggregate-address`` arguments are displayed as:
-
- ::
-
- A.B.C.D M.M.M.M
-
- FRR: network 10.0.0.0/8
- Cisco: network 10.0.0.0
-
- FRR: aggregate-address 192.168.0.0/24
- Cisco: aggregate-address 192.168.0.0 255.255.255.0
-
- Community attribute handling is also different. If no configuration is
- specified community attribute and extended community attribute are sent to
- the neighbor. If a user manually disables the feature, the community
- attribute is not sent to the neighbor. When ``bgp config-type cisco`` is
- specified, the community attribute is not sent to the neighbor by default.
- To send the community attribute user has to specify
- :clicmd:`neighbor A.B.C.D send-community` like so:
-
- .. code-block:: frr
-
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 1
- address-family ipv4 unicast
- no neighbor 10.0.0.1 send-community
- exit-address-family
- !
- router bgp 1
- neighbor 10.0.0.1 remote-as 1
- address-family ipv4 unicast
- neighbor 10.0.0.1 send-community
- exit-address-family
- !
-
-.. deprecated:: 5.0
- Please transition to using the FRR specific syntax for your configuration.
-
-.. index:: bgp config-type zebra
-.. clicmd:: bgp config-type zebra
-
- FRR style BGP configuration. This is the default.
-
.. _bgp-debugging:
Debugging
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index cd095af862..6295ba9293 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -20,8 +20,11 @@ OSPF6 router
Set router's Router-ID.
-.. index:: interface IFNAME area AREA
-.. clicmd:: interface IFNAME area AREA
+.. index:: interface IFNAME area (0-4294967295)
+.. clicmd:: interface IFNAME area (0-4294967295)
+
+.. index:: interface IFNAME area A.B.C.D
+.. clicmd:: interface IFNAME area A.B.C.D
Bind interface to specified area, and start sending OSPF packets. `area` can
be specified as 0.
diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst
index 149949e863..99ef258cb2 100644
--- a/doc/user/pbr.rst
+++ b/doc/user/pbr.rst
@@ -123,6 +123,22 @@ end destination.
on another platform it will be denied. This mark translates to the
underlying `ip rule .... fwmark XXXX` command.
+.. clicmd:: match dscp (DSCP|0-63)
+
+ Match packets according to the specified differentiated services code point
+ (DSCP) in the IP header; if this value matches then forward the packet
+ according to the nexthop(s) specified. The passed DSCP value may also be a
+ standard name for a differentiated service code point like cs0 or af11.
+
+ You may only specify one dscp per route map sequence; to match on multiple
+ dscp values you will need to create several sequences, one for each value.
+
+.. clicmd:: match ecn (0-3)
+
+ Match packets according to the specified explicit congestion notification
+ (ECN) field in the IP header; if this value matches then forward the packet
+ according to the nexthop(s) specified.
+
.. clicmd:: set nexthop-group NAME
Use the nexthop-group NAME as the place to forward packets when the match
diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c
index 69c971ee2c..5729994baa 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -255,6 +255,43 @@ static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
command_str, dst_str, interface, src_str);
}
+static void bfd_command(int command, struct bfd_info *bfd_info, int family,
+ const void *dst_ip, const void *src_ip,
+ const char *if_name)
+{
+ struct bfd_session_arg args = {};
+ size_t addrlen;
+
+ args.cbit = 1;
+ args.family = family;
+ args.vrf_id = VRF_DEFAULT;
+ args.command = command;
+ args.bfd_info = bfd_info;
+ if (args.bfd_info) {
+ args.min_rx = bfd_info->required_min_rx;
+ args.min_tx = bfd_info->desired_min_tx;
+ args.detection_multiplier = bfd_info->detect_mult;
+ if (bfd_info->profile[0]) {
+ args.profilelen = strlen(bfd_info->profile);
+ strlcpy(args.profile, bfd_info->profile,
+ sizeof(args.profile));
+ }
+ }
+
+ addrlen = family == AF_INET ? sizeof(struct in_addr)
+ : sizeof(struct in6_addr);
+ memcpy(&args.dst, dst_ip, addrlen);
+ if (src_ip)
+ memcpy(&args.src, src_ip, addrlen);
+
+ if (if_name) {
+ strlcpy(args.ifname, if_name, sizeof(args.ifname));
+ args.ifnamelen = strlen(args.ifname);
+ }
+
+ zclient_bfd_command(zclient, &args);
+}
+
static void bfd_handle_adj_down(struct isis_adjacency *adj)
{
if (!adj->bfd_session)
@@ -264,17 +301,11 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj)
&adj->bfd_session->src_ip, adj->circuit->interface->name,
ZEBRA_BFD_DEST_DEREGISTER);
- bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family,
- &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
- (adj->circuit->interface)
- ? adj->circuit->interface->name
- : NULL,
- 0, /* ttl */
- 0, /* multihop */
- 1, /* control plane independent bit is on */
- ZEBRA_BFD_DEST_DEREGISTER,
- 0, /* set_flag */
- VRF_DEFAULT);
+ bfd_command(ZEBRA_BFD_DEST_DEREGISTER, NULL, adj->bfd_session->family,
+ &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
+ (adj->circuit->interface) ? adj->circuit->interface->name
+ : NULL);
+
bfd_session_free(&adj->bfd_session);
}
@@ -324,18 +355,12 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
&adj->bfd_session->src_ip, circuit->interface->name, command);
- bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family,
- &adj->bfd_session->dst_ip,
- &adj->bfd_session->src_ip,
- (adj->circuit->interface)
- ? adj->circuit->interface->name
- : NULL,
- 0, /* ttl */
- 0, /* multihop */
- 1, /* control plane independent bit is on */
- command,
- 0, /* set flag */
- VRF_DEFAULT);
+
+ bfd_command(command, circuit->bfd_info, family,
+ &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
+ (adj->circuit->interface) ? adj->circuit->interface->name
+ : NULL);
+
return;
out:
bfd_handle_adj_down(adj);
@@ -383,14 +408,14 @@ void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command)
}
}
-void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
- uint32_t min_rx, uint32_t min_tx,
- uint32_t detect_mult, int defaults)
+void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx,
+ uint32_t min_tx, uint32_t detect_mult,
+ const char *profile, int defaults)
{
int command = 0;
- bfd_set_param(&circuit->bfd_info, min_rx,
- min_tx, detect_mult, defaults, &command);
+ bfd_set_param(&circuit->bfd_info, min_rx, min_tx, detect_mult, profile,
+ defaults, &command);
if (command)
isis_bfd_circuit_cmd(circuit, command);
diff --git a/isisd/isis_bfd.h b/isisd/isis_bfd.h
index 3193f16061..6ce630688c 100644
--- a/isisd/isis_bfd.h
+++ b/isisd/isis_bfd.h
@@ -22,9 +22,9 @@
struct isis_circuit;
void isis_bfd_circuit_cmd(struct isis_circuit *circuit, int command);
-void isis_bfd_circuit_param_set(struct isis_circuit *circuit,
- uint32_t min_rx, uint32_t min_tx,
- uint32_t detect_mult, int defaults);
+void isis_bfd_circuit_param_set(struct isis_circuit *circuit, uint32_t min_rx,
+ uint32_t min_tx, uint32_t detect_mult,
+ const char *profile, int defaults);
void isis_bfd_init(void);
#endif
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 28b98610b2..d864053b15 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -330,8 +330,7 @@ void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode,
DEFPY(isis_bfd,
isis_bfd_cmd,
"[no] isis bfd",
- NO_STR
- PROTO_HELP
+ NO_STR PROTO_HELP
"Enable BFD support\n")
{
const struct lyd_node *dnode;
@@ -343,19 +342,53 @@ DEFPY(isis_bfd,
return CMD_SUCCESS;
}
- nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring",
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/enabled",
NB_OP_MODIFY, no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile
+ */
+DEFPY(isis_bfd_profile,
+ isis_bfd_profile_cmd,
+ "[no] isis bfd profile WORD",
+ NO_STR PROTO_HELP
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n"
+ "Profile name\n")
+{
+ const struct lyd_node *dnode;
+
+ dnode = yang_dnode_get(vty->candidate_config->dnode,
+ "%s/frr-isisd:isis", VTY_CURR_XPATH);
+ if (dnode == NULL) {
+ vty_out(vty, "ISIS is not enabled on this circuit\n");
+ return CMD_SUCCESS;
+ }
+
+ nb_cli_enqueue_change(vty, "./frr-isisd:isis/bfd-monitoring/profile",
+ NB_OP_MODIFY, no ? NULL : profile);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
- if (!yang_dnode_get_bool(dnode, NULL))
+ const char *profile;
+
+ if (!yang_dnode_get_bool(dnode, "./enabled"))
vty_out(vty, " no");
vty_out(vty, " isis bfd\n");
+
+ if (yang_dnode_exists(dnode, "./profile")) {
+ profile = yang_dnode_get_string(dnode, "./profile");
+ if (profile[0] != '\0')
+ vty_out(vty, " isis bfd profile %s\n", profile);
+ }
}
/*
@@ -2281,6 +2314,7 @@ void isis_cli_init(void)
install_element(INTERFACE_NODE, &ip6_router_isis_cmd);
install_element(INTERFACE_NODE, &no_ip_router_isis_cmd);
install_element(INTERFACE_NODE, &isis_bfd_cmd);
+ install_element(INTERFACE_NODE, &isis_bfd_profile_cmd);
install_element(ISIS_NODE, &net_cmd);
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 1d842eb13b..2b8b02e3f1 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -574,11 +574,24 @@ const struct frr_yang_module_info frr_isisd_info = {
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring",
.cbs = {
- .modify = lib_interface_isis_bfd_monitoring_modify,
+ .apply_finish = lib_interface_isis_bfd_monitoring_apply_finish,
.cli_show = cli_show_ip_isis_bfd_monitoring,
}
},
{
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled",
+ .cbs = {
+ .modify = lib_interface_isis_bfd_monitoring_enabled_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile",
+ .cbs = {
+ .modify = lib_interface_isis_bfd_monitoring_profile_modify,
+ .destroy = lib_interface_isis_bfd_monitoring_profile_destroy,
+ }
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/csnp-interval",
.cbs = {
.cli_show = cli_show_ip_isis_csnp_interval,
diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h
index e887b1a388..a9401bc86a 100644
--- a/isisd/isis_nb.h
+++ b/isisd/isis_nb.h
@@ -171,7 +171,14 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_ipv4_routing_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args);
int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args);
-int lib_interface_isis_bfd_monitoring_modify(struct nb_cb_modify_args *args);
+void lib_interface_isis_bfd_monitoring_apply_finish(
+ struct nb_cb_apply_finish_args *args);
+int lib_interface_isis_bfd_monitoring_enabled_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_bfd_monitoring_profile_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_bfd_monitoring_profile_destroy(
+ struct nb_cb_destroy_args *args);
int isis_instance_segment_routing_enabled_modify(
struct nb_cb_modify_args *args);
int isis_instance_segment_routing_enabled_modify(
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index 5fad974d25..6873c652f2 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -2029,26 +2029,53 @@ int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args)
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring
*/
-int lib_interface_isis_bfd_monitoring_modify(struct nb_cb_modify_args *args)
+void lib_interface_isis_bfd_monitoring_apply_finish(
+ struct nb_cb_apply_finish_args *args)
{
struct isis_circuit *circuit;
- bool bfd_monitoring;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
+ bool enabled;
+ const char *profile = NULL;
circuit = nb_running_get_entry(args->dnode, NULL, true);
- bfd_monitoring = yang_dnode_get_bool(args->dnode, NULL);
+ enabled = yang_dnode_get_bool(args->dnode, "./enabled");
+
+ if (yang_dnode_exists(args->dnode, "./profile"))
+ profile = yang_dnode_get_string(args->dnode, "./profile");
- if (bfd_monitoring) {
+ if (enabled) {
isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT,
- true);
+ profile, true);
} else {
isis_bfd_circuit_cmd(circuit, ZEBRA_BFD_DEST_DEREGISTER);
bfd_info_free(&circuit->bfd_info);
}
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/enabled
+ */
+int lib_interface_isis_bfd_monitoring_enabled_modify(
+ struct nb_cb_modify_args *args)
+{
+ /* Everything done in apply_finish */
+ return NB_OK;
+}
+/*
+ * XPath: /frr-interface:lib/interface/frr-isisd:isis/bfd-monitoring/profile
+ */
+int lib_interface_isis_bfd_monitoring_profile_modify(
+ struct nb_cb_modify_args *args)
+{
+ /* Everything done in apply_finish */
+ return NB_OK;
+}
+
+int lib_interface_isis_bfd_monitoring_profile_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ /* Everything done in apply_finish */
return NB_OK;
}
diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c
index 562881bbdb..a574c5bd3f 100644
--- a/isisd/isis_vty_fabricd.c
+++ b/isisd/isis_vty_fabricd.c
@@ -315,8 +315,8 @@ DEFUN (isis_bfd,
return CMD_SUCCESS;
}
- isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX,
- BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, true);
+ isis_bfd_circuit_param_set(circuit, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
+ BFD_DEF_DETECT_MULT, NULL, true);
return CMD_SUCCESS;
}
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c
index 0d479e77ba..2c68f3edbd 100644
--- a/ldpd/l2vpn.c
+++ b/ldpd/l2vpn.c
@@ -294,6 +294,16 @@ l2vpn_pw_reset(struct l2vpn_pw *pw)
pw->flags |= F_PW_STATUSTLV;
else
pw->flags &= ~F_PW_STATUSTLV;
+
+ if (pw->flags & F_PW_STATUSTLV_CONF) {
+ struct fec_node *fn;
+ struct fec fec;
+ l2vpn_pw_fec(pw, &fec);
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (fn)
+ pw->remote_status = fn->pw_remote_status;
+ }
+
}
int
@@ -433,6 +443,8 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm)
/* unknown fec */
return;
+ fn->pw_remote_status = nm->pw_status;
+
pw = (struct l2vpn_pw *) fn->data;
if (pw == NULL)
return;
diff --git a/ldpd/lde.c b/ldpd/lde.c
index 4fca4b0962..734c1ea230 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -296,7 +296,7 @@ lde_dispatch_imsg(struct thread *thread)
switch (imsg.hdr.type) {
case IMSG_LABEL_MAPPING:
- lde_check_mapping(map, ln);
+ lde_check_mapping(map, ln, 1);
break;
case IMSG_LABEL_REQUEST:
lde_check_request(map, ln);
diff --git a/ldpd/lde.h b/ldpd/lde.h
index 2895e00ae5..9e6db3a90b 100644
--- a/ldpd/lde.h
+++ b/ldpd/lde.h
@@ -125,6 +125,9 @@ struct fec_node {
struct lde_map_head upstream; /* sent mappings */
uint32_t local_label;
+
+ uint32_t pw_remote_status;
+
void *data; /* fec specific data */
};
@@ -209,7 +212,7 @@ void lde_kernel_insert(struct fec *, int, union ldpd_addr *,
void lde_kernel_remove(struct fec *, int, union ldpd_addr *,
ifindex_t, uint8_t, unsigned short);
void lde_kernel_update(struct fec *);
-void lde_check_mapping(struct map *, struct lde_nbr *);
+void lde_check_mapping(struct map *, struct lde_nbr *, int);
void lde_check_request(struct map *, struct lde_nbr *);
void lde_check_request_wcard(struct map *, struct lde_nbr *);
void lde_check_release(struct map *, struct lde_nbr *);
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 8f524e0aa9..71fb0c7bf2 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -267,6 +267,9 @@ fec_add(struct fec *fec)
RB_INIT(lde_map_head, &fn->downstream);
LIST_INIT(&fn->nexthops);
+ if (fec->type == FEC_TYPE_PWID)
+ fn->pw_remote_status = PW_FORWARDING;
+
if (fec_insert(&ft, &fn->fec))
log_warnx("failed to add %s to ft tree",
log_fec(&fn->fec));
@@ -455,13 +458,13 @@ lde_kernel_update(struct fec *fec)
me = (struct lde_map *)fec_find(&ln->recv_map, &fn->fec);
if (me)
/* FEC.5 */
- lde_check_mapping(&me->map, ln);
+ lde_check_mapping(&me->map, ln, 0);
}
}
}
void
-lde_check_mapping(struct map *map, struct lde_nbr *ln)
+lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping)
{
struct fec fec;
struct fec_node *fn;
@@ -507,8 +510,12 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)
lde_req_del(ln, lre, 1);
/* RFC 4447 control word and status tlv negotiation */
- if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map))
+ if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) {
+ if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS)
+ fn->pw_remote_status = map->pw_status;
+
return;
+ }
/*
* LMp.3 - LMp.8: loop detection - unnecessary for frame-mode
@@ -570,8 +577,10 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln)
pw->remote_group = map->fec.pwid.group_id;
if (map->flags & F_MAP_PW_IFMTU)
pw->remote_mtu = map->fec.pwid.ifmtu;
- if (map->flags & F_MAP_PW_STATUS)
+ if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) {
pw->remote_status = map->pw_status;
+ fn->pw_remote_status = map->pw_status;
+ }
else
pw->remote_status = PW_FORWARDING;
fnh->remote_label = map->label;
diff --git a/lib/bfd.c b/lib/bfd.c
index 6173ebc197..d1a0ec671e 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -94,7 +94,8 @@ int bfd_validate_param(struct vty *vty, const char *dm_str, const char *rx_str,
* bfd_set_param - Set the configured BFD paramter values
*/
void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
- uint8_t detect_mult, int defaults, int *command)
+ uint8_t detect_mult, const char *profile, int defaults,
+ int *command)
{
if (!*bfd_info) {
*bfd_info = bfd_info_create();
@@ -102,7 +103,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
} else {
if (((*bfd_info)->required_min_rx != min_rx)
|| ((*bfd_info)->desired_min_tx != min_tx)
- || ((*bfd_info)->detect_mult != detect_mult))
+ || ((*bfd_info)->detect_mult != detect_mult)
+ || (profile && strcmp((*bfd_info)->profile, profile)))
*command = ZEBRA_BFD_DEST_UPDATE;
}
@@ -110,6 +112,11 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
(*bfd_info)->required_min_rx = min_rx;
(*bfd_info)->desired_min_tx = min_tx;
(*bfd_info)->detect_mult = detect_mult;
+ if (profile)
+ strlcpy((*bfd_info)->profile, profile,
+ BFD_PROFILE_NAME_LEN);
+ else
+ (*bfd_info)->profile[0] = '\0';
}
if (!defaults)
@@ -121,6 +128,8 @@ void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx, uint32_t min_tx,
/*
* bfd_peer_sendmsg - Format and send a peer register/Unregister
* command to Zebra to be forwarded to BFD
+ *
+ * DEPRECATED: use zclient_bfd_command instead
*/
void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip, char *if_name,
@@ -161,6 +170,11 @@ void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
args.min_rx = bfd_info->required_min_rx;
args.min_tx = bfd_info->desired_min_tx;
args.detection_multiplier = bfd_info->detect_mult;
+ if (bfd_info->profile[0]) {
+ args.profilelen = strlen(bfd_info->profile);
+ strlcpy(args.profile, bfd_info->profile,
+ sizeof(args.profile));
+ }
}
addrlen = family == AF_INET ? sizeof(struct in_addr)
@@ -425,6 +439,15 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
struct stream *s;
size_t addrlen;
+ /* Individual reg/dereg messages are suppressed during shutdown. */
+ if (CHECK_FLAG(bfd_gbl.flags, BFD_GBL_FLAG_IN_SHUTDOWN)) {
+ if (bfd_debug)
+ zlog_debug(
+ "%s: Suppressing BFD peer reg/dereg messages",
+ __func__);
+ return -1;
+ }
+
/* Check socket. */
if (!zc || zc->sock < 0) {
if (bfd_debug)
diff --git a/lib/bfd.h b/lib/bfd.h
index d7d4b5fe35..ceab4628b6 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -92,8 +92,8 @@ extern int bfd_validate_param(struct vty *vty, const char *dm_str,
uint32_t *tx_val);
extern void bfd_set_param(struct bfd_info **bfd_info, uint32_t min_rx,
- uint32_t min_tx, uint8_t detect_mult, int defaults,
- int *command);
+ uint32_t min_tx, uint8_t detect_mult,
+ const char *profile, int defaults, int *command);
extern void bfd_peer_sendmsg(struct zclient *zclient, struct bfd_info *bfd_info,
int family, void *dst_ip, void *src_ip,
char *if_name, int ttl, int multihop, int cbit,
diff --git a/lib/command.h b/lib/command.h
index 21bb613540..1acca90665 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -328,18 +328,6 @@ struct cmd_node {
DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \
CMD_ATTR_DEPRECATED, daemon)
-#else /* VTYSH_EXTRACT_PL */
-#define DEFPY(funcname, cmdname, cmdstr, helpstr) \
- DEFUN(funcname, cmdname, cmdstr, helpstr)
-
-#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr)
-
-#define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \
- DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr)
-
-#define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \
- DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr)
#endif /* VTYSH_EXTRACT_PL */
/* Some macroes */
diff --git a/lib/libfrr.c b/lib/libfrr.c
index b3df7de6d3..2597eb61e2 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -105,6 +105,7 @@ static const struct option lo_always[] = {
{"daemon", no_argument, NULL, 'd'},
{"module", no_argument, NULL, 'M'},
{"profile", required_argument, NULL, 'F'},
+ {"pathspace", required_argument, NULL, 'N'},
{"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
{"moduledir", required_argument, NULL, OPTION_MODULEDIR},
{"log", required_argument, NULL, OPTION_LOG},
@@ -113,12 +114,13 @@ static const struct option lo_always[] = {
{"command-log-always", no_argument, NULL, OPTION_LOGGING},
{NULL}};
static const struct optspec os_always = {
- "hvdM:F:",
+ "hvdM:F:N:",
" -h, --help Display this help and exit\n"
" -v, --version Print program version\n"
" -d, --daemon Runs in daemon mode\n"
" -M, --module Load specified module\n"
" -F, --profile Use specified configuration profile\n"
+ " -N, --pathspace Insert prefix into config & socket paths\n"
" --vty_socket Override vty socket path\n"
" --moduledir Override modules directory\n"
" --log Set Logging to stdout, syslog, or file:<name>\n"
@@ -133,18 +135,16 @@ static const struct option lo_cfg_pid_dry[] = {
#ifdef HAVE_SQLITE3
{"db_file", required_argument, NULL, OPTION_DB_FILE},
#endif
- {"pathspace", required_argument, NULL, 'N'},
{"dryrun", no_argument, NULL, 'C'},
{"terminal", no_argument, NULL, 't'},
{NULL}};
static const struct optspec os_cfg_pid_dry = {
- "f:i:CtN:",
+ "f:i:Ct",
" -f, --config_file Set configuration file name\n"
" -i, --pid_file Set process identifier file name\n"
#ifdef HAVE_SQLITE3
" --db_file Set database file name\n"
#endif
- " -N, --pathspace Insert prefix into config & socket paths\n"
" -C, --dryrun Check configuration for validity and exit\n"
" -t, --terminal Open terminal session on stdio\n"
" -d -t Daemonize after terminal session ends\n",
@@ -428,8 +428,6 @@ static int frr_opt(int opt)
di->config_file = optarg;
break;
case 'N':
- if (di->flags & FRR_NO_CFG_PID_DRY)
- return 1;
if (di->pathspace) {
fprintf(stderr,
"-N/--pathspace option specified more than once!\n");
diff --git a/lib/nexthop.c b/lib/nexthop.c
index 3496081d47..28d96a539c 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -157,6 +157,10 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
goto done;
if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
+ !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ return 0;
+
+ if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return -1;
@@ -164,12 +168,18 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
!CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return 1;
- if (next1->backup_idx < next2->backup_idx)
+ if (next1->backup_num == 0 && next2->backup_num == 0)
+ goto done;
+
+ if (next1->backup_num < next2->backup_num)
return -1;
- if (next1->backup_idx > next2->backup_idx)
+ if (next1->backup_num > next2->backup_num)
return 1;
+ ret = memcmp(next1->backup_idx,
+ next2->backup_idx, next1->backup_num);
+
done:
return ret;
}
@@ -515,11 +525,12 @@ struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
return next;
}
-unsigned int nexthop_level(struct nexthop *nexthop)
+unsigned int nexthop_level(const struct nexthop *nexthop)
{
unsigned int rv = 0;
- for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
+ for (const struct nexthop *par = nexthop->rparent;
+ par; par = par->rparent)
rv++;
return rv;
@@ -529,14 +540,15 @@ unsigned int nexthop_level(struct nexthop *nexthop)
uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
- uint32_t val;
+ int i;
key = jhash_3words(nexthop->type, nexthop->vrf_id,
nexthop->nh_label_type, key);
if (nexthop->nh_label) {
int labels = nexthop->nh_label->num_labels;
- int i = 0;
+
+ i = 0;
while (labels >= 3) {
key = jhash_3words(nexthop->nh_label->label[i],
@@ -559,14 +571,35 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
key = jhash_1word(nexthop->nh_label->label[i], key);
}
- val = 0;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- val = (uint32_t)nexthop->backup_idx;
-
- key = jhash_3words(nexthop->ifindex,
- CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val,
+ key = jhash_2words(nexthop->ifindex,
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
key);
+ /* Include backup nexthops, if present */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ int backups = nexthop->backup_num;
+
+ i = 0;
+
+ while (backups >= 3) {
+ key = jhash_3words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1],
+ nexthop->backup_idx[i + 2], key);
+ backups -= 3;
+ i += 3;
+ }
+
+ while (backups >= 2) {
+ key = jhash_2words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1], key);
+ backups -= 2;
+ i += 2;
+ }
+
+ if (backups >= 1)
+ key = jhash_1word(nexthop->backup_idx[i], key);
+ }
+
return key;
}
@@ -604,7 +637,12 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
copy->type = nexthop->type;
copy->flags = nexthop->flags;
copy->weight = nexthop->weight;
- copy->backup_idx = nexthop->backup_idx;
+
+ assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
+ copy->backup_num = nexthop->backup_num;
+ if (copy->backup_num > 0)
+ memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
+
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
@@ -621,7 +659,7 @@ void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
nexthop_copy_no_recurse(copy, nexthop, rparent);
/* Bit of a special case here, we need to handle the case
- * of a nexthop resolving to agroup. Hence, we need to
+ * of a nexthop resolving to a group. Hence, we need to
* use a nexthop_group API.
*/
if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
@@ -647,6 +685,67 @@ struct nexthop *nexthop_dup(const struct nexthop *nexthop,
}
/*
+ * Parse one or more backup index values, as comma-separated numbers,
+ * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
+ * in size. Mails back the number of values converted, and returns 0 on
+ * success, <0 if an error in parsing.
+ */
+int nexthop_str2backups(const char *str, int *num_backups,
+ uint8_t *backups)
+{
+ char *ostr; /* copy of string (start) */
+ char *lstr; /* working copy of string */
+ char *nump; /* pointer to next segment */
+ char *endp; /* end pointer */
+ int i, ret;
+ uint8_t tmp[NEXTHOP_MAX_BACKUPS];
+ uint32_t lval;
+
+ /* Copy incoming string; the parse is destructive */
+ lstr = ostr = XSTRDUP(MTYPE_TMP, str);
+ *num_backups = 0;
+ ret = 0;
+
+ for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) {
+ nump = strsep(&lstr, ",");
+ lval = strtoul(nump, &endp, 10);
+
+ /* Format check */
+ if (*endp != '\0') {
+ ret = -1;
+ break;
+ }
+
+ /* Empty value */
+ if (endp == nump) {
+ ret = -1;
+ break;
+ }
+
+ /* Limit to one octet */
+ if (lval > 255) {
+ ret = -1;
+ break;
+ }
+
+ tmp[i] = lval;
+ }
+
+ /* Excess values */
+ if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr)
+ ret = -1;
+
+ if (ret == 0) {
+ *num_backups = i;
+ memcpy(backups, tmp, i);
+ }
+
+ XFREE(MTYPE_TMP, ostr);
+
+ return ret;
+}
+
+/*
* nexthop printing variants:
* %pNHvv
* via 1.2.3.4
diff --git a/lib/nexthop.h b/lib/nexthop.h
index eda88efc08..ed40cc7eed 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -65,6 +65,12 @@ enum nh_encap_type {
NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */
};
+/* Fixed limit on the number of backup nexthops per primary nexthop */
+#define NEXTHOP_MAX_BACKUPS 8
+
+/* Backup index value is limited */
+#define NEXTHOP_BACKUP_IDX_MAX 255
+
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
@@ -124,10 +130,11 @@ struct nexthop {
/* Weight of the nexthop ( for unequal cost ECMP ) */
uint8_t weight;
- /* Index of a corresponding backup nexthop in a backup list;
+ /* Count and index of corresponding backup nexthop(s) in a backup list;
* only meaningful if the HAS_BACKUP flag is set.
*/
- uint8_t backup_idx;
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
/* Encapsulation information. */
enum nh_encap_type nh_encap_type;
@@ -136,9 +143,6 @@ struct nexthop {
} nh_encap;
};
-/* Backup index value is limited */
-#define NEXTHOP_BACKUP_IDX_MAX 255
-
/* Utility to append one nexthop to another. */
#define NEXTHOP_APPEND(to, new) \
do { \
@@ -216,7 +220,7 @@ extern const char *nexthop2str(const struct nexthop *nexthop,
extern struct nexthop *nexthop_next(const struct nexthop *nexthop);
extern struct nexthop *
nexthop_next_active_resolved(const struct nexthop *nexthop);
-extern unsigned int nexthop_level(struct nexthop *nexthop);
+extern unsigned int nexthop_level(const struct nexthop *nexthop);
/* Copies to an already allocated nexthop struct */
extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
struct nexthop *rparent);
@@ -231,6 +235,15 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop,
extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
struct nexthop *rparent);
+/*
+ * Parse one or more backup index values, as comma-separated numbers,
+ * into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
+ * in size. Mails back the number of values converted, and returns 0 on
+ * success, <0 if an error in parsing.
+ */
+int nexthop_str2backups(const char *str, int *num_backups,
+ uint8_t *backups);
+
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pNH" (struct nexthop *)
#endif
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index 4f0c72af27..97815ceeb9 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -43,12 +43,9 @@ struct nexthop_hold {
char *intf;
char *labels;
uint32_t weight;
- int backup_idx; /* Index of backup nexthop, if >= 0 */
+ char *backup_str;
};
-/* Invalid/unset value for nexthop_hold's backup_idx */
-#define NHH_BACKUP_IDX_INVALID -1
-
struct nexthop_group_hooks {
void (*new)(const char *name);
void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
@@ -677,7 +674,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name,
const union sockunion *addr,
const char *intf, const char *labels,
- const uint32_t weight, int backup_idx)
+ const uint32_t weight,
+ const char *backup_str)
{
struct nexthop_hold *nh;
@@ -694,7 +692,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
nh->weight = weight;
- nh->backup_idx = backup_idx;
+ if (backup_str)
+ nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
listnode_add_sort(nhgc->nhg_list, nh);
}
@@ -741,10 +740,11 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
const union sockunion *addr,
const char *intf, const char *name,
const char *labels, int *lbl_ret,
- uint32_t weight, int backup_idx)
+ uint32_t weight, const char *backup_str)
{
int ret = 0;
struct vrf *vrf;
+ int num;
memset(nhop, 0, sizeof(*nhop));
@@ -800,13 +800,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
nhop->weight = weight;
- if (backup_idx != NHH_BACKUP_IDX_INVALID) {
- /* Validate index value */
- if (backup_idx > NEXTHOP_BACKUP_IDX_MAX)
+ if (backup_str) {
+ /* Parse backup indexes */
+ ret = nexthop_str2backups(backup_str,
+ &num, nhop->backup_idx);
+ if (ret == 0) {
+ SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nhop->backup_num = num;
+ } else
return false;
-
- SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nhop->backup_idx = backup_idx;
}
return true;
@@ -820,7 +822,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop,
{
return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name, nhh->labels, NULL,
- nhh->weight, nhh->backup_idx));
+ nhh->weight, nhh->backup_str));
}
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
@@ -833,7 +835,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
nexthop-vrf NAME$vrf_name \
|label WORD \
|weight (1-255) \
- |backup-idx$bi_str (0-254)$idx \
+ |backup-idx WORD \
}]",
NO_STR
"Specify one of the nexthops in this ECMP group\n"
@@ -847,19 +849,26 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"One or more labels in the range (16-1048575) separated by '/'\n"
"Weight to be used by the nexthop for purposes of ECMP\n"
"Weight value to be used\n"
- "Backup nexthop index in another group\n"
- "Nexthop index value\n")
+ "Specify backup nexthop indexes in another group\n"
+ "One or more indexes in the range (0-254) separated by ','\n")
{
VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct nexthop nhop;
struct nexthop *nh;
int lbl_ret = 0;
bool legal;
- int backup_idx = idx;
+ int num;
+ uint8_t backups[NEXTHOP_MAX_BACKUPS];
bool yes = !no;
- if (bi_str == NULL)
- backup_idx = NHH_BACKUP_IDX_INVALID;
+ /* Pre-parse backup string to validate */
+ if (backup_idx) {
+ lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
+ if (lbl_ret < 0) {
+ vty_out(vty, "%% Invalid backups\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
&lbl_ret, weight, backup_idx);
@@ -943,10 +952,11 @@ static struct cmd_node nexthop_group_node = {
.config_write = nexthop_group_write,
};
-void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
+void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
{
char buf[100];
struct vrf *vrf;
+ int i;
vty_out(vty, "nexthop ");
@@ -991,16 +1001,22 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
- if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, " backup-idx %d", nh->backup_idx);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
+
+ for (i = 1; i < nh->backup_num; i++)
+ vty_out(vty, ",%d", nh->backup_idx[i]);
+ }
vty_out(vty, "\n");
}
-void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh)
+void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
{
char buf[100];
struct vrf *vrf;
+ json_object *json_backups = NULL;
+ int i;
switch (nh->type) {
case NEXTHOP_TYPE_IFINDEX:
@@ -1047,12 +1063,19 @@ void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh)
if (nh->weight)
json_object_int_add(j, "weight", nh->weight);
- if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(j, "backupIdx", nh->backup_idx);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nh->backup_num; i++)
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nh->backup_idx[i]));
+
+ json_object_object_add(j, "backupIdx", json_backups);
+ }
}
static void nexthop_group_write_nexthop_internal(struct vty *vty,
- struct nexthop_hold *nh)
+ const struct nexthop_hold *nh)
{
char buf[100];
@@ -1073,8 +1096,8 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
if (nh->weight)
vty_out(vty, " weight %u", nh->weight);
- if (nh->backup_idx != NHH_BACKUP_IDX_INVALID)
- vty_out(vty, " backup-idx %d", nh->backup_idx);
+ if (nh->backup_str)
+ vty_out(vty, " backup-idx %s", nh->backup_str);
vty_out(vty, "\n");
}
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index 9888dad982..0b5ac91bb2 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -135,9 +135,11 @@ extern bool nexthop_group_equal(const struct nexthop_group *nhg1,
extern struct nexthop_group_cmd *nhgc_find(const char *name);
-extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh);
+extern void nexthop_group_write_nexthop(struct vty *vty,
+ const struct nexthop *nh);
-extern void nexthop_group_json_nexthop(json_object *j, struct nexthop *nh);
+extern void nexthop_group_json_nexthop(json_object *j,
+ const struct nexthop *nh);
/* Return the number of nexthops in this nhg */
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
diff --git a/lib/pbr.h b/lib/pbr.h
index cf6ac41d32..fd183d7115 100644
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,6 +49,10 @@ struct pbr_filter {
#define PBR_FILTER_PROTO (1 << 5)
#define PBR_FILTER_SRC_PORT_RANGE (1 << 6)
#define PBR_FILTER_DST_PORT_RANGE (1 << 7)
+#define PBR_FILTER_DSFIELD (1 << 8)
+
+#define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
+#define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
/* Source and Destination IP address with masks. */
struct prefix src_ip;
@@ -58,6 +62,9 @@ struct pbr_filter {
uint16_t src_port;
uint16_t dst_port;
+ /* Filter by Differentiated Services field */
+ uint8_t dsfield; /* DSCP (6 bits) & ECN (2 bits) */
+
/* Filter with fwmark */
uint32_t fwmark;
};
diff --git a/lib/route_types.pl b/lib/route_types.pl
index f297096633..e007de4d69 100755
--- a/lib/route_types.pl
+++ b/lib/route_types.pl
@@ -121,7 +121,7 @@ sub codelist {
}
$str =~ s/ $//;
push @lines, $str . "\\n\" \\\n";
- push @lines, " \" > - selected route, * - FIB route, q - queued route, r - rejected route\\n\\n\"";
+ push @lines, " \" > - selected route, * - FIB route, q - queued, r - rejected, b - backup\\n\\n\"";
return join("", @lines);
}
diff --git a/lib/stream.c b/lib/stream.c
index 17520f978e..d3afebbf13 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -55,15 +55,19 @@ DEFINE_MTYPE_STATIC(LIB, STREAM_FIFO, "Stream FIFO")
* using stream_put..._at() functions.
*/
#define STREAM_WARN_OFFSETS(S) \
- flog_warn(EC_LIB_STREAM, \
- "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
- (void *)(S), (unsigned long)(S)->size, \
- (unsigned long)(S)->getp, (unsigned long)(S)->endp)
+ do { \
+ flog_warn(EC_LIB_STREAM, \
+ "&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \
+ (void *)(S), (unsigned long)(S)->size, \
+ (unsigned long)(S)->getp, (unsigned long)(S)->endp); \
+ zlog_backtrace(LOG_WARNING); \
+ } while (0)
#define STREAM_VERIFY_SANE(S) \
do { \
- if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) \
+ if (!(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp))) { \
STREAM_WARN_OFFSETS(S); \
+ } \
assert(GETP_VALID(S, (S)->getp)); \
assert(ENDP_VALID(S, (S)->endp)); \
} while (0)
diff --git a/lib/subdir.am b/lib/subdir.am
index f185841c76..34ad30f968 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -124,9 +124,6 @@ nodist_lib_libfrr_la_SOURCES = \
yang/ietf/ietf-interfaces.yang.c \
yang/frr-module-translator.yang.c \
yang/frr-nexthop.yang.c \
- yang/frr-igmp.yang.c \
- yang/frr-pim.yang.c \
- yang/frr-pim-rp.yang.c \
# end
vtysh_scan += \
diff --git a/lib/vrf.c b/lib/vrf.c
index a9ea632ac5..a7a10451bf 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -757,10 +757,8 @@ DEFUN (no_vrf,
vrfp = vrf_lookup_by_name(vrfname);
- if (vrfp == NULL) {
- vty_out(vty, "%% VRF %s does not exist\n", vrfname);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (vrfp == NULL)
+ return CMD_SUCCESS;
if (CHECK_FLAG(vrfp->status, VRF_ACTIVE)) {
vty_out(vty, "%% Only inactive VRFs can be deleted\n");
diff --git a/lib/zclient.c b/lib/zclient.c
index d8f311fcec..92ff2537d5 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -888,7 +888,7 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp,
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
uint32_t api_flags)
{
- int ret = 0;
+ int i, ret = 0;
int nh_flags = api_nh->flags;
stream_putl(s, api_nh->vrf_id);
@@ -951,8 +951,17 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
sizeof(struct ethaddr));
/* Index of backup nexthop */
- if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- stream_putc(s, api_nh->backup_idx);
+ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ /* Validate backup count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ ret = -1;
+ goto done;
+ }
+
+ stream_putc(s, api_nh->backup_num);
+ for (i = 0; i < api_nh->backup_num; i++)
+ stream_putc(s, api_nh->backup_idx[i]);
+ }
done:
return ret;
@@ -1111,7 +1120,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
uint32_t api_flags)
{
- int ret = -1;
+ int i, ret = -1;
STREAM_GETL(s, api_nh->vrf_id);
STREAM_GETC(s, api_nh->type);
@@ -1163,8 +1172,15 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
sizeof(struct ethaddr));
/* Backup nexthop index */
- if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- STREAM_GETC(s, api_nh->backup_idx);
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ STREAM_GETC(s, api_nh->backup_num);
+
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
+ for (i = 0; i < api_nh->backup_num; i++)
+ STREAM_GETC(s, api_nh->backup_idx[i]);
+ }
/* Success */
ret = 0;
@@ -1483,7 +1499,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP);
- n->backup_idx = znh->backup_idx;
+ n->backup_num = znh->backup_num;
+ memcpy(n->backup_idx, znh->backup_idx, n->backup_num);
}
return n;
@@ -1519,8 +1536,12 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
}
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ if (nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
- znh->backup_idx = nh->backup_idx;
+ znh->backup_num = nh->backup_num;
+ memcpy(znh->backup_idx, nh->backup_idx, znh->backup_num);
}
return 0;
diff --git a/lib/zclient.h b/lib/zclient.h
index 3ded2f55d7..250824e612 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -394,8 +394,9 @@ struct zapi_nexthop {
uint32_t weight;
- /* Index of backup nexthop */
- uint8_t backup_idx;
+ /* Backup nexthops, for IP-FRR, TI-LFA, etc */
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
};
/*
diff --git a/lib/zlog.c b/lib/zlog.c
index 45726755f8..8dfd20371b 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -246,10 +246,10 @@ void zlog_tls_buffer_init(void)
fchown(mmfd, zlog_uid, zlog_gid);
#ifdef HAVE_POSIX_FALLOCATE
- if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) < 0) {
-#else
- if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
+ if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0)
+ /* note next statement is under above if() */
#endif
+ if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
zlog_err("failed to allocate thread log buffer \"%s\": %s",
mmpath, strerror(errno));
goto out_anon_unlink;
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index 9fe077b544..713ce26ecb 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -379,22 +379,6 @@ void ospf6_area_show(struct vty *vty, struct ospf6_area *oa)
vty_out(vty, "SPF has not been run\n");
}
-
-#define OSPF6_CMD_AREA_GET(str, oa) \
- { \
- char *ep; \
- uint32_t area_id = htonl(strtoul(str, &ep, 10)); \
- if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \
- vty_out(vty, "Malformed Area-ID: %s\n", str); \
- return CMD_SUCCESS; \
- } \
- int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \
- : OSPF6_AREA_FMT_DOTTEDQUAD; \
- oa = ospf6_area_lookup(area_id, ospf6); \
- if (oa == NULL) \
- oa = ospf6_area_create(area_id, ospf6, format); \
- }
-
DEFUN (area_range,
area_range_cmd,
"area <A.B.C.D|(0-4294967295)> range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>]",
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index 5648b1dfec..7ce6717fcc 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -117,6 +117,21 @@ struct ospf6_area {
#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT))
#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB))
+#define OSPF6_CMD_AREA_GET(str, oa) \
+ { \
+ char *ep; \
+ uint32_t area_id = htonl(strtoul(str, &ep, 10)); \
+ if (*ep && inet_pton(AF_INET, str, &area_id) != 1) { \
+ vty_out(vty, "Malformed Area-ID: %s\n", str); \
+ return CMD_SUCCESS; \
+ } \
+ int format = !*ep ? OSPF6_AREA_FMT_DECIMAL \
+ : OSPF6_AREA_FMT_DOTTEDQUAD; \
+ oa = ospf6_area_lookup(area_id, ospf6); \
+ if (oa == NULL) \
+ oa = ospf6_area_create(area_id, ospf6, format); \
+ }
+
/* prototypes */
extern int ospf6_area_cmp(void *va, void *vb);
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
index 4e7a0050aa..916e59baf0 100644
--- a/ospf6d/ospf6_bfd.c
+++ b/ospf6d/ospf6_bfd.c
@@ -308,7 +308,7 @@ static void ospf6_bfd_if_param_set(struct ospf6_interface *oi, uint32_t min_rx,
int command = 0;
bfd_set_param((struct bfd_info **)&(oi->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
if (command)
ospf6_bfd_reg_dereg_all_nbr(oi, command);
}
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index a36012194d..50687a7290 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -642,11 +642,12 @@ DEFUN (no_ospf6_distance_source,
DEFUN (ospf6_interface_area,
ospf6_interface_area_cmd,
- "interface IFNAME area A.B.C.D",
+ "interface IFNAME area <A.B.C.D|(0-4294967295)>",
"Enable routing on an IPv6 interface\n"
IFNAME_STR
"Specify the OSPF6 area ID\n"
"OSPF6 area ID in IPv4 address notation\n"
+ "OSPF6 area ID in decimal notation\n"
)
{
VTY_DECLVAR_CONTEXT(ospf6, o);
@@ -655,7 +656,6 @@ DEFUN (ospf6_interface_area,
struct ospf6_area *oa;
struct ospf6_interface *oi;
struct interface *ifp;
- uint32_t area_id;
/* find/create ospf6 interface */
ifp = if_get_by_name(argv[idx_ifname]->arg, VRF_DEFAULT);
@@ -669,15 +669,7 @@ DEFUN (ospf6_interface_area,
}
/* parse Area-ID */
- if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) {
- vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg);
- return CMD_SUCCESS;
- }
-
- /* find/create ospf6 area */
- oa = ospf6_area_lookup(area_id, o);
- if (oa == NULL)
- oa = ospf6_area_create(area_id, o, OSPF6_AREA_FMT_DOTTEDQUAD);
+ OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa);
/* attach interface to area */
listnode_add(oa->if_list, oi); /* sort ?? */
@@ -701,12 +693,13 @@ DEFUN (ospf6_interface_area,
DEFUN (no_ospf6_interface_area,
no_ospf6_interface_area_cmd,
- "no interface IFNAME area A.B.C.D",
+ "no interface IFNAME area <A.B.C.D|(0-4294967295)>",
NO_STR
"Disable routing on an IPv6 interface\n"
IFNAME_STR
"Specify the OSPF6 area ID\n"
"OSPF6 area ID in IPv4 address notation\n"
+ "OSPF6 area ID in decimal notation\n"
)
{
int idx_ifname = 2;
@@ -729,10 +722,8 @@ DEFUN (no_ospf6_interface_area,
}
/* parse Area-ID */
- if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) {
- vty_out(vty, "Invalid Area-ID: %s\n", argv[idx_ipv4]->arg);
- return CMD_SUCCESS;
- }
+ if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1)
+ area_id = htonl(strtoul(argv[idx_ipv4]->arg, NULL, 10));
/* Verify Area */
if (oi->area == NULL) {
diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c
index b9e78f4cd3..d2c5090f2f 100644
--- a/ospfd/ospf_bfd.c
+++ b/ospfd/ospf_bfd.c
@@ -381,7 +381,7 @@ static void ospf_bfd_if_param_set(struct interface *ifp, uint32_t min_rx,
params = IF_DEF_PARAMS(ifp);
bfd_set_param((struct bfd_info **)&(params->bfd_info), min_rx, min_tx,
- detect_mult, defaults, &command);
+ detect_mult, NULL, defaults, &command);
if (command)
ospf_bfd_reg_dereg_all_nbr(ifp, command);
}
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 08736b555f..e8cc50c8d0 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -1177,9 +1177,9 @@ DEFUN (no_ospf_area_vlink,
"no area <A.B.C.D|(0-4294967295)> virtual-link A.B.C.D [authentication [<message-digest|null>]] [<message-digest-key (1-255) md5 KEY|authentication-key AUTH_KEY>]",
NO_STR
VLINK_HELPSTR_IPADDR
- "Enable authentication on this virtual link\n" \
- "Use message-digest authentication\n" \
- "Use null authentication\n" \
+ "Enable authentication on this virtual link\n"
+ "Use message-digest authentication\n"
+ "Use null authentication\n"
VLINK_HELPSTR_AUTH_MD5
VLINK_HELPSTR_AUTH_SIMPLE)
{
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c
index 0ef8432e5e..10a75a9f54 100644
--- a/pbrd/pbr_map.c
+++ b/pbrd/pbr_map.c
@@ -444,6 +444,59 @@ static void pbr_map_add_interfaces(struct pbr_map *pbrm)
}
}
+/* Decodes a standardized DSCP into its representative value */
+uint8_t pbr_map_decode_dscp_enum(const char *name)
+{
+ /* Standard Differentiated Services Field Codepoints */
+ if (!strcmp(name, "cs0"))
+ return 0;
+ if (!strcmp(name, "cs1"))
+ return 8;
+ if (!strcmp(name, "cs2"))
+ return 16;
+ if (!strcmp(name, "cs3"))
+ return 24;
+ if (!strcmp(name, "cs4"))
+ return 32;
+ if (!strcmp(name, "cs5"))
+ return 40;
+ if (!strcmp(name, "cs6"))
+ return 48;
+ if (!strcmp(name, "cs7"))
+ return 56;
+ if (!strcmp(name, "af11"))
+ return 10;
+ if (!strcmp(name, "af12"))
+ return 12;
+ if (!strcmp(name, "af13"))
+ return 14;
+ if (!strcmp(name, "af21"))
+ return 18;
+ if (!strcmp(name, "af22"))
+ return 20;
+ if (!strcmp(name, "af23"))
+ return 22;
+ if (!strcmp(name, "af31"))
+ return 26;
+ if (!strcmp(name, "af32"))
+ return 28;
+ if (!strcmp(name, "af33"))
+ return 30;
+ if (!strcmp(name, "af41"))
+ return 34;
+ if (!strcmp(name, "af42"))
+ return 36;
+ if (!strcmp(name, "af43"))
+ return 38;
+ if (!strcmp(name, "ef"))
+ return 46;
+ if (!strcmp(name, "voice-admit"))
+ return 44;
+
+ /* No match? Error out */
+ return -1;
+}
+
struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)
{
struct pbr_map *pbrm;
@@ -547,7 +600,7 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)
static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms)
{
- if (!pbrms->src && !pbrms->dst && !pbrms->mark)
+ if (!pbrms->src && !pbrms->dst && !pbrms->mark && !pbrms->dsfield)
pbrms->reason |= PBR_MAP_INVALID_EMPTY;
}
diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h
index 704f1a0361..64c090d2e8 100644
--- a/pbrd/pbr_map.h
+++ b/pbrd/pbr_map.h
@@ -89,6 +89,7 @@ struct pbr_map_sequence {
*/
struct prefix *src;
struct prefix *dst;
+ uint8_t dsfield;
uint32_t mark;
/*
@@ -168,6 +169,8 @@ extern void pbr_map_add_interface(struct pbr_map *pbrm, struct interface *ifp);
extern void pbr_map_interface_delete(struct pbr_map *pbrm,
struct interface *ifp);
+extern uint8_t pbr_map_decode_dscp_enum(const char *name);
+
/* Update maps installed on interface */
extern void pbr_map_policy_interface_update(const struct interface *ifp,
bool state_up);
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index cd9096cbc8..a73d885ea6 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -183,6 +183,91 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
return CMD_SUCCESS;
}
+DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
+ "[no] match dscp DSCP$dscp",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP DSCP field\n"
+ "DSCP value (below 64) or standard codepoint name\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+ char dscpname[100];
+ uint8_t rawDscp;
+
+ /* Discriminate dscp enums (cs0, cs1 etc.) and numbers */
+ bool isANumber = true;
+ for (int i = 0; i < (int)strlen(dscp); i++) {
+ /* Letters are not numbers */
+ if (!isdigit(dscp[i]))
+ isANumber = false;
+
+ /* Lowercase the dscp enum (if needed) */
+ if (isupper(dscp[i]))
+ dscpname[i] = tolower(dscp[i]);
+ else
+ dscpname[i] = dscp[i];
+ }
+ dscpname[strlen(dscp)] = '\0';
+
+ if (isANumber) {
+ /* dscp passed is a regular number */
+ long dscpAsNum = strtol(dscp, NULL, 0);
+
+ if (dscpAsNum > PBR_DSFIELD_DSCP >> 2) {
+ /* Refuse to install on overflow */
+ vty_out(vty, "dscp (%s) must be less than 64\n", dscp);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rawDscp = dscpAsNum;
+ } else {
+ /* check dscp if it is an enum like cs0 */
+ rawDscp = pbr_map_decode_dscp_enum(dscpname);
+ if (rawDscp > PBR_DSFIELD_DSCP) {
+ vty_out(vty, "Invalid dscp value: %s\n", dscpname);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (!no) {
+ if (((pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2) == rawDscp)
+ return CMD_SUCCESS;
+
+ /* Set the DSCP bits of the DSField */
+ pbrms->dsfield =
+ (pbrms->dsfield & ~PBR_DSFIELD_DSCP) | (rawDscp << 2);
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_DSCP;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_ecn, pbr_map_match_ecn_cmd,
+ "[no] match ecn (0-3)$ecn",
+ NO_STR
+ "Match the rest of the command\n"
+ "Match based on IP ECN field\n"
+ "Explicit Congestion Notification\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ if (!no) {
+ if ((pbrms->dsfield & PBR_DSFIELD_ECN) == ecn)
+ return CMD_SUCCESS;
+
+ /* Set the ECN bits of the DSField */
+ pbrms->dsfield = (pbrms->dsfield & ~PBR_DSFIELD_ECN) | ecn;
+ } else {
+ pbrms->dsfield &= ~PBR_DSFIELD_ECN;
+ }
+
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
"[no] match mark (1-4294967295)$mark",
NO_STR
@@ -559,6 +644,12 @@ static void vty_show_pbrms(struct vty *vty,
if (pbrms->dst)
vty_out(vty, " DST Match: %s\n",
prefix2str(pbrms->dst, buf, sizeof(buf)));
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " DSCP Match: %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " ECN Match: %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
if (pbrms->mark)
vty_out(vty, " MARK Match: %u\n", pbrms->mark);
@@ -653,6 +744,12 @@ static void vty_json_pbrms(json_object *j, struct vty *vty,
prefix2str(pbrms->dst, buf, sizeof(buf)));
if (pbrms->mark)
json_object_int_add(jpbrm, "matchMark", pbrms->mark);
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ json_object_int_add(jpbrm, "matchDscp",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ json_object_int_add(jpbrm, "matchEcn",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
json_object_array_add(j, jpbrm);
}
@@ -946,6 +1043,14 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
vty_out(vty, " match dst-ip %s\n",
prefix2str(pbrms->dst, buff, sizeof(buff)));
+ if (pbrms->dsfield & PBR_DSFIELD_DSCP)
+ vty_out(vty, " match dscp %u\n",
+ (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
+
+ if (pbrms->dsfield & PBR_DSFIELD_ECN)
+ vty_out(vty, " match ecn %u\n",
+ pbrms->dsfield & PBR_DSFIELD_ECN);
+
if (pbrms->mark)
vty_out(vty, " match mark %u\n", pbrms->mark);
@@ -1026,6 +1131,8 @@ void pbr_vty_init(void)
install_element(INTERFACE_NODE, &pbr_policy_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
+ install_element(PBRMAP_NODE, &pbr_map_match_ecn_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index de2a99e269..d0099a46e3 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -536,6 +536,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
stream_putw(s, 0); /* src port */
pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
stream_putw(s, 0); /* dst port */
+ stream_putc(s, pbrms->dsfield);
stream_putl(s, pbrms->mark);
if (pbrms->vrf_unchanged || pbrms->vrf_lookup)
diff --git a/pimd/pim_bfd.c b/pimd/pim_bfd.c
index 0df8ea6922..146b53fa8f 100644
--- a/pimd/pim_bfd.c
+++ b/pimd/pim_bfd.c
@@ -194,7 +194,7 @@ void pim_bfd_if_param_set(struct interface *ifp, uint32_t min_rx,
if (!pim_ifp)
return;
- bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult,
+ bfd_set_param(&(pim_ifp->bfd_info), min_rx, min_tx, detect_mult, NULL,
defaults, &command);
if (pim_ifp->bfd_info) {
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 6ede015a0e..db3f0b8b23 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -6784,12 +6784,13 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, struct vty *vty,
if (result == PIM_GROUP_BAD_ADDR_MASK_COMBO) {
vty_out(vty, "%% Inconsistent address and mask: %s\n",
- group);
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
if (result == PIM_GROUP_BAD_ADDRESS) {
- vty_out(vty, "%% Bad group address specified: %s\n", group);
+ vty_out(vty, "%% Bad group address specified: %s\n",
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
@@ -7158,7 +7159,8 @@ static int pim_no_rp_cmd_worker(struct pim_instance *pim, struct vty *vty,
int result = pim_rp_del_config(pim, rp, group, plist);
if (result == PIM_GROUP_BAD_ADDRESS) {
- vty_out(vty, "%% Bad group address specified: %s\n", group);
+ vty_out(vty, "%% Bad group address specified: %s\n",
+ group ? group : "No Group Address");
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 121abea598..8540651544 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -127,6 +127,12 @@ clippy_scan += \
pimd/pim_cmd.c \
# end
+nodist_pimd_pimd_SOURCES = \
+ yang/frr-igmp.yang.c \
+ yang/frr-pim.yang.c \
+ yang/frr-pim-rp.yang.c \
+ # end
+
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la $(LIBCAP)
pimd_pimd_SOURCES = pimd/pim_main.c
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 48220d1c9b..1d2b87b9ba 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -278,7 +278,8 @@ DEFPY (install_routes,
if (backup) {
/* Set flag and index in primary nexthop */
SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP);
- sg.r.nhop.backup_idx = 0;
+ sg.r.nhop.backup_num = 1;
+ sg.r.nhop.backup_idx[0] = 0;
if (backup_nexthop4.s_addr != INADDR_ANY) {
sg.r.backup_nhop.gate.ipv4 = backup_nexthop4;
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 7ab2d6ec22..74e44014a9 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -155,6 +155,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
}
@@ -188,6 +190,8 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p,
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
if (i > 0)
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
index abca1ed131..d2d0c601c3 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json
@@ -25,14 +25,14 @@
"local": "*",
"multihop": false,
"peer": "*",
- "receive-interval": 300,
+ "receive-interval": 250,
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
"remote-receive-interval": 300,
"remote-transmit-interval": 300,
"status": "up",
- "transmit-interval": 300,
+ "transmit-interval": 250,
"uptime": "*",
"vrf": "default"
}
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
index 74dae5a60d..08eb0468d6 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bfdd.conf
@@ -3,9 +3,8 @@ debug bfd network
debug bfd zebra
!
bfd
- ! profile is commented out on purpose.
- !profile fasttx
- ! receive-interval 250
- ! transmit-interval 250
- !!
+ profile fasttx
+ receive-interval 250
+ transmit-interval 250
+ !
!
diff --git a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
index 9c56a349ed..c7b75d2fde 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/bgpd.conf
@@ -1,7 +1,7 @@
router bgp 100
bgp router-id 10.254.254.3
neighbor 172.16.1.2 remote-as 100
- neighbor 172.16.1.2 bfd profile fasttx
+ neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
index 5d774a356b..d27a783adf 100644
--- a/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r3/isisd.conf
@@ -8,6 +8,7 @@ interface r3-eth1
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile fasttx
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
index c8bc4c20e9..2c2e136abf 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
+++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json
@@ -11,8 +11,8 @@
"remote-detect-multiplier": 3,
"remote-diagnostic": "ok",
"remote-id": "*",
- "remote-receive-interval": 300,
- "remote-transmit-interval": 300,
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
"status": "up",
"transmit-interval": 300,
"uptime": "*",
diff --git a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
index 7c4b39b020..aff1016dee 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/bgpd.conf
@@ -5,7 +5,7 @@ router bgp 200
no bgp ebgp-requires-policy
neighbor 2001:db8:1::2 remote-as 100
neighbor 2001:db8:1::2 ebgp-multihop 2
- neighbor 2001:db8:1::2 bfd profile fasttx
+ neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST
address-family ipv4 unicast
redistribute connected
exit-address-family
diff --git a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
index 477740087d..01e197bed5 100644
--- a/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
+++ b/tests/topotests/bfd-profiles-topo1/r4/isisd.conf
@@ -8,6 +8,7 @@ interface r4-eth0
ipv6 router isis lan
isis circuit-type level-1
isis bfd
+ isis bfd profile DOES_NOT_EXIST
!
router isis lan
net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
diff --git a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
index 02385b32e5..02385b32e5 100644..100755
--- a/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
+++ b/tests/topotests/bfd-profiles-topo1/test_bfd_profiles_topo1.py
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
index c1bb7e3d15..2712e54f12 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
index c889a4c596..69305512cb 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
index 36dd97190e..3ad95c3612 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
no bgp network import-check
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
index 33041262f6..502c4c8b2f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
index 29b9f0da6c..2f7de073c3 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
index e09b505ee4..720d06dbf1 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
index c3309d8c75..b81cd33c4f 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
index 54401bfb2f..f18e5b852e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
index f742fede1a..54a0933588 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
index 91311f32c5..5289628480 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname ce4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
index a9549e8fee..5da53ae1e7 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
index cda6d9429a..e4a6b8e32c 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debugging
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
index e2a8de7db7..a861469c7a 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
index 7b267a6ee1..480f95954e 100644
--- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
log file bgpd.log debug
diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
index 2f8759f960..06bdc31f8c 100644
--- a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
+++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
@@ -1,5 +1,4 @@
log stdout notifications
-log monitor notifications
log commands
!
router bgp 1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
index b3fe5ff23d..ada354bd62 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
index fbb6a65d61..4932d63d4f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
index d61f776f3d..1a5e41aae6 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
index 626d8227e7..a38afd632f 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r1
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 1.1.1.1
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
index 524051426b..95890f25b9 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r2
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 2.2.2.2
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
index 8c75a39efa..dbeb2c4665 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r3
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 3.3.3.3
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
index 38f8758cbc..ae1787718c 100644
--- a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
@@ -3,7 +3,6 @@ frr defaults traditional
hostname r4
password zebra
log stdout notifications
-log monitor notifications
log commands
router bgp 5226
bgp router-id 4.4.4.4
diff --git a/tests/topotests/evpn_type5_test_topo1/__init__.py b/tests/topotests/evpn_type5_test_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/__init__.py
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_chaos_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
new file mode 100644
index 0000000000..14842da326
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -0,0 +1,887 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "e1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"10.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ },
+ {
+ "network":"10::1/128",
+ "next_hop":"Null0",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "e1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network":"20.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"20::1/128",
+ "next_hop":"Null0",
+ "vrf": "BLUE"
+ },
+ {
+ "network":"30.1.1.1/32",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ },
+ {
+ "network":"30::1/128",
+ "next_hop":"Null0",
+ "vrf": "GREEN"
+ }
+ ]
+ },
+ "e1": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ },
+ "d2": {
+ "ipv4":{
+ "e1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d1": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d1-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d1-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "d2": {
+ "links": {
+ "e1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1",
+ "vni": 75100
+ },
+ {
+ "name": "BLUE",
+ "id": "2",
+ "vni": 75200
+ },
+ {
+ "name": "GREEN",
+ "id": "3",
+ "vni": 75300
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "d2-link1": {
+ "deactivate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4":{
+ "d2-link1": "activate"
+ }
+ }
+ },
+ "advertise-all-vni": true
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "d1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "d2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"}
+ },
+ "vrfs":[
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "d1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "d2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "d2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE",
+ "id": "1"
+ },
+ {
+ "name": "GREEN",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
new file mode 100755
index 0000000000..e160264cad
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -0,0 +1,1048 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+
+1. In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+2. EVPN CLI output and JSON format validation.
+3. RT verification(auto)
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ create_vrf_cfg,
+ create_route_maps,
+ create_interface_in_kernel,
+ check_router_status,
+ configure_vxlan,
+ configure_brctl,
+ apply_raw_config,
+ verify_vrf_vni,
+ verify_cli_json
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_chaos_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Reading the data from JSON File for topology creation
+# Global variables
+TCPDUMP_FILE = "evpn_log.txt"
+LOGDIR = "/tmp/topotests/"
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+MULTICAST_MAC1 = "01:00:5e:00:52:02"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_verify_overlay_index_p1(request):
+ """
+ In absence of an overlay index all IP-Prefixes(RT-5)
+ are advertised with default values for below parameters:
+ --> Ethernet Tag ID = GW IP address = ESI=0
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Following steps are taken care in base config:")
+ step(
+ "Configure BGP neighborship for both address families"
+ "(IPv4 & IPv6) between Edge-1 and VFN routers(R1 and R2)"
+ )
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify: Prefixes are received in all VRFs on Edge-1 router.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that EVPN routes, received on DCG-1 and DCG-2 do not "
+ "carry any overlay index and these indexes are set to default "
+ "value=0. "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d1", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes, ethTag=0
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_cli_json_available_p1(request):
+ """
+ EVPN CLI output and JSON format validation.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Need to verify below CLIs and associated JSON format " "outputs:")
+
+ input_dict = {
+ "e1": {
+ "cli": [
+ "show evpn vni detail",
+ "show bgp l2vpn evpn all overlay",
+ "show bgp l2vpn evpn vni"
+ ]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_auto_p0(request):
+ """
+ RT verification(auto)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs "
+ "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that Edge-1 receives same prefixes in all 3 VRFs via "
+ "corresponding next-hop in associated VRF sh bgp vrf all"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 4-byte local AS number on Edge-1 and establish EVPN "
+ "neighborship with DCG-1 & DCG-2."
+ )
+
+ topo_local = deepcopy(topo)
+
+ step("Delete BGP config for vrf RED.")
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {}
+ for dut in ["e1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that all overlapping prefixes across different VRFs are "
+ "advertised in EVPN with unique RD value(auto derived)."
+ )
+ step(
+ "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto "
+ "derived RT value."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+ input_routes_2 = {
+ "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]}
+ }
+ input_routes_3 = {
+ "r2": {
+ "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+ step(
+ "Verify if DCG-2(eBGP peer) automatically imports the prefixes "
+ "from EVPN address-family to respective VRFs or not."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK4_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Change the VNI number for all 3 VRFs on Edge-1 as:"
+ "RED : 75400, GREEN: 75500, BLUE: 75600"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": 75400},
+ {"name": "BLUE", "vni": 75500},
+ {"name": "GREEN", "vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75400, 75500, 75600]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number.."
+ )
+
+ input_dict = {
+ "e1": {
+ "vrfs": [
+ {"RED": {"vni": 75400}},
+ {"BLUE": {"vni": 75500}},
+ {"GREEN": {"vni": 75600}},
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on DCG-2 that prefixes are not imported from EVPN "
+ "address-family to VRFs as RT values are different on sending("
+ "edge-1) and receiving(DCG-2) end."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Revert back to original VNI number for all 3 VRFs on Edge-1 "
+ "as: RED : 75100, GREEN: 75200, BLUE: 75300"
+ )
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": 75400},
+ {"name": "BLUE", "no_vni": 75500},
+ {"name": "GREEN", "no_vni": 75600},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {
+ "e1": {
+ "vrfs": [
+ {"name": "RED", "vni": VNI_1},
+ {"name": "BLUE", "vni": VNI_2},
+ {"name": "GREEN", "vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete configured vxlan")
+ dut = "e1"
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configured vxlan")
+ VXLAN["vxlan_id"] = [75100, 75200, 75300]
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on Edge-1 that auto derived RT value has changed for "
+ "each VRF based on VNI number."
+ )
+ step(
+ "Verify that DCG-1(iBGP peer) automatically imports the prefixes"
+ " from EVPN address-family to respective VRFs."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Test with smaller VNI numbers (1-75000)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 receives EVPN prefixes along with auto "
+ "derived RT values(based on smaller VNI numbers)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("CLI error for malformed VNI.")
+ input_dict = {
+ "e1": {
+ "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes_1 = {
+ "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]}
+ }
+
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n "
+ "Malfaromed Auto-RT value accepted: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Un-configure VNI number more than boundary limit (16777215)")
+
+ input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}}
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
new file mode 100755
index 0000000000..3cdec760f7
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -0,0 +1,2117 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation,
+# Inc. ("NetDEF") in this file.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Following tests are covered to test EVPN-Type5 functionality:
+
+1. RD verification (manual/auto).
+2. RT verification(manual)
+3. In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+4. EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+5. Route-map operations for EVPN address family.
+6. BGP attributes for EVPN address-family.
+"""
+
+import os
+import re
+import sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+from time import sleep
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topotest import version_cmp
+from lib.topogen import Topogen, get_topogen
+from mininet.topo import Topo
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ create_route_maps,
+ verify_cli_json,
+ start_router_daemons,
+ kill_router_daemons,
+ create_static_routes,
+ stop_router,
+ start_router,
+ create_vrf_cfg,
+ check_router_status,
+ apply_raw_config,
+ configure_vxlan,
+ configure_brctl,
+ verify_vrf_vni,
+ create_interface_in_kernel
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_attributes_for_evpn_routes,
+ verify_evpn_routes
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/evpn_type5_topo1.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+NETWORK1_1 = {"ipv4": "10.1.1.1/32", "ipv6": "10::1/128"}
+NETWORK1_2 = {"ipv4": "40.1.1.1/32", "ipv6": "40::1/128"}
+NETWORK1_3 = {"ipv4": "40.1.1.2/32", "ipv6": "40::2/128"}
+NETWORK1_4 = {"ipv4": "40.1.1.3/32", "ipv6": "40::3/128"}
+NETWORK2_1 = {"ipv4": "20.1.1.1/32", "ipv6": "20::1/128"}
+NETWORK3_1 = {"ipv4": "30.1.1.1/32", "ipv6": "30::1/128"}
+NETWORK4_1 = {"ipv4": "100.1.1.1/32 ", "ipv6": "100::100/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+VNI_1 = 75100
+VNI_2 = 75200
+VNI_3 = 75300
+MAC_1 = "00:80:48:ba:d1:00"
+MAC_2 = "00:80:48:ba:d1:01"
+MAC_3 = "00:80:48:ba:d1:02"
+BRCTL_1 = "br100"
+BRCTL_2 = "br200"
+BRCTL_3 = "br300"
+VXLAN_1 = "vxlan75100"
+VXLAN_2 = "vxlan75200"
+VXLAN_3 = "vxlan75300"
+BRIDGE_INTF1 = "120.0.0.1"
+BRIDGE_INTF2 = "120.0.0.2"
+BRIDGE_INTF3 = "120.0.0.3"
+
+VXLAN = {
+ "vxlan_name": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vxlan_id": [75100, 75200, 75300],
+ "dstport": 4789,
+ "local_addr": {"e1": BRIDGE_INTF1, "d1": BRIDGE_INTF2, "d2": BRIDGE_INTF3},
+ "learning": "no",
+}
+BRCTL = {
+ "brctl_name": [BRCTL_1, BRCTL_2, BRCTL_3],
+ "addvxlan": [VXLAN_1, VXLAN_2, VXLAN_3],
+ "vrf": ["RED", "BLUE", "GREEN"],
+ "stp": [0, 0, 0],
+}
+
+
+class CreateTopo(Topo):
+ """
+ Test BasicTopo - topology 1
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), '4.19') < 0:
+ error_msg = ('EVPN tests will not run (have kernel "{}", '
+ 'but it requires >= 4.19)'.format(platform.release()))
+ pytest.skip(error_msg)
+
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ ADDR_TYPES = check_address_types()
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Pre-requisite config for testsuite")
+ prerequisite_config_for_test_suite(tgen)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def prerequisite_config_for_test_suite(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Configure vxlan, bridge interface")
+ for dut in ["e1", "d1", "d2"]:
+ step("[DUT: ]Configure vxlan")
+ vxlan_input = {
+ dut: {
+ "vxlan": [
+ {
+ "vxlan_name": VXLAN["vxlan_name"],
+ "vxlan_id": VXLAN["vxlan_id"],
+ "dstport": VXLAN["dstport"],
+ "local_addr": VXLAN["local_addr"][dut],
+ "learning": VXLAN["learning"],
+ }
+ ]
+ }
+ }
+
+ result = configure_vxlan(tgen, vxlan_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure bridge interface")
+ brctl_input = {
+ dut: {
+ "brctl": [
+ {
+ "brctl_name": BRCTL["brctl_name"],
+ "addvxlan": BRCTL["addvxlan"],
+ "vrf": BRCTL["vrf"],
+ "stp": BRCTL["stp"],
+ }
+ ]
+ }
+ }
+ result = configure_brctl(tgen, topo, brctl_input)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes")
+ add_default_routes(tgen)
+
+
+def add_default_routes(tgen):
+ """
+ API to do prerequisite config for testsuite
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ """
+
+ step("Add default routes..")
+
+ default_routes = {
+ "e1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["d1"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["d2"]["links"]["e1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d1": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d2"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d1-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ "d2": {
+ "static_routes": [
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["d1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ {
+ "network": "{}/32".format(VXLAN["local_addr"]["e1"]),
+ "next_hop": topo["routers"]["e1"]["links"]["d2-link1"][
+ "ipv4"
+ ].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, default_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+
+def test_RD_verification_manual_and_auto_p0(request):
+ """
+ RD verification (manual/auto).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step(
+ "Advertise vrf RED's routes in EVPN address family from Edge-1 router"
+ ", without manual configuration of RD."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify on DCG-1 and DCG-2:")
+ step("EVPN route for 10.1.1.1/32 has auto-assigned RD value.")
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="auto", rd_peer="e1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 50.50.50.50:50 and "
+ "advertise vrf RED's routes in EVPN address family from "
+ "Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {"l2vpn": {"evpn": {"rd": "50.50.50.50:50"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("EVPN route for vrf RED has RD value as 50.50.50.50:50")
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="50.50.50.50:50"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RD for vrf RED manually as 100.100.100.100:100 and "
+ "advertise vrf RED's routes in EVPN address family from Edge-1 "
+ "router."
+ )
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for vrf RED is overridden with RD value as " "100.100.100.100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rd="100.100.100.100:100"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RD for vrf BLUE manually same as vrf RED "
+ "(100.100.100.100:100) and advertise vrf RED and BLUE's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {"evpn": {"rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Delete manually configured RD and advertise vrf RED's routes "
+ "in EVPN address family from Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {"evpn": {"no rd": "100.100.100.100:100"}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure same RD value for vrf GREEN, as auto generated RD "
+ "value for vrf RED on Edge-1 router."
+ )
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete auto configured RD value from vrf RED in EVPN " "address family.")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"no rd": "10.0.0.33:1"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure RD value as 100.100.100:100")
+
+ input_dict_rd = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {"l2vpn": {"evpn": {"rd": "100.100.100:100"}}},
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rd)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_RT_verification_manual_p0(request):
+ """
+ RT verification(manual)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+ step("Advertise VRF routes as in EVPN address family from Edge-1 " "router.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 100:100 "
+ "and advertise vrf RED's routes in EVPN address family"
+ " from Edge-1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"e1": {}}}}}
+ },
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "100:100"}]}}
+ },
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 100:100."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure RT for vrf RED manually as export 500:500 and"
+ " advertise vrf RED's routes in EVPN address family from"
+ " e1 router."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {"route-target": {"export": [{"value": "500:500"}]}}
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on dcg-1 and dcg-2, EVPN route for 10.1.1.1/32"
+ " and 10::1/128 have RT value as 500:500."
+ )
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Import RT value 100:100 and 500:500 in vrf BLUE manually on"
+ " peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [
+ {"value": "100:100"},
+ {"value": "500:500"},
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be installed "
+ "in vrf BLUE on DCG-1 and DCG-2 and further advertised to "
+ "VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Delete import RT value 500:500 in vrf BLUE manually on "
+ "peer router DCG-1 and DCG-2."
+ )
+
+ input_dict_rt = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "import": [{"value": "500:500", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete RT export value 100:100 for vrf RED on Edge-1")
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [{"value": "100:100", "delete": True}]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "EVPN route for 10.1.1.1/32 and 10::1 should be withdrawn "
+ "from vrf BLUE on DCG-1,DCG-2 and VNF router."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "d1", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: Routes are still "
+ "present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Configure RT value as 100:100000010000010000101010 to check "
+ "the boundary value."
+ )
+
+ input_dict_rt = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "route-target": {
+ "export": [
+ {"value": "100:100000010000010000101010"}
+ ]
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rt)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "CLI error: RT value: 100:100000010000010000101010 should not " "be configured"
+ )
+
+ dut = "e1"
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_attributes_for_evpn_routes(
+ tgen, topo, dut, input_routes, rt="100:100000010000010000101010", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behavior: RT value of out"
+ " of boundary \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_active_standby_evpn_implementation_p1(request):
+ """
+ In an active/standby EVPN implementation, if active DCG goes down,
+ secondary takes over.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Taken care in base config: Configure BGP neighborship for both "
+ "address families(IPv4 & IPv6) between DCG-1/DCG-2 and VFN routers"
+ "(R3 and R4)."
+ )
+
+ step(
+ "BGP neighborships come up within defined VRFs. Please use below "
+ "command: sh bgp vrf all summary"
+ )
+
+ result = verify_bgp_convergence(tgen, topo, "d1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo, "d2")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Advertise prefixes from VNF routers R3 and R4 in associated "
+ "VRFs for both address-families."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Prefixes are received in respective VRFs on DCG-1/DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "d1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VRF routes in EVPN "
+ "address-family from DCG-1 and DCG-2 router."
+ )
+
+ step("Verify on Edge-1 that EVPN routes are installed via next-hop " "as DCG-2.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ if addr_type == "ipv4":
+ result = verify_rib(
+ tgen, addr_type, "e1", input_routes, next_hop=BRIDGE_INTF2
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure 'next-hop self' on DCG-1 for peer Edge-1 in EVPN " "address-family."
+ )
+
+ input_dict_3 = {
+ "d1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {"d1-link1": {"next_hop_self": True}}
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "Creating route-map so ipv6 glpbal ip wpuld be preferred " "as next-hop"
+ )
+
+ step(
+ "Verify on Edge-1 that EVPN routes are now preferred via "
+ "next-hop as DCG-1(iBGP) due to shortest AS-Path."
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ logger.info("Verifying only ipv4 routes")
+ if addr_type != "ipv4":
+ continue
+
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ next_hop = topo["routers"]["d1"]["links"]["e1-link1"]["ipv4"].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes, next_hop=next_hop)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_routes_from_VNFs_p1(request):
+ """
+ EVPN routes are advertised/withdrawn, based on VNFs
+ advertising/withdrawing IP prefixes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taken care in base config: Advertise VNFs'(R1 and R2) "
+ "originated routes in EVPN address-family from Edge-1 to "
+ "DCG-1 and DCG-2 routers."
+ )
+ step(
+ "Taken care in base config: Advertise IPv4 and IPv6 routes "
+ "from default vrf in EVPN address-family from Edge-1."
+ )
+
+ step(
+ "Verify on DCG-2 that VNF routes are received in respective "
+ "VRFs along with auto derived RD/RT values 'show bgp l2vpn evpn'"
+ )
+ for dut in ["d1", "d2"]:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, dut, input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R3 and R4 that DCG-2 further advertises all EVPN "
+ "routes to corresponding VRFs."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes associated to default "
+ "VRF and install in default IP routing table as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw the IP prefixes from VFN(R1).")
+ dut = "r1"
+ input_dict_2 = {}
+ static_routes = topo["routers"][dut]["static_routes"]
+ for static_route in static_routes:
+ static_route["delete"] = True
+ temp = {dut: {"static_routes": [static_route]}}
+ input_dict_2.update(temp)
+
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to vrf RED and "
+ "send an withdraw to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Re-advertise IP prefixes from VFN(R1).")
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to vrf RED "
+ "again and send an update to VNF(R3) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_rib(tgen, addr_type, "r3", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete vrf BLUE from router Edge-1")
+ input_dict_3 = {"e1": {"vrfs": [{"name": "BLUE", "id": "2", "delete": True}]}}
+
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that DCG-2 removes EVPN routes corresponding to "
+ "vrf BLUE and send an withdraw to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Add vrf BLUE on router Edge-1 again.")
+ interface = topo["routers"]["e1"]["links"]["r2-link1"]["interface"]
+ input_dict_3 = {
+ "e1": {
+ "links": {
+ "r2-link1": {
+ "interface": interface,
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "BLUE",
+ }
+ },
+ "vrfs": [{"name": "BLUE", "id": "2"}],
+ }
+ }
+ result = create_vrf_cfg(tgen, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "After deleting VRFs ipv6 addresses wil be deleted "
+ "from kernel Adding back ipv6 addresses"
+ )
+ dut = "e1"
+ vrfs = ["BLUE"]
+
+ for vrf in vrfs:
+ for c_link, c_data in topo["routers"][dut]["links"].items():
+ if "vrf" in c_data:
+ if c_data["vrf"] != vrf:
+ continue
+
+ intf_name = c_data["interface"]
+ intf_ipv6 = c_data["ipv6"]
+
+ create_interface_in_kernel(
+ tgen, dut, intf_name, intf_ipv6, vrf, create=False
+ )
+
+ logger.info("Wait for 60 sec.")
+ sleep(60)
+
+ step(
+ "Verify that DCG-2 receives EVPN routes corresponding to "
+ "vrf BLUE again and send an update to VNF(R4) as well."
+ )
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK2_1[addr_type], "vrf": "BLUE"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Withdraw IPv6 address-family in EVPN advertisements for " "VRF GREEN")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "advertise": {addr_type: {"unicast": {"delete": True}}}
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that EVPN routes (IPv6)associated with vrf GREEN are "
+ "withdrawn from DCG-2 and VNF R4."
+ )
+ input_routes = {
+ "r2": {"static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]}
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes, expected=False)
+ assert result is not True, "Testcase {} :Failed \n "
+ "Routes are still present: {}".format(tc_name, result)
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("Advertise IPv6 address-family in EVPN advertisements " "for VRF GREEN.")
+ addr_type = "ipv6"
+ input_dict_4 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "l2vpn": {"evpn": {"advertise": {addr_type: {"unicast": {}}}}}
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r2": {
+ "static_routes": [{"network": NETWORK3_1[addr_type], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize(
+ "attribute", [{"route-type": "prefix"}, {"vni": VNI_1}, {"rt": "300:300"}]
+)
+def test_route_map_operations_for_evpn_address_family_p1(request, attribute):
+ """
+ Route-map operations for EVPN address family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise VRF routes in EVPN address family from Edge-1 router."
+ " Configure a route-map on e1 to filter EVPN routes based on"
+ " below keywords: route-type: prefix"
+ )
+
+ for key, value in attribute.items():
+ if key == "rt":
+ logger.info("Creating extcommunity using raw_config")
+ raw_config = {
+ "d2": {
+ "raw_config": [
+ "bgp extcommunity-list standard ECOMM300 permit {} {}".format(
+ key, value
+ )
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "set": {"extcommunity": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"extcommunity": "ECOMM300"}}
+ ]
+ }
+ },
+ }
+
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ "d2": {
+ "route_maps": {
+ "rmap_route_type": [
+ {"action": "permit", "match": {"evpn": {key: value}}}
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ "d2": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "e1": {
+ "ipv4": {
+ "d2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_route_type",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router DCG-2 that EVPN routes corresponding to all "
+ "VRFs are received. As all EVPN routes are type-5 only."
+ )
+
+ input_routes = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_routes = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_evpn_routes(tgen, topo, "d2", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("attribute", ["locPrf", "weight", "path"])
+def test_bgp_attributes_for_evpn_address_family_p1(request, attribute):
+ """
+ BGP attributes for EVPN address-family.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ add_default_routes(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise prefixes from VNF routers R1 and R2 in associated "
+ "VRFs for both address-family."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK3_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ topo_local = deepcopy(topo)
+
+ logger.info("Modifying topology b/w e1 and d1 from iBGP to eBGP")
+ step("Delete BGP config for vrf RED.")
+
+ if attribute == "locPrf":
+ input_dict_vni = {
+ "d1": {
+ "vrfs": [
+ {"name": "RED", "no_vni": VNI_1},
+ {"name": "BLUE", "no_vni": VNI_2},
+ {"name": "GREEN", "no_vni": VNI_3},
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {}
+ for dut in ["d1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ INDEX = [0, 1, 2, 3]
+ VRFS = ["RED", "BLUE", "GREEN", None]
+ AS_NUM = [100, 100, 100, 100]
+
+ for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM):
+ topo_local["routers"][dut]["bgp"][index]["local_as"] = 200
+ if vrf:
+ temp[dut]["bgp"].append(
+ {"local_as": as_num, "vrf": vrf, "delete": True}
+ )
+ else:
+ temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = create_router_bgp(tgen, topo_local["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Advertise VRF routes in EVPN address-family from DCG-1 " "and DCG-2 routers.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Redistribute static in (IPv4 and IPv6) address-family "
+ "on Edge-1 for all VRFs."
+ )
+
+ input_dict_2 = {}
+ for dut in ["r3", "r4"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_2.update(temp)
+
+ if dut == "r3":
+ VRFS = ["RED"]
+ AS_NUM = [3]
+ if dut == "r4":
+ VRFS = ["BLUE", "GREEN"]
+ AS_NUM = [4, 4]
+
+ for vrf, as_num in zip(VRFS, AS_NUM):
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes corresponding to "
+ "all VRFs are received from both routers DCG-1 and DCG-2"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map on Edge-1 to modify below BGP attributes "
+ "for EVPN address-family:"
+ )
+
+ if attribute == "path":
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {
+ "as_num": "123 231 321",
+ "as_action": "prepend",
+ }
+ },
+ }
+ ],
+ "rmap_d2".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {"as_num": "121", "as_action": "prepend"}
+ },
+ }
+ ],
+ }
+ }
+ }
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1".format(addr_type): [
+ {"action": "permit", "set": {attribute: 120}}
+ ],
+ "rmap_d2".format(addr_type): [
+ {"action": "permit", "set": {attribute: 150}}
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "e1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ "l2vpn": {
+ "evpn": {
+ "neighbor": {
+ "d1": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d1",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "d2": {
+ "ipv4": {
+ "e1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_d2",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on router Edge-1 that EVPN routes are preferred via"
+ " DCG-1 or DCG-2 based on best path selection criteria "
+ "(according to the configured BGP attribute values in route-map)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK1_2[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK1_3[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE",
+ },
+ {
+ "network": NETWORK1_4[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "GREEN",
+ },
+ ]
+ },
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, "e1", input_routes, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
index 971bbd0f3b..7b1eead944 100644
--- a/tests/topotests/lib/bgp.py
+++ b/tests/topotests/lib/bgp.py
@@ -27,6 +27,8 @@ import sys
from lib import topotest
from lib.topolog import logger
+from lib.topogen import TopoRouter, get_topogen
+
# Import common_config to use commomnly used APIs
from lib.common_config import (
create_common_configuration,
@@ -166,6 +168,7 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
ipv4_data = bgp_addr_data.setdefault("ipv4", {})
ipv6_data = bgp_addr_data.setdefault("ipv6", {})
+ l2vpn_data = bgp_addr_data.setdefault("l2vpn", {})
neigh_unicast = (
True
@@ -174,6 +177,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
else False
)
+ l2vpn_evpn = True if l2vpn_data.setdefault("evpn", {}) else False
+
if neigh_unicast:
data_all_bgp = __create_bgp_unicast_neighbor(
tgen,
@@ -184,6 +189,11 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True
config_data=data_all_bgp,
)
+ if l2vpn_evpn:
+ data_all_bgp = __create_l2vpn_evpn_address_family(
+ tgen, topo, bgp_data, router, config_data=data_all_bgp
+ )
+
try:
result = create_common_configuration(
tgen, router, data_all_bgp, "bgp", build, load_config
@@ -467,6 +477,166 @@ def __create_bgp_unicast_neighbor(
return config_data
+def __create_l2vpn_evpn_address_family(
+ tgen, topo, input_dict, router, config_data=None
+):
+ """
+ Helper API to create configuration for l2vpn evpn address-family
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring
+ from testcase
+ * `router` : router id to be configured.
+ * `build` : Only for initial setup phase this is set as True.
+ """
+
+ result = False
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ bgp_data = input_dict["address_family"]
+
+ for family_type, family_dict in bgp_data.iteritems():
+ if family_type != "l2vpn":
+ continue
+
+ family_data = family_dict["evpn"]
+ if family_data:
+ config_data.append("address-family l2vpn evpn")
+
+ advertise_data = family_data.setdefault("advertise", {})
+ neighbor_data = family_data.setdefault("neighbor", {})
+ advertise_all_vni_data = family_data.setdefault("advertise-all-vni", None)
+ rd_data = family_data.setdefault("rd", None)
+ no_rd_data = family_data.setdefault("no rd", False)
+ route_target_data = family_data.setdefault("route-target", {})
+
+ if advertise_data:
+ for address_type, unicast_type in advertise_data.items():
+
+ if isinstance(unicast_type, dict):
+ for key, value in unicast_type.items():
+ cmd = "advertise {} {}".format(address_type, key)
+
+ if value:
+ route_map = value.setdefault("route-map", {})
+ advertise_del_action = value.setdefault("delete", None)
+
+ if route_map:
+ cmd = "{} route-map {}".format(cmd, route_map)
+
+ if advertise_del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ if neighbor_data:
+ for neighbor, neighbor_data in neighbor_data.items():
+ ipv4_neighbor = neighbor_data.setdefault("ipv4", {})
+ ipv6_neighbor = neighbor_data.setdefault("ipv6", {})
+
+ if ipv4_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv4"
+ ].split("/")[0]
+
+ if isinstance(action, dict):
+ next_hop_self = action.setdefault("next_hop_self", None)
+ route_maps = action.setdefault("route_maps", {})
+
+ if next_hop_self is not None:
+ if next_hop_self is True:
+ config_data.append(
+ "neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+ elif next_hop_self is False:
+ config_data.append(
+ "no neighbor {} "
+ "next-hop-self".format(neighbor_ip)
+ )
+
+ if route_maps:
+ for route_map in route_maps:
+ name = route_map.setdefault("name", {})
+ direction = route_map.setdefault("direction", "in")
+ del_action = route_map.setdefault("delete", False)
+
+ if not name:
+ logger.info(
+ "Router %s: 'name' "
+ "not present in "
+ "input_dict for BGP "
+ "neighbor route name",
+ router,
+ )
+ else:
+ cmd = "neighbor {} route-map {} " "{}".format(
+ neighbor_ip, name, direction
+ )
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ else:
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if ipv6_neighbor:
+ for neighbor_name, action in ipv4_neighbor.items():
+ neighbor_ip = topo[neighbor]["links"][neighbor_name][
+ "ipv6"
+ ].split("/")[0]
+ if action == "activate":
+ cmd = "neighbor {} activate".format(neighbor_ip)
+ elif action == "deactivate":
+ cmd = "no neighbor {} activate".format(neighbor_ip)
+
+ config_data.append(cmd)
+
+ if advertise_all_vni_data == True:
+ cmd = "advertise-all-vni"
+ config_data.append(cmd)
+ elif advertise_all_vni_data == False:
+ cmd = "no advertise-all-vni"
+ config_data.append(cmd)
+
+ if rd_data:
+ cmd = "rd {}".format(rd_data)
+ config_data.append(cmd)
+
+ if no_rd_data:
+ cmd = "no rd {}".format(no_rd_data)
+ config_data.append(cmd)
+
+ if route_target_data:
+ for rt_type, rt_dict in route_target_data.items():
+ for _rt_dict in rt_dict:
+ rt_value = _rt_dict.setdefault("value", None)
+ del_rt = _rt_dict.setdefault("delete", None)
+
+ if rt_value:
+ cmd = "route-target {} {}".format(rt_type, rt_value)
+ if del_rt:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return config_data
+
+
def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
"""
Helper API to create neighbor specific configuration
@@ -489,7 +659,7 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
for dest_link, peer in peer_dict["dest_link"].iteritems():
nh_details = topo[name]
- if "vrfs" in topo[router]:
+ if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
remote_as = nh_details["bgp"][0]["local_as"]
else:
remote_as = nh_details["bgp"]["local_as"]
@@ -887,19 +1057,16 @@ def verify_bgp_convergence(tgen, topo, dut=None):
API will verify if BGP is converged with in the given time frame.
Running "show bgp summary json" command and verify bgp neighbor
state is established,
-
Parameters
----------
* `tgen`: topogen object
* `topo`: input json file data
* `dut`: device under test
-
Usage
-----
# To veriry is BGP is converged for all the routers used in
topology
results = verify_bgp_convergence(tgen, topo, dut="r1")
-
Returns
-------
errormsg(str) or True
@@ -3463,3 +3630,552 @@ def verify_gr_address_family(tgen, topo, addr_type, addr_family, dut):
return errormsg
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_attributes_for_evpn_routes(
+ tgen,
+ topo,
+ dut,
+ input_dict,
+ rd=None,
+ rt=None,
+ ethTag=None,
+ ipLen=None,
+ rd_peer=None,
+ rt_peer=None,
+):
+ """
+ API to verify rd and rt value using "sh bgp l2vpn evpn 10.1.1.1"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `rd` : route distinguisher
+ * `rt` : route target
+ * `ethTag` : Ethernet Tag
+ * `ipLen` : IP prefix length
+ * `rd_peer` : Peer name from which RD will be auto-generated
+ * `rt_peer` : Peer name from which RT will be auto-generated
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+
+ result = verify_attributes_for_evpn_routes(tgen, topo,
+ input_dict, rd = "10.0.0.33:1")
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if "vrf" in static_route:
+ vrf = static_route["vrf"]
+
+ if type(network) is not list:
+ network = [network]
+
+ for route in network:
+ route = route.split("/")[0]
+ _addr_type = validate_ip_address(route)
+ if "v4" in _addr_type:
+ input_afi = "v4"
+ elif "v6" in _addr_type:
+ input_afi = "v6"
+
+ cmd = "show bgp l2vpn evpn {} json".format(route)
+ evpn_rd_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_rd_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if rd is not None and rd != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rd == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rd value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rd_peer:
+ index = 1
+ vni_dict = {}
+
+ rnode = tgen.routers()[rd_peer]
+ vrfs = topo["routers"][rd_peer]["vrfs"]
+ for vrf_dict in vrfs:
+ vni_dict[vrf_dict["name"]] = index
+ index += 1
+
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ router_id = afi_data["routerId"]
+
+ rd = "{}:{}".format(router_id, vni_dict[vrf])
+ if rd in evpn_rd_value_json:
+ rd_value_json = evpn_rd_value_json[rd]
+ if rd_value_json["rd"] != rd:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ "[FAILED]!!, EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rd, rd_value_json["rd"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rd,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD : %s is not present"
+ " in cli json output" % (dut, rd)
+ )
+ return errormsg
+
+ if rt == "auto":
+ logger.info(
+ "[DUT: %s]: Verifying auto-rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rt_peer:
+ vni_dict = {}
+
+ rnode = tgen.routers()[rt_peer]
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp vrf all summary json", isjson=True
+ )
+
+ # Verifying output dictionary show_bgp_json is empty
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ show_bgp_json_vrf = show_bgp_json[vrf]
+ for afi, afi_data in show_bgp_json_vrf.items():
+ if input_afi not in afi:
+ continue
+ as_num = afi_data["as"]
+
+ show_vrf_vni_json = run_frr_cmd(
+ rnode, "show vrf vni json", isjson=True
+ )
+
+ vrfs = show_vrf_vni_json["vrfs"]
+ for vrf_dict in vrfs:
+ if vrf_dict["vrf"] == vrf:
+ vni_dict[vrf_dict["vrf"]] = str(vrf_dict["vni"])
+
+ # If AS is 4 byte, FRR uses only the lower 2 bytes of ASN+VNI
+ # for auto derived RT value.
+ if as_num > 65535:
+ as_bin = bin(as_num)
+ as_bin = as_bin[-16:]
+ as_num = int(as_bin, 2)
+
+ rt = "{}:{}".format(str(as_num), vni_dict[vrf])
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ if vni_dict[vrf] == rt_data["VNI"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " Verifying RT "
+ "value for EVPN "
+ " route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "RT value for EVPN "
+ "route: %s [PASSED]||"
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if rt is not None and rt != "auto":
+ logger.info(
+ "[DUT: %s]: Verifying rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if type(rt) is not list:
+ rt = [rt]
+
+ for _rt in rt:
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ for rt_data in route_data["paths"]:
+ rt_string = rt_data["extendedCommunity"][
+ "string"
+ ]
+ rt_input = "RT:{}".format(_rt)
+ if rt_input not in rt_string:
+ errormsg = (
+ "[DUT: %s] Failed: "
+ "Verifying RT value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, route, rt_input, rt_string)
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying RT"
+ " value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ route,
+ rt_input,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Route : %s is not"
+ " present in cli json output" % (dut, route)
+ )
+ return errormsg
+
+ if ethTag is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ethTag value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ethTag"] != ethTag:
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ethTag value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ _rd,
+ route,
+ ethTag,
+ route_data["ethTag"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ethTag value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ethTag,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output" % (dut, _rd, route)
+ )
+ return errormsg
+
+ if ipLen is not None:
+ logger.info(
+ "[DUT: %s]: Verifying ipLen value for " "evpn route :", dut
+ )
+
+ for _rd, route_data in evpn_rd_value_json.items():
+ if route_data["ip"] == route:
+ if route_data["ipLen"] != int(ipLen):
+ errormsg = (
+ "[DUT: %s] RD: %s, Failed: "
+ "Verifying ipLen value "
+ "for EVPN route: %s"
+ "[FAILED]!!,"
+ " EXPECTED : %s "
+ " FOUND : %s"
+ % (dut, _rd, route, ipLen, route_data["ipLen"])
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: RD: %s, Verifying "
+ "ipLen value for EVPN route:"
+ " %s [PASSED]|| "
+ "Found Exprected: %s",
+ dut,
+ _rd,
+ route,
+ ipLen,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output " % (dut, route)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2)
+def verify_evpn_routes(
+ tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None
+):
+ """
+ API to verify evpn routes using "sh bgp l2vpn evpn"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `input_dict`: having details like - for which route, rd value
+ needs to be verified
+ * `route_type` : Route type 5 is supported as of now
+ * `EthTag` : Ethernet tag, by-default is 0
+ * `next_hop` : Prefered nexthop for the evpn routes
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED"
+ }]
+ }
+ }
+ result = verify_evpn_routes(tgen, topo, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn routes: ", dut)
+
+ if "static_routes" in input_dict[router]:
+ for static_route in input_dict[router]["static_routes"]:
+ network = static_route["network"]
+
+ if type(network) is not list:
+ network = [network]
+
+ missing_routes = {}
+ for route in network:
+ rd_keys = 0
+ ip_len = route.split("/")[1]
+ route = route.split("/")[0]
+
+ prefix = "[{}]:[{}]:[{}]:[{}]".format(
+ routeType, EthTag, ip_len, route
+ )
+
+ cmd = "show bgp l2vpn evpn route json"
+ evpn_value_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if not bool(evpn_value_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if evpn_value_json["numPrefix"] == 0:
+ errormsg = "[DUT: %s]: No EVPN prefixes exist" % (dut)
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ rd_keys += 1
+ if prefix not in route_data_json:
+ missing_routes[key] = prefix
+
+ if rd_keys == len(missing_routes.keys()):
+ errormsg = (
+ "[DUT: %s]: "
+ "Missing EVPN routes: "
+ "%s [FAILED]!!" % (dut, list(set(missing_routes.values())))
+ )
+ return errormsg
+
+ for key, route_data_json in evpn_value_json.items():
+ if isinstance(route_data_json, dict):
+ if prefix not in route_data_json:
+ continue
+
+ for paths in route_data_json[prefix]["paths"]:
+ for path in paths:
+ if path["routeType"] != routeType:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying routeType "
+ "for EVPN route: %s "
+ "[FAILED]!! "
+ "Expected: %s, "
+ "Found: %s"
+ % (
+ dut,
+ prefix,
+ routeType,
+ path["routeType"],
+ )
+ )
+ return errormsg
+
+ elif next_hop:
+ for nh_dict in path["nexthops"]:
+ if nh_dict["ip"] != next_hop:
+ errormsg = (
+ "[DUT: %s]: "
+ "Verifying "
+ "nexthop for "
+ "EVPN route: %s"
+ "[FAILED]!! "
+ "Expected: %s,"
+ " Found: %s"
+ % (
+ dut,
+ prefix,
+ next_hop,
+ nh_dict["ip"],
+ )
+ )
+ return errormsg
+
+ else:
+ logger.info(
+ "[DUT %s]: Verifying "
+ "EVPN route : %s, "
+ "routeType: %s is "
+ "installed "
+ "[PASSED]|| ",
+ dut,
+ prefix,
+ routeType,
+ )
+ return True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return False
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index d72d0aa223..156a5f7ea4 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -933,6 +933,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
)
rnode.run(cmd)
+ if vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "vni {}".format(vni)
+ config_data.append(cmd)
+
+ if del_vni:
+ config_data.append("vrf {}".format(vrf["name"]))
+ cmd = "no vni {}".format(del_vni)
+ config_data.append(cmd)
+
result = create_common_configuration(
tgen, c_router, config_data, "vrf", build=build
)
@@ -984,6 +994,34 @@ def create_interface_in_kernel(
rnode.run(cmd)
+def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False):
+ """
+ Cretae interfaces in kernel for ipv4/ipv6
+ Config is done in Linux Kernel:
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut` : Device for which interfaces to be added
+ * `intf_name` : interface name
+ * `ifaceaction` : False to shutdown and True to bringup the
+ ineterface
+ """
+
+ rnode = tgen.routers()[dut]
+
+ cmd = "ip link set dev"
+ if ifaceaction:
+ action = "up"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+ else:
+ action = "down"
+ cmd = "{} {} {}".format(cmd, intf_name, action)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+
def validate_ip_address(ip_address):
"""
Validates the type of ip address
@@ -1042,7 +1080,7 @@ def check_address_types(addr_type=None):
return addr_types
if addr_type not in addr_types:
- logger.error(
+ logger.debug(
"{} not in supported/configured address types {}".format(
addr_type, addr_types
)
@@ -1732,6 +1770,7 @@ def create_route_maps(tgen, input_dict, build=False):
set_action = set_data.setdefault("set_action", None)
nexthop = set_data.setdefault("nexthop", None)
origin = set_data.setdefault("origin", None)
+ ext_comm_list = set_data.setdefault("extcommunity", {})
# Local Preference
if local_preference:
@@ -1796,6 +1835,19 @@ def create_route_maps(tgen, input_dict, build=False):
logger.error("In large_comm_list 'id' not" " provided")
return False
+ if ext_comm_list:
+ rt = ext_comm_list.setdefault("rt", None)
+ del_comm = ext_comm_list.setdefault("delete", None)
+ if rt:
+ cmd = "set extcommunity rt {}".format(rt)
+ if del_comm:
+ cmd = "{} delete".format(cmd)
+
+ rmap_data.append(cmd)
+ else:
+ logger.debug("In ext_comm_list 'rt' not" " provided")
+ return False
+
# Weight
if weight:
rmap_data.append("set weight {}".format(weight))
@@ -2151,6 +2203,243 @@ def addKernelRoute(
return True
+def configure_vxlan(tgen, input_dict):
+ """
+ Add and configure vxlan
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for vxlan config
+
+ Usage:
+ ------
+ input_dict= {
+ "dcg2":{
+ "vxlan":[{
+ "vxlan_name": "vxlan75100",
+ "vxlan_id": "75100",
+ "dstport": 4789,
+ "local_addr": "120.0.0.1",
+ "learning": "no",
+ "delete": True
+ }]
+ }
+ }
+
+ configure_vxlan(tgen, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "vxlan" in input_dict[dut]:
+ for vxlan_dict in input_dict[dut]["vxlan"]:
+ cmd = "ip link "
+
+ del_vxlan = vxlan_dict.setdefault("delete", None)
+ vxlan_names = vxlan_dict.setdefault("vxlan_name", [])
+ vxlan_ids = vxlan_dict.setdefault("vxlan_id", [])
+ dstport = vxlan_dict.setdefault("dstport", None)
+ local_addr = vxlan_dict.setdefault("local_addr", None)
+ learning = vxlan_dict.setdefault("learning", None)
+
+ config_data = []
+ if vxlan_names and vxlan_ids:
+ for vxlan_name, vxlan_id in zip(vxlan_names, vxlan_ids):
+ cmd = "ip link"
+
+ if del_vxlan:
+ cmd = "{} del {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+ else:
+ cmd = "{} add {} type vxlan id {}".format(
+ cmd, vxlan_name, vxlan_id
+ )
+
+ if dstport:
+ cmd = "{} dstport {}".format(cmd, dstport)
+
+ if local_addr:
+ ip_cmd = "ip addr add {} dev {}".format(
+ local_addr, vxlan_name
+ )
+ if del_vxlan:
+ ip_cmd = "ip addr del {} dev {}".format(
+ local_addr, vxlan_name
+ )
+
+ config_data.append(ip_cmd)
+
+ cmd = "{} local {}".format(cmd, local_addr)
+
+ if learning == "no":
+ cmd = "{} {} learning".format(cmd, learning)
+
+ elif learning == "yes":
+ cmd = "{} learning".format(cmd)
+
+ config_data.append(cmd)
+
+ try:
+ for _cmd in config_data:
+ logger.info("[DUT: %s]: Running command: %s", dut, _cmd)
+ rnode.run(_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def configure_brctl(tgen, topo, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for brctl config
+
+ Usage:
+ ------
+ input_dict= {
+ dut:{
+ "brctl": [{
+ "brctl_name": "br100",
+ "addvxlan": "vxlan75100",
+ "vrf": "RED",
+ "stp": "off"
+ }]
+ }
+ }
+
+ configure_brctl(tgen, topo, input_dict)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ if "brctl" in input_dict[dut]:
+ for brctl_dict in input_dict[dut]["brctl"]:
+
+ brctl_names = brctl_dict.setdefault("brctl_name", [])
+ addvxlans = brctl_dict.setdefault("addvxlan", [])
+ stp_values = brctl_dict.setdefault("stp", [])
+ vrfs = brctl_dict.setdefault("vrf", [])
+
+ ip_cmd = "ip link set"
+ for brctl_name, vxlan, vrf, stp in zip(
+ brctl_names, addvxlans, vrfs, stp_values
+ ):
+
+ ip_cmd_list = []
+ cmd = "ip link add name {} type bridge stp_state {}".format(brctl_name, stp)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, brctl_name))
+
+ if vxlan:
+ cmd = "{} dev {} master {}".format(ip_cmd, vxlan, brctl_name)
+
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ ip_cmd_list.append("{} up dev {}".format(ip_cmd, vxlan))
+
+ if vrf:
+ ip_cmd_list.append(
+ "{} dev {} master {}".format(ip_cmd, brctl_name, vrf)
+ )
+
+ for intf_name, data in topo["routers"][dut]["links"].items():
+ if "vrf" not in data:
+ continue
+
+ if data["vrf"] == vrf:
+ ip_cmd_list.append(
+ "{} up dev {}".format(ip_cmd, data["interface"])
+ )
+
+ try:
+ for _ip_cmd in ip_cmd_list:
+ logger.info("[DUT: %s]: Running command: %s", dut, _ip_cmd)
+ rnode.run(_ip_cmd)
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def configure_interface_mac(tgen, input_dict):
+ """
+ Add and configure brctl
+
+ * `tgen`: tgen onject
+ * `input_dict` : data for mac config
+
+ input_mac= {
+ "edge1":{
+ "br75100": "00:80:48:BA:d1:00,
+ "br75200": "00:80:48:BA:d1:00
+ }
+ }
+
+ configure_interface_mac(tgen, input_mac)
+
+ Returns:
+ -------
+ True or errormsg
+
+ """
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for intf, mac in input_dict[dut].items():
+ cmd = "ifconfig {} hw ether {}".format(intf, mac)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+
+ try:
+ result = rnode.run(cmd)
+ if len(result) != 0:
+ return result
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ return True
+
+
#############################################
# Verification APIs
#############################################
@@ -2875,3 +3164,283 @@ def verify_create_community_list(tgen, input_dict):
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
return True
+
+
+def verify_cli_json(tgen, input_dict):
+ """
+ API to verify if JSON is available for clis
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: CLIs for which JSON needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "cli": ["show evpn vni detail", show evpn rmac vni all]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ for cli in input_dict[dut]["cli"]:
+ logger.info(
+ "[DUT: %s]: Verifying JSON is available for " "CLI %s :", dut, cli
+ )
+
+ test_cli = "{} json".format(cli)
+ ret_json = rnode.vtysh_cmd(test_cli, isjson=True)
+ if not bool(ret_json):
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ elif "unknown" in ret_json or "Unknown" in ret_json:
+ errormsg = "CLI: %s, JSON format is not available" % (cli)
+ return errormsg
+ else:
+ logger.info(
+ "CLI : %s JSON format is available: " "\n %s", cli, ret_json
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_evpn_vni(tgen, input_dict):
+ """
+ API to verify evpn vni details using "show evpn vni detail json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vni": [
+ {
+ "75100":{
+ "vrf": "RED",
+ "vxlanIntf": "vxlan75100",
+ "localVtepIp": "120.1.1.1",
+ "sviIntf": "br100"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_evpn_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying evpn vni details :", dut)
+
+ cmd = "show evpn vni detail json"
+ evpn_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(evpn_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vni" in input_dict[dut]:
+ for vni_dict in input_dict[dut]["vni"]:
+ found = False
+ vni = vni_dict["name"]
+ for evpn_vni_json in evpn_all_vni_json:
+ if "vni" in evpn_vni_json:
+ if evpn_vni_json["vni"] != int(vni):
+ continue
+
+ for attribute in vni_dict.keys():
+ if vni_dict[attribute] != evpn_vni_json[attribute]:
+ errormsg = (
+ "[DUT: %s] Verifying "
+ "%s for VNI: %s [FAILED]||"
+ ", EXPECTED : %s "
+ " FOUND : %s"
+ % (
+ dut,
+ attribute,
+ vni,
+ vni_dict[attribute],
+ evpn_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ found = True
+ logger.info(
+ "[DUT: %s] Verifying"
+ " %s for VNI: %s , "
+ "Found Expected : %s ",
+ dut,
+ attribute,
+ vni,
+ evpn_vni_json[attribute],
+ )
+
+ if evpn_vni_json["state"] != "Up":
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " State for VNI: %s is not Up" % (dut, vni)
+ )
+ return errormsg
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:"
+ " VNI: %s is not present in JSON" % (dut, vni)
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s]: Verifying VNI : %s "
+ "details and state is Up [PASSED]!!",
+ dut,
+ vni,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vni details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(attempts=2, wait=4, return_is_str=True, initial_wait=2)
+def verify_vrf_vni(tgen, input_dict):
+ """
+ API to verify vrf vni details using "show vrf vni json"
+ command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, evpn details
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "edge1":{
+ "vrfs": [
+ {
+ "RED":{
+ "vni": 75000,
+ "vxlanIntf": "vxlan75100",
+ "sviIntf": "br100",
+ "routerMac": "00:80:48:ba:d1:00",
+ "state": "Up"
+ }
+ }
+ ]
+ }
+ }
+
+ result = verify_vrf_vni(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for dut in input_dict.keys():
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying vrf vni details :", dut)
+
+ cmd = "show vrf vni json"
+ vrf_all_vni_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if not bool(vrf_all_vni_json):
+ errormsg = "No output for '{}' cli".format(cmd)
+ return errormsg
+
+ if "vrfs" in input_dict[dut]:
+ for vrfs in input_dict[dut]["vrfs"]:
+ for vrf, vrf_dict in vrfs.items():
+ found = False
+ for vrf_vni_json in vrf_all_vni_json["vrfs"]:
+ if "vrf" in vrf_vni_json:
+ if vrf_vni_json["vrf"] != vrf:
+ continue
+
+ for attribute in vrf_dict.keys():
+ if vrf_dict[attribute] == vrf_vni_json[attribute]:
+ found = True
+ logger.info(
+ "[DUT %s]: VRF: %s, "
+ "verifying %s "
+ ", Found Expected: %s "
+ "[PASSED]!!",
+ dut,
+ vrf,
+ attribute,
+ vrf_vni_json[attribute],
+ )
+ else:
+ errormsg = (
+ "[DUT: %s] VRF: %s, "
+ "verifying %s [FAILED!!] "
+ ", EXPECTED : %s "
+ ", FOUND : %s"
+ % (
+ dut,
+ vrf,
+ attribute,
+ vrf_dict[attribute],
+ vrf_vni_json[attribute],
+ )
+ )
+ return errormsg
+
+ else:
+ errormsg = "[DUT: %s] VRF: %s " "is not present in JSON" % (
+ dut,
+ vrf,
+ )
+ return errormsg
+
+ if found:
+ logger.info(
+ "[DUT %s] Verifying VRF: %s " " details [PASSED]!!",
+ dut,
+ vrf,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] Failed:" " vrf details are not present in input data" % (dut)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json
index f0738dc540..bfa0ecb849 100644
--- a/tests/topotests/pbr-topo1/r1/pbr-map.json
+++ b/tests/topotests/pbr-topo1/r1/pbr-map.json
@@ -62,6 +62,32 @@
},
"matchDst":"dead:beef::\/64",
"matchMark":314159
+ },
+ {
+ "sequenceNumber":15,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA15",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchDscp":10
+ },
+ {
+ "sequenceNumber":20,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA20",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"dead:beef::/64",
+ "matchEcn":1
}
]
},
diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf
index 298cba2860..45cb7656ab 100644
--- a/tests/topotests/pbr-topo1/r1/pbrd.conf
+++ b/tests/topotests/pbr-topo1/r1/pbrd.conf
@@ -73,6 +73,16 @@ pbr-map ASAKUSA seq 10
match mark 314159
set nexthop c0ff:ee::1
!
+pbr-map ASAKUSA seq 15
+ match dst-ip dead:beef::/64
+ match dscp af11
+ set nexthop c0ff:ee::1
+!
+pbr-map ASAKUSA seq 20
+ match dst-ip dead:beef::/64
+ match ecn 1
+ set nexthop c0ff:ee::1
+!
# Interface policies
int r1-eth1
pbr-policy EVA
diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons
index 8bec3c5bb6..0221b0c19e 100644
--- a/tools/etc/frr/daemons
+++ b/tools/etc/frr/daemons
@@ -72,6 +72,11 @@ vrrpd_options=" -A 127.0.0.1"
# The list of daemons to watch is automatically generated by the init script.
#watchfrr_options=""
+# To make watchfrr create/join the specified netns, use the following option:
+#watchfrr_options="--netns"
+# This only has an effect in /etc/frr/<somename>/daemons, and you need to
+# start FRR with "/usr/lib/frr/frrinit.sh start <somename>".
+
# for debugging purposes, you can specify a "wrap" command to start instead
# of starting the daemon directly, e.g. to use valgrind on ospfd:
# ospfd_wrap="/usr/bin/valgrind"
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 9e86cf2156..05410e3c25 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -63,14 +63,17 @@ class VtyshException(Exception):
pass
class Vtysh(object):
- def __init__(self, bindir=None, confdir=None, sockdir=None):
+ def __init__(self, bindir=None, confdir=None, sockdir=None, pathspace=None):
self.bindir = bindir
self.confdir = confdir
+ self.pathspace = pathspace
self.common_args = [os.path.join(bindir or '', 'vtysh')]
if confdir:
self.common_args.extend(['--config_dir', confdir])
if sockdir:
self.common_args.extend(['--vty_socket', sockdir])
+ if pathspace:
+ self.common_args.extend(['-N', pathspace])
def _call(self, args, stdin=None, stdout=None, stderr=None):
kwargs = {}
@@ -729,6 +732,36 @@ def line_exist(lines, target_ctx_keys, target_line, exact_match=True):
return True
return False
+def check_for_exit_vrf(lines_to_add, lines_to_del):
+
+ # exit-vrf is a bit tricky. If the new config is missing it but we
+ # have configs under a vrf, we need to add it at the end to do the
+ # right context changes. If exit-vrf exists in both the running and
+ # new config, we cannot delete it or it will break context changes.
+ add_exit_vrf = False
+ index = 0
+
+ for (ctx_keys, line) in lines_to_add:
+ if add_exit_vrf == True:
+ if ctx_keys[0] != prior_ctx_key:
+ insert_key=(prior_ctx_key),
+ lines_to_add.insert(index, ((insert_key, "exit-vrf")))
+ add_exit_vrf = False
+
+ if ctx_keys[0].startswith('vrf') and line:
+ if line is not "exit-vrf":
+ add_exit_vrf = True
+ prior_ctx_key = (ctx_keys[0])
+ else:
+ add_exit_vrf = False
+ index+=1
+
+ for (ctx_keys, line) in lines_to_del:
+ if line == "exit-vrf":
+ if (line_exist(lines_to_add, ctx_keys, line)):
+ lines_to_del.remove((ctx_keys, line))
+
+ return (lines_to_add, lines_to_del)
def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
@@ -1156,6 +1189,7 @@ def compare_context_objects(newconf, running):
for line in newconf_ctx.lines:
lines_to_add.append((newconf_ctx_keys, line))
+ (lines_to_add, lines_to_del) = check_for_exit_vrf(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_delete_re_add_lines(lines_to_add, lines_to_del)
(lines_to_add, lines_to_del) = ignore_unconfigurable_lines(lines_to_add, lines_to_del)
@@ -1171,6 +1205,7 @@ if __name__ == '__main__':
group.add_argument('--test', action='store_true', help='Show the deltas', default=False)
parser.add_argument('--debug', action='store_true', help='Enable debugs', default=False)
parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False)
+ parser.add_argument('--pathspace', '-N', metavar='NAME', help='Reload specified path/namespace', default=None)
parser.add_argument('filename', help='Location of new frr config file')
parser.add_argument('--overwrite', action='store_true', help='Overwrite frr.conf with running config output', default=False)
parser.add_argument('--bindir', help='path to the vtysh executable', default='/usr/bin')
@@ -1246,10 +1281,13 @@ if __name__ == '__main__':
log.error(msg)
sys.exit(1)
- vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket)
+ vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace)
# Verify that 'service integrated-vtysh-config' is configured
- vtysh_filename = args.confdir + '/vtysh.conf'
+ if args.pathspace:
+ vtysh_filename = args.confdir + '/' + args.pathspace + '/vtysh.conf'
+ else:
+ vtysh_filename = args.confdir + '/vtysh.conf'
service_integrated_vtysh_config = True
if os.path.isfile(vtysh_filename):
diff --git a/tools/frr.in b/tools/frr.in
index 40862aa4c9..b860797d5b 100755
--- a/tools/frr.in
+++ b/tools/frr.in
@@ -77,7 +77,7 @@ vtysh_b ()
{
# Rember, that all variables have been incremented by 1 in convert_daemon_prios()
if [ "$vtysh_enable" = 2 -a -f $C_PATH/frr.conf ]; then
- $VTYSH -b -n
+ $VTYSH -b
fi
}
diff --git a/tools/frr@.service b/tools/frr@.service
new file mode 100644
index 0000000000..0fa41c74a3
--- /dev/null
+++ b/tools/frr@.service
@@ -0,0 +1,25 @@
+[Unit]
+Description=FRRouting
+Documentation=https://frrouting.readthedocs.io/en/latest/setup.html
+Wants=network.target
+After=network-pre.target systemd-sysctl.service
+Before=network.target
+OnFailure=heartbeat-failed@%n.service
+
+[Service]
+Nice=-5
+Type=forking
+NotifyAccess=all
+StartLimitInterval=3m
+StartLimitBurst=3
+TimeoutSec=2m
+WatchdogSec=60s
+RestartSec=5
+Restart=on-abnormal
+LimitNOFILE=1024
+ExecStart=/usr/lib/frr/frrinit.sh start %I
+ExecStop=/usr/lib/frr/frrinit.sh stop %I
+ExecReload=/usr/lib/frr/frrinit.sh reload %I
+
+[Install]
+WantedBy=multi-user.target
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 2955f74ce3..9a144b2b06 100644
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -16,10 +16,14 @@
#
# This script should be installed in @CFG_SBIN@/frrcommon.sh
+# FRR_PATHSPACE is passed in from watchfrr
+suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}"
+nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}"
+
PATH=/bin:/usr/bin:/sbin:/usr/sbin
D_PATH="@CFG_SBIN@" # /usr/lib/frr
-C_PATH="@CFG_SYSCONF@" # /etc/frr
-V_PATH="@CFG_STATE@" # /var/run/frr
+C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr
+V_PATH="@CFG_STATE@${suffix}" # /var/run/frr
VTYSH="@vtysh_bin@" # /usr/bin/vtysh
FRR_USER="@enable_user@" # frr
FRR_GROUP="@enable_group@" # frr
@@ -61,9 +65,9 @@ vtysh_b () {
[ "$1" = "watchfrr" ] && return 0
[ -r "$C_PATH/frr.conf" ] || return 0
if [ -n "$1" ]; then
- "$VTYSH" -b -n -d "$1"
+ "$VTYSH" `echo $nsopt` -b -d "$1"
else
- "$VTYSH" -b -n
+ "$VTYSH" `echo $nsopt` -b
fi
}
@@ -156,7 +160,7 @@ daemon_start() {
instopt="${inst:+-n $inst}"
eval args="\$${daemon}_options"
- if eval "$all_wrap $wrap $bin -d $frr_global_options $instopt $args"; then
+ if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then
log_success_msg "Started $dmninst"
vtysh_b "$daemon"
else
@@ -292,9 +296,11 @@ load_old_config() {
}
. "$C_PATH/daemons"
-load_old_config "$C_PATH/daemons.conf"
-load_old_config "/etc/default/frr"
-load_old_config "/etc/sysconfig/frr"
+if [ -z "$FRR_PATHSPACE" ]; then
+ load_old_config "$C_PATH/daemons.conf"
+ load_old_config "/etc/default/frr"
+ load_old_config "/etc/sysconfig/frr"
+fi
if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then
log_warning_msg "watchfrr_options contains a bash array value." \
diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in
index 423d6b9b1d..539ab7d816 100644
--- a/tools/frrinit.sh.in
+++ b/tools/frrinit.sh.in
@@ -30,6 +30,9 @@ else
}
fi
+# "/usr/lib/frr/frrinit.sh start somenamespace"
+FRR_PATHSPACE="$2"
+
self="`dirname $0`"
if [ -r "$self/frrcommon.sh" ]; then
. "$self/frrcommon.sh"
@@ -105,7 +108,7 @@ reload)
NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}"
[ ! -r $NEW_CONFIG_FILE ] && log_failure_msg "Unable to read new configuration file $NEW_CONFIG_FILE" && exit 1
- "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE"
+ "$RELOAD_SCRIPT" --reload "$NEW_CONFIG_FILE" `echo $nsopt`
exit $?
;;
diff --git a/tools/subdir.am b/tools/subdir.am
index 723a87d100..e159d82d4c 100644
--- a/tools/subdir.am
+++ b/tools/subdir.am
@@ -48,6 +48,7 @@ EXTRA_DIST += \
tools/frr-reload \
tools/frr-reload.py \
tools/frr.service \
+ tools/frr@.service \
tools/generate_support_bundle.py \
tools/multiple-bgpd.sh \
tools/rrcheck.pl \
diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c
index 3e79cff391..d3f9b0c730 100644
--- a/vrrpd/vrrp.c
+++ b/vrrpd/vrrp.c
@@ -2351,7 +2351,7 @@ int vrrp_config_write_global(struct vty *vty)
if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes)
vty_out(vty,
- "vrrp default advertisement-interval %hu\n",
+ "vrrp default advertisement-interval %u\n",
vd.advertisement_interval * CS2MS);
if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes)
@@ -2374,7 +2374,7 @@ static unsigned int vrrp_hash_key(const void *arg)
const struct vrrp_vrouter *vr = arg;
char key[IFNAMSIZ + 64];
- snprintf(key, sizeof(key), "%s@%hhu", vr->ifp->name, vr->vrid);
+ snprintf(key, sizeof(key), "%s@%u", vr->ifp->name, vr->vrid);
return string_hash_make(key);
}
diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c
index 61fcc4e85d..3cb13bd71b 100644
--- a/vrrpd/vrrp_packet.c
+++ b/vrrpd/vrrp_packet.c
@@ -296,7 +296,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m,
(*pkt)->hdr.chksum, chksum);
/* Type check */
- VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %hhu",
+ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u",
(*pkt)->hdr.vertype & 0x0f);
/* Exact size check */
diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c
index 1a12337086..dc7f023e8f 100644
--- a/vrrpd/vrrp_vty.c
+++ b/vrrpd/vrrp_vty.c
@@ -653,7 +653,7 @@ DEFPY(vrrp_vrid_show_summary,
continue;
ttable_add_row(
- tt, "%s|%hhu|%hhu|%d|%d|%s|%s",
+ tt, "%s|%u|%hhu|%d|%d|%s|%s",
vr->ifp->name, vr->vrid, vr->priority,
vr->v4->addrs->count, vr->v6->addrs->count,
vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master"
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index 794e1f4c73..346061d7ca 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -40,7 +40,7 @@ sub scan_file {
$cppadd = $fabricd ? "-DFABRICD=1" : "";
- open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |");
+ open (FH, "@CPP@ -P -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -Ivtysh/@top_builddir@ -Ivtysh/@top_srcdir@ -Ivtysh/@top_srcdir@/lib -Ivtysh/@top_builddir@/lib -Ivtysh/@top_srcdir@/bgpd -Ivtysh/@top_srcdir@/bgpd/rfapi @LUA_INCLUDE@ @CPPFLAGS@ $cppadd $file |");
local $/; undef $/;
$line = <FH>;
if (!close (FH)) {
@@ -48,7 +48,7 @@ sub scan_file {
}
# ?: makes a group non-capturing
- @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|ALIAS|ALIAS_HIDDEN)\s*\(.+?\));?\s?\s?\n/sg);
+ @defun = ($line =~ /((?:DEFUN|DEFUN_HIDDEN|ALIAS|ALIAS_HIDDEN|DEFPY|DEFPY_HIDDEN)\s*\(.+?\));?\s?\s?\n/sg);
@install = ($line =~ /install_element\s*\(\s*[0-9A-Z_]+,\s*&[^;]*;\s*\n/sg);
# DEFUN process
diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c
index db327a0fa2..d1003ad5fa 100644
--- a/watchfrr/watchfrr.c
+++ b/watchfrr/watchfrr.c
@@ -29,6 +29,7 @@
#include "lib_errors.h"
#include "zlog_targets.h"
#include "network.h"
+#include "printfrr.h"
#include <getopt.h>
#include <sys/un.h>
@@ -174,6 +175,7 @@ struct daemon {
#define OPTION_MINRESTART 2000
#define OPTION_MAXRESTART 2001
#define OPTION_DRY 2002
+#define OPTION_NETNS 2003
static const struct option longopts[] = {
{"daemon", no_argument, NULL, 'd'},
@@ -190,6 +192,9 @@ static const struct option longopts[] = {
{"max-restart-interval", required_argument, NULL, OPTION_MAXRESTART},
{"pid-file", required_argument, NULL, 'p'},
{"blank-string", required_argument, NULL, 'b'},
+#ifdef GNU_LINUX
+ {"netns", optional_argument, NULL, OPTION_NETNS},
+#endif
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}};
@@ -244,7 +249,12 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n",
-d, --daemon Run in daemon mode. In this mode, error messages are sent\n\
to syslog instead of stdout.\n\
-S, --statedir Set the vty socket directory (default is %s)\n\
--l, --loglevel Set the logging level (default is %d).\n\
+-N, --pathspace Insert prefix into config & socket paths\n"
+#ifdef GNU_LINUX
+" --netns Create and/or use Linux network namespace. If no name is\n"
+" given, uses the value from `-N`.\n"
+#endif
+"-l, --loglevel Set the logging level (default is %d).\n\
The value should range from %d (LOG_EMERG) to %d (LOG_DEBUG),\n\
but it can be set higher than %d if extra-verbose debugging\n\
messages are desired.\n\
@@ -704,7 +714,7 @@ static void daemon_send_ready(int exitcode)
frr_detach();
- snprintf(started, sizeof(started), "%s%s", frr_vtydir,
+ snprintf(started, sizeof(started), "%s/%s", frr_vtydir,
"watchfrr.started");
fp = fopen(started, "w");
if (fp)
@@ -1102,6 +1112,148 @@ static int startup_timeout(struct thread *t_wakeup)
return 0;
}
+#ifdef GNU_LINUX
+
+#include <sys/mount.h>
+#include <sched.h>
+
+#define NETNS_RUN_DIR "/var/run/netns"
+
+static void netns_create(int dirfd, const char *nsname)
+{
+ /* make /var/run/netns shared between mount namespaces
+ * just like iproute2 sets it up
+ */
+ if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
+ if (errno != EINVAL) {
+ perror("mount");
+ exit(1);
+ }
+
+ if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none",
+ MS_BIND | MS_REC, NULL)) {
+ perror("mount");
+ exit(1);
+ }
+
+ if (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC,
+ NULL)) {
+ perror("mount");
+ exit(1);
+ }
+ }
+
+ /* need an empty file to mount on top of */
+ int nsfd = openat(dirfd, nsname, O_CREAT | O_RDONLY | O_EXCL, 0);
+
+ if (nsfd < 0) {
+ fprintf(stderr, "failed to create \"%s/%s\": %s\n",
+ NETNS_RUN_DIR, nsname, strerror(errno));
+ exit(1);
+ }
+ close(nsfd);
+
+ if (unshare(CLONE_NEWNET)) {
+ perror("unshare");
+ unlinkat(dirfd, nsname, 0);
+ exit(1);
+ }
+
+ char *dstpath = asprintfrr(MTYPE_TMP, "%s/%s", NETNS_RUN_DIR, nsname);
+
+ /* bind-mount so the namespace has a name and is persistent */
+ if (mount("/proc/self/ns/net", dstpath, "none", MS_BIND, NULL) < 0) {
+ fprintf(stderr, "failed to bind-mount netns to \"%s\": %s\n",
+ dstpath, strerror(errno));
+ unlinkat(dirfd, nsname, 0);
+ exit(1);
+ }
+
+ XFREE(MTYPE_TMP, dstpath);
+}
+
+static void netns_setup(const char *nsname)
+{
+ int dirfd, nsfd;
+
+ dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ if (errno == ENOTDIR) {
+ fprintf(stderr, "error: \"%s\" is not a directory!\n",
+ NETNS_RUN_DIR);
+ exit(1);
+ } else if (errno == ENOENT) {
+ if (mkdir(NETNS_RUN_DIR, 0755)) {
+ fprintf(stderr, "error: \"%s\": mkdir: %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ dirfd = open(NETNS_RUN_DIR, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ fprintf(stderr, "error: \"%s\": opendir: %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ } else {
+ fprintf(stderr, "error: \"%s\": %s\n",
+ NETNS_RUN_DIR, strerror(errno));
+ exit(1);
+ }
+ }
+
+ nsfd = openat(dirfd, nsname, O_RDONLY);
+ if (nsfd < 0 && errno != ENOENT) {
+ fprintf(stderr, "error: \"%s/%s\": %s\n",
+ NETNS_RUN_DIR, nsname, strerror(errno));
+ exit(1);
+ }
+ if (nsfd < 0)
+ netns_create(dirfd, nsname);
+ else {
+ if (setns(nsfd, CLONE_NEWNET)) {
+ perror("setns");
+ exit(1);
+ }
+ close(nsfd);
+ }
+ close(dirfd);
+
+ /* make sure loopback is up... weird things happen otherwise.
+ * ioctl is perfectly fine for this, don't need netlink...
+ */
+ int sockfd;
+ struct ifreq ifr = { };
+
+ strlcpy(ifr.ifr_name, "lo", sizeof(ifr.ifr_name));
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr)) {
+ perror("ioctl(SIOCGIFFLAGS, \"lo\")");
+ exit(1);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, &ifr)) {
+ perror("ioctl(SIOCSIFFLAGS, \"lo\")");
+ exit(1);
+ }
+ }
+ close(sockfd);
+}
+
+#else /* !GNU_LINUX */
+
+static void netns_setup(const char *nsname)
+{
+ fprintf(stderr, "network namespaces are only available on Linux\n");
+ exit(1);
+}
+#endif
+
static void watchfrr_init(int argc, char **argv)
{
const char *special = "zebra";
@@ -1191,11 +1343,13 @@ int main(int argc, char **argv)
{
int opt;
const char *blankstr = NULL;
+ const char *netns = NULL;
+ bool netns_en = false;
frr_preinit(&watchfrr_di, argc, argv);
progname = watchfrr_di.progname;
- frr_opt_add("b:dk:l:i:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, "");
+ frr_opt_add("b:di:k:l:N:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, "");
gs.restart.name = "all";
while ((opt = frr_getopt(argc, argv, NULL)) != EOF) {
@@ -1260,6 +1414,16 @@ int main(int argc, char **argv)
frr_help_exit(1);
}
} break;
+ case OPTION_NETNS:
+ netns_en = true;
+ if (strchr(optarg, '/')) {
+ fprintf(stderr,
+ "invalid network namespace name \"%s\" (may not contain slashes)\n",
+ optarg);
+ frr_help_exit(1);
+ }
+ netns = optarg;
+ break;
case 'i': {
char garbage[3];
int period;
@@ -1351,6 +1515,17 @@ int main(int argc, char **argv)
gs.restart.interval = gs.min_restart_interval;
+ /* env variable for the processes that we start */
+ if (watchfrr_di.pathspace)
+ setenv("FRR_PATHSPACE", watchfrr_di.pathspace, 1);
+ else
+ unsetenv("FRR_PATHSPACE");
+
+ if (netns_en && !netns)
+ netns = watchfrr_di.pathspace;
+ if (netns_en && netns && netns[0])
+ netns_setup(netns);
+
master = frr_init();
watchfrr_error_init();
watchfrr_init(argc, argv);
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index befdc3467d..1bb693a1ef 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -361,11 +361,18 @@ module frr-isisd {
"IS-type of this circuit.";
}
- leaf bfd-monitoring {
- type boolean;
- default "false";
- description
- "Monitor IS-IS peers on this circuit.";
+ container bfd-monitoring {
+ leaf enabled {
+ type boolean;
+ default "false";
+ description
+ "Monitor IS-IS peers on this circuit.";
+ }
+ leaf profile {
+ type string;
+ description
+ "Let BFD use a pre-configured profile.";
+ }
}
container csnp-interval {
diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang
index 8894eeaa26..7762c75d68 100644
--- a/yang/frr-zebra.yang
+++ b/yang/frr-zebra.yang
@@ -648,6 +648,23 @@ module frr-zebra {
}
}
+ grouping vrf-vni-mapping {
+ description
+ "EVPN L3-VNI mapping corresponding to a VRF.";
+ leaf l3vni-id {
+ type vni-id-type;
+ description
+ "EVPN L3-VNI id to map to the VRF.";
+ }
+
+ leaf prefix-only {
+ type boolean;
+ default "false";
+ description
+ "EVPN asymmetric mode advertise prefix routes only.";
+ }
+ }
+
// End of zebra container
/*
* RPCs
@@ -2041,7 +2058,11 @@ module frr-zebra {
description
"Extends VRF model with Zebra-related parameters.";
container zebra {
+ description
+ "Zebra's vrf specific configuration and operational model.";
uses ribs;
+
+ uses vrf-vni-mapping;
}
}
@@ -2144,26 +2165,6 @@ module frr-zebra {
description
"Limit on the number of updates queued to the dataplane subsystem.";
}
- list vrf-vni-mapping {
- key "vrf-id";
- description
- "EVPN VNI mapping corresponding to a VRF.";
- leaf vrf-id {
- type uint32;
- description
- "The identifier for a VRF.";
- }
- leaf vni-id {
- type vni-id-type;
- description
- "The VNI id to map to the VRF.";
- }
- leaf prefix-only {
- type empty;
- description
- "Prefix routes only.";
- }
- }
/*
* Debug options
*/
diff --git a/yang/subdir.am b/yang/subdir.am
index 6caf9fc5f3..6aae0e4701 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -29,10 +29,7 @@ dist_yangmodels_DATA += yang/frr-vrf.yang
dist_yangmodels_DATA += yang/frr-route-types.yang
dist_yangmodels_DATA += yang/frr-routing.yang
dist_yangmodels_DATA += yang/ietf/ietf-routing-types.yang
-dist_yangmodels_DATA += yang/frr-igmp.yang
dist_yangmodels_DATA += yang/ietf/ietf-interfaces.yang
-dist_yangmodels_DATA += yang/frr-pim.yang
-dist_yangmodels_DATA += yang/frr-pim-rp.yang
if BFDD
dist_yangmodels_DATA += yang/frr-bfdd.yang
@@ -65,3 +62,9 @@ endif
if ZEBRA
dist_yangmodels_DATA += yang/frr-zebra.yang
endif
+
+if PIMD
+dist_yangmodels_DATA += yang/frr-igmp.yang
+dist_yangmodels_DATA += yang/frr-pim.yang
+dist_yangmodels_DATA += yang/frr-pim-rp.yang
+endif
diff --git a/zebra/rib.h b/zebra/rib.h
index ec992974fa..6ec9241b73 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -144,6 +144,10 @@ struct route_entry {
#define ROUTE_ENTRY_INSTALLED 0x10
/* Route has Failed installation into the Data Plane in some manner */
#define ROUTE_ENTRY_FAILED 0x20
+/* Route has a 'fib' set of nexthops, probably because the installed set
+ * differs from the rib/normal set of nexthops.
+ */
+#define ROUTE_ENTRY_USE_FIB_NHG 0x40
/* Sequence value incremented for each dataplane operation */
uint32_t dplane_sequence;
@@ -526,26 +530,28 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
/*
- * Access active nexthop-group, either RIB or FIB version
+ * Access installed/fib nexthops, which may be a subset of the
+ * rib nexthops.
*/
static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re)
{
- if (re->fib_ng.nexthop)
+ /* If the fib set is a subset of the active rib set,
+ * use the dedicated fib list.
+ */
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG))
return &(re->fib_ng);
else
return &(re->nhe->nhg);
}
/*
- * Access active nexthop-group, either RIB or FIB version
+ * Access backup nexthop-group that represents the installed backup nexthops;
+ * any installed backup will be on the fib list.
*/
static inline struct nexthop_group *rib_get_fib_backup_nhg(
struct route_entry *re)
{
- if (re->fib_backup_ng.nexthop)
- return &(re->fib_backup_ng);
- else
- return zebra_nhg_get_backup_nhg(re->nhe);
+ return &(re->fib_backup_ng);
}
extern void zebra_vty_init(void);
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index d5686ad883..648e9eabe1 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -58,10 +58,12 @@
* Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
* or the number of bytes written to buf.
*/
-static ssize_t netlink_rule_msg_encode(
- int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
- uint32_t priority, uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen)
+static ssize_t
+netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
+ uint32_t filter_bm, uint32_t priority, uint32_t table,
+ const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark,
+ uint8_t dsfield, void *buf, size_t buflen)
{
uint8_t protocol = RTPROT_ZEBRA;
int family;
@@ -122,6 +124,10 @@ static ssize_t netlink_rule_msg_encode(
return 0;
}
+ /* dsfield, if specified */
+ if (filter_bm & PBR_FILTER_DSFIELD)
+ req->frh.tos = dsfield;
+
/* Route table to use to forward, if filter criteria matches. */
if (table < 256)
req->frh.table = table;
@@ -145,16 +151,15 @@ static ssize_t netlink_rule_msg_encode(
/* Install or uninstall specified rule for a specific interface.
* Form netlink message and ship it.
*/
-static int
-netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx,
- uint32_t filter_bm, uint32_t priority,
- uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark)
+static int netlink_rule_update_internal(
+ int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
+ uint32_t priority, uint32_t table, const struct prefix *src_ip,
+ const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield)
{
char buf[NL_PKT_BUF_SIZE];
netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
- dst_ip, fwmark, buf, sizeof(buf));
+ dst_ip, fwmark, dsfield, buf, sizeof(buf));
return netlink_talk_info(netlink_talk_filter, (void *)&buf,
dplane_ctx_get_ns(ctx), 0);
}
@@ -188,7 +193,8 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
dplane_ctx_rule_get_priority(ctx),
dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
dplane_ctx_rule_get_dst_ip(ctx),
- dplane_ctx_rule_get_fwmark(ctx));
+ dplane_ctx_rule_get_fwmark(ctx),
+ dplane_ctx_rule_get_dsfield(ctx));
/**
* Delete the old one.
@@ -203,7 +209,8 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
dplane_ctx_rule_get_old_table(ctx),
dplane_ctx_rule_get_old_src_ip(ctx),
dplane_ctx_rule_get_old_dst_ip(ctx),
- dplane_ctx_rule_get_old_fwmark(ctx));
+ dplane_ctx_rule_get_old_fwmark(ctx),
+ dplane_ctx_rule_get_old_dsfield(ctx));
return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index d088a3a3d0..33972681f8 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -1416,6 +1416,7 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re,
struct nexthop *nexthop = NULL;
struct ipaddr vtep_ip;
struct interface *ifp;
+ int i;
char nhbuf[INET6_ADDRSTRLEN] = "";
switch (api_nh->type) {
@@ -1521,17 +1522,36 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re,
nexthop->weight = api_nh->weight;
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- if (api_nh->backup_idx < api->backup_nexthop_num) {
- /* Capture backup info */
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = api_nh->backup_idx;
- } else {
- /* Warn about invalid backup index */
+ /* Validate count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s: invalid backup nh idx %d",
- __func__, api_nh->backup_idx);
+ zlog_debug("%s: invalid backup nh count %d",
+ __func__, api_nh->backup_num);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
+
+ /* Copy backup info */
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nexthop->backup_num = api_nh->backup_num;
+
+ for (i = 0; i < api_nh->backup_num; i++) {
+ /* Validate backup index */
+ if (api_nh->backup_idx[i] < api->backup_nexthop_num) {
+ nexthop->backup_idx[i] = api_nh->backup_idx[i];
+ } else {
+ if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: invalid backup nh idx %d",
+ __func__,
+ api_nh->backup_idx[i]);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
}
}
+
done:
return nexthop;
}
@@ -1703,7 +1723,7 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
__func__, nhbuf);
}
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = 0;
+ nexthop->backup_num = 0;
}
/* MPLS labels for BGP-LU or Segment Routing */
@@ -1981,6 +2001,56 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS)
}
/*
+ * Validate incoming zapi mpls lsp / labels message
+ */
+static int zapi_labels_validate(const struct zapi_labels *zl)
+{
+ int ret = -1;
+ int i, j, idx;
+ uint32_t bits[8];
+ uint32_t ival;
+ const struct zapi_nexthop *znh;
+
+ /* Validate backup info: no duplicates for a single primary */
+ if (zl->backup_nexthop_num == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ for (j = 0; j < zl->nexthop_num; j++) {
+ znh = &zl->nexthops[j];
+
+ memset(bits, 0, sizeof(bits));
+
+ for (i = 0; i < znh->backup_num; i++) {
+ idx = znh->backup_idx[i] / 32;
+
+ ival = 1 << znh->backup_idx[i] % 32;
+
+ /* Check whether value is already used */
+ if (ival & bits[idx]) {
+ /* Fail */
+
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: invalid zapi mpls message: duplicate backup nexthop index %d",
+ __func__,
+ znh->backup_idx[i]);
+ goto done;
+ }
+
+ /* Mark index value */
+ bits[idx] |= ival;
+ }
+ }
+
+ ret = 0;
+
+done:
+
+ return ret;
+}
+
+/*
* Handle request to create an MPLS LSP.
*
* A single message can fully specify an LSP with multiple nexthops.
@@ -2006,6 +2076,10 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS)
if (!mpls_enabled)
return;
+ /* Validate; will debug on failure */
+ if (zapi_labels_validate(&zl) < 0)
+ return;
+
ret = mpls_zapi_labels_process(true, zvrf, &zl);
if (ret < 0) {
if (IS_ZEBRA_DEBUG_RECV)
@@ -2087,6 +2161,10 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS)
if (!mpls_enabled)
return;
+ /* Validate; will debug on failure */
+ if (zapi_labels_validate(&zl) < 0)
+ return;
+
/* This removes everything, then re-adds from the client's
* zapi message. Since the LSP will be processed later, on this
* this same pthread, all of the changes will 'appear' at once.
@@ -2526,6 +2604,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s,
prefix_blen(&zpr.rule.filter.dst_ip));
STREAM_GETW(s, zpr.rule.filter.dst_port);
+ STREAM_GETC(s, zpr.rule.filter.dsfield);
STREAM_GETL(s, zpr.rule.filter.fwmark);
STREAM_GETL(s, zpr.rule.action.table);
STREAM_GETL(s, zpr.rule.ifindex);
@@ -2556,6 +2635,9 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
if (zpr.rule.filter.dst_port)
zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT;
+ if (zpr.rule.filter.dsfield)
+ zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD;
+
if (zpr.rule.filter.fwmark)
zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 4e19e58d7a..0cc7139d6b 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -204,6 +204,7 @@ struct dplane_ctx_rule {
/* Filter criteria */
uint32_t filter_bm;
uint32_t fwmark;
+ uint8_t dsfield;
struct prefix src_ip;
struct prefix dst_ip;
};
@@ -1676,6 +1677,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
return ctx->u.rule.old.fwmark;
}
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.new.dsfield;
+}
+
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.rule.old.dsfield;
+}
+
const struct prefix *
dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx)
{
@@ -2001,10 +2016,19 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
break;
}
- /* Need to copy flags too */
+ /* Need to copy flags and backup info too */
new_nhlfe->flags = nhlfe->flags;
new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
+ if (CHECK_FLAG(new_nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP)) {
+ new_nhlfe->nexthop->backup_num =
+ nhlfe->nexthop->backup_num;
+ memcpy(new_nhlfe->nexthop->backup_idx,
+ nhlfe->nexthop->backup_idx,
+ new_nhlfe->nexthop->backup_num);
+ }
+
if (nhlfe == lsp->best_nhlfe)
ctx->u.lsp.best_nhlfe = new_nhlfe;
}
@@ -2104,8 +2128,15 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
if (re) {
nhg = rib_get_fib_nhg(re);
- copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- nhg->nexthop, NULL);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(ctx->u.pw.nhg.nexthop),
+ nhg->nexthop, NULL);
+
+ /* Include any installed backup nexthops */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(ctx->u.pw.nhg.nexthop),
+ nhg->nexthop, NULL);
}
route_unlock_node(rn);
}
@@ -2129,6 +2160,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
dplane_rule->filter_bm = rule->rule.filter.filter_bm;
dplane_rule->fwmark = rule->rule.filter.fwmark;
+ dplane_rule->dsfield = rule->rule.filter.dsfield;
prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
}
@@ -2461,6 +2493,7 @@ dplane_route_notif_update(struct route_node *rn,
enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
struct zebra_dplane_ctx *new_ctx = NULL;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
if (rn == NULL || re == NULL)
goto done;
@@ -2482,8 +2515,17 @@ dplane_route_notif_update(struct route_node *rn,
nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop);
new_ctx->u.rinfo.zd_ng.nexthop = NULL;
- copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
- (rib_get_fib_nhg(re))->nexthop, NULL);
+ nhg = rib_get_fib_nhg(re);
+ if (nhg && nhg->nexthop)
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ nhg->nexthop, NULL);
+
+ /* Check for installed backup nexthops also */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop) {
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ nhg->nexthop, NULL);
+ }
for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -2583,6 +2625,8 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp,
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
int ret = EINVAL;
struct zebra_dplane_ctx *ctx = NULL;
+ struct nhlfe_list_head *head;
+ zebra_nhlfe_t *nhlfe, *new_nhlfe;
/* Obtain context block */
ctx = dplane_ctx_alloc();
@@ -2591,10 +2635,27 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp,
goto done;
}
+ /* Copy info from zebra LSP */
ret = dplane_ctx_lsp_init(ctx, op, lsp);
if (ret != AOK)
goto done;
+ /* Add any installed backup nhlfes */
+ head = &(ctx->u.lsp.backup_nhlfe_list);
+ frr_each(nhlfe_list, head, nhlfe) {
+
+ if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) &&
+ CHECK_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp),
+ nhlfe->type,
+ nhlfe->nexthop);
+
+ /* Need to copy flags too */
+ new_nhlfe->flags = nhlfe->flags;
+ new_nhlfe->nexthop->flags = nhlfe->nexthop->flags;
+ }
+ }
+
/* Capture info about the source of the notification */
dplane_ctx_set_notif_provider(
ctx,
@@ -4002,19 +4063,19 @@ bool dplane_is_in_shutdown(void)
*/
void zebra_dplane_pre_finish(void)
{
- struct zebra_dplane_provider *dp;
+ struct zebra_dplane_provider *prov;
if (IS_ZEBRA_DEBUG_DPLANE)
- zlog_debug("Zebra dataplane pre-fini called");
+ zlog_debug("Zebra dataplane pre-finish called");
zdplane_info.dg_is_shutdown = true;
/* Notify provider(s) of pending shutdown. */
- TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) {
- if (dp->dp_fini == NULL)
+ TAILQ_FOREACH(prov, &zdplane_info.dg_providers_q, dp_prov_link) {
+ if (prov->dp_fini == NULL)
continue;
- dp->dp_fini(dp, true);
+ prov->dp_fini(prov, true /* early */);
}
}
@@ -4336,7 +4397,10 @@ void zebra_dplane_shutdown(void)
zdplane_info.dg_pthread = NULL;
zdplane_info.dg_master = NULL;
- /* Notify provider(s) of final shutdown. */
+ /* Notify provider(s) of final shutdown.
+ * Note that this call is in the main pthread, so providers must
+ * be prepared for that.
+ */
TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) {
if (dp->dp_fini == NULL)
continue;
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 8e873886df..0fa21f620d 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -412,6 +412,8 @@ uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx);
const struct prefix *
dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
const struct prefix *
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 27f1f9396c..f905036323 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -120,7 +120,8 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp,
enum lsp_types_t type);
static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf,
mpls_label_t in_label);
-static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty);
+static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty,
+ const char *indent);
static void lsp_print(struct vty *vty, zebra_lsp_t *lsp);
static void *slsp_alloc(void *p);
static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype,
@@ -1503,7 +1504,9 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
{
char buf[BUFSIZ];
json_object *json_nhlfe = NULL;
+ json_object *json_backups = NULL;
struct nexthop *nexthop = nhlfe->nexthop;
+ int i;
json_nhlfe = json_object_new_object();
json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type));
@@ -1534,13 +1537,27 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
default:
break;
}
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nexthop->backup_num; i++) {
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nexthop->backup_idx[i]));
+ }
+
+ json_object_object_add(json_nhlfe, "backupIndex",
+ json_backups);
+ }
+
return json_nhlfe;
}
/*
* Print the NHLFE for a LSP forwarding entry.
*/
-static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
+static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty,
+ const char *indent)
{
struct nexthop *nexthop;
char buf[MPLS_LABEL_STRLEN];
@@ -1555,6 +1572,10 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
nexthop->nh_label->label,
buf, sizeof(buf), 0),
nhlfe->distance);
+
+ if (indent)
+ vty_out(vty, "%s", indent);
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
@@ -1592,31 +1613,34 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
static void lsp_print(struct vty *vty, zebra_lsp_t *lsp)
{
zebra_nhlfe_t *nhlfe, *backup;
- int i;
+ int i, j;
vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label,
CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)"
: "");
frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) {
- nhlfe_print(nhlfe, vty);
+ nhlfe_print(nhlfe, vty, NULL);
+
+ if (nhlfe->nexthop == NULL ||
+ !CHECK_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP))
+ continue;
- if (nhlfe->nexthop &&
- CHECK_FLAG(nhlfe->nexthop->flags,
- NEXTHOP_FLAG_HAS_BACKUP)) {
- /* Find backup in backup list */
+ /* Backup nhlfes: find backups in backup list */
+ for (j = 0; j < nhlfe->nexthop->backup_num; j++) {
i = 0;
backup = NULL;
frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) {
- if (i == nhlfe->nexthop->backup_idx)
+ if (i == nhlfe->nexthop->backup_idx[j])
break;
i++;
}
if (backup) {
vty_out(vty, " [backup %d]", i);
- nhlfe_print(backup, vty);
+ nhlfe_print(backup, vty, " ");
}
}
}
@@ -1640,6 +1664,19 @@ static json_object *lsp_json(zebra_lsp_t *lsp)
json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe));
json_object_object_add(json, "nexthops", json_nhlfe_list);
+ json_nhlfe_list = NULL;
+
+
+ frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) {
+ if (json_nhlfe_list == NULL)
+ json_nhlfe_list = json_object_new_array();
+
+ json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe));
+ }
+
+ if (json_nhlfe_list)
+ json_object_object_add(json, "backupNexthops", json_nhlfe_list);
+
return json;
}
@@ -2109,6 +2146,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
__func__, buf);
SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
} else {
if (is_debug)
@@ -2116,6 +2154,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
__func__, buf);
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
}
if (CHECK_FLAG(ctx_nhlfe->nexthop->flags,
@@ -2137,6 +2176,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head,
zlog_debug("%s: no match for lsp nhlfe %s",
__func__, buf);
UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED);
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
@@ -3332,7 +3372,14 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,
/* Update backup info if present */
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- nhlfe->nexthop->backup_idx = znh->backup_idx;
+ if (znh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ nhlfe_del(nhlfe);
+ return -1;
+ }
+
+ nhlfe->nexthop->backup_num = znh->backup_num;
+ memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx,
+ znh->backup_num);
SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
}
diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c
index 6f01c41d71..a25d08f267 100644
--- a/zebra/zebra_nb.c
+++ b/zebra/zebra_nb.c
@@ -92,27 +92,6 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping",
- .cbs = {
- .create = zebra_vrf_vni_mapping_create,
- .destroy = zebra_vrf_vni_mapping_destroy,
- }
- },
- {
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping/vni-id",
- .cbs = {
- .modify = zebra_vrf_vni_mapping_vni_id_modify,
- .destroy = zebra_vrf_vni_mapping_vni_id_destroy,
- }
- },
- {
- .xpath = "/frr-zebra:zebra/vrf-vni-mapping/prefix-only",
- .cbs = {
- .create = zebra_vrf_vni_mapping_prefix_only_create,
- .destroy = zebra_vrf_vni_mapping_prefix_only_destroy,
- }
- },
- {
.xpath = "/frr-zebra:zebra/debugs/debug-events",
.cbs = {
.modify = zebra_debugs_debug_events_modify,
@@ -636,6 +615,19 @@ const struct frr_yang_module_info frr_zebra_info = {
}
},
{
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id",
+ .cbs = {
+ .modify = lib_vrf_zebra_l3vni_id_modify,
+ .destroy = lib_vrf_zebra_l3vni_id_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only",
+ .cbs = {
+ .modify = lib_vrf_zebra_prefix_only_modify,
+ }
+ },
+ {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length",
.cbs = {
.modify = lib_route_map_entry_match_condition_ipv4_prefix_length_modify,
diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h
index defa206d59..80aeb02d2d 100644
--- a/zebra/zebra_nb.h
+++ b/zebra/zebra_nb.h
@@ -57,12 +57,6 @@ int zebra_import_kernel_table_route_map_destroy(
int zebra_allow_external_route_update_create(struct nb_cb_create_args *args);
int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args);
int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args);
-int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args);
-int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args);
-int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args);
-int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args);
-int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args);
-int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args);
int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args);
int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args);
int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args);
@@ -316,6 +310,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem(
struct yang_data *
lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem(
struct nb_cb_get_elem_args *args);
+int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args);
+int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args);
+int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index 259b5f1eb2..e4501273bd 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -31,6 +31,8 @@
#include "zebra/connected.h"
#include "zebra/zebra_router.h"
#include "zebra/debug.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_vxlan.h"
/*
* XPath: /frr-zebra:zebra/mcast-rpf-lookup
@@ -273,99 +275,6 @@ int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args)
}
/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping
- */
-int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping/vni-id
- */
-int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
- * XPath: /frr-zebra:zebra/vrf-vni-mapping/prefix-only
- */
-int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args)
-{
- switch (args->event) {
- case NB_EV_VALIDATE:
- case NB_EV_PREPARE:
- case NB_EV_ABORT:
- case NB_EV_APPLY:
- /* TODO: implement me. */
- break;
- }
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-zebra:zebra/debugs/debug-events
*/
int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args)
@@ -1277,6 +1186,150 @@ int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args)
return NB_OK;
}
+/*
+ * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id
+ */
+int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ vni_t vni = 0;
+ zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf_evpn = NULL;
+ char err[ERR_STR_SZ];
+ bool pfx_only = false;
+ const struct lyd_node *pn_dnode;
+ const char *vrfname;
+
+ switch (args->event) {
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_VALIDATE:
+ zvrf_evpn = zebra_vrf_get_evpn();
+ if (!zvrf_evpn)
+ return NB_ERR_VALIDATION;
+
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+ /* Get vrf info from parent node, reject configuration
+ * if zebra vrf already mapped to different vni id.
+ */
+ pn_dnode = yang_dnode_get_parent(args->dnode, "vrf");
+ if (pn_dnode) {
+ vrfname = yang_dnode_get_string(pn_dnode, "./name");
+ vrf = vrf_lookup_by_name(vrfname);
+ zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ if (zvrf->l3vni && zvrf->l3vni != vni) {
+ zlog_debug(
+ "vni %u cannot be configured as vni %u is already configured under the vrf",
+ vni, zvrf->l3vni);
+ return NB_ERR_VALIDATION;
+ }
+ }
+
+ /* Check if this VNI is already present in the system */
+ zl3vni = zl3vni_lookup(vni);
+ if (zl3vni) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "VNI %u is already configured as L3-VNI",
+ vni);
+ return NB_ERR_VALIDATION;
+ }
+
+ break;
+ case NB_EV_APPLY:
+
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+ /* Note: This covers lib_vrf_zebra_prefix_only_modify() config
+ * along with l3vni config
+ */
+ pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only");
+
+ if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ pfx_only ? 1 : 0, 1)
+ != 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "vrf vni %u mapping failed with error: %s",
+ vni, err);
+ return NB_ERR;
+ }
+
+ /* Mark as having FRR configuration */
+ vrf_set_user_cfged(vrf);
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+ vni_t vni = 0;
+ char err[ERR_STR_SZ];
+ uint8_t filter = 0;
+
+ switch (args->event) {
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_VALIDATE:
+ break;
+ case NB_EV_APPLY:
+ vrf = nb_running_get_entry(args->dnode, NULL, true);
+ zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ vni = yang_dnode_get_uint32(args->dnode, NULL);
+
+ if (!zl3vni_lookup(vni))
+ return NB_OK;
+
+ if (zvrf->l3vni != vni)
+ return NB_ERR;
+
+ if (is_l3vni_for_prefix_routes_only(zvrf->l3vni))
+ filter = 1;
+
+ if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ filter, 0)
+ != 0) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "vrf vni %u unmapping failed with error: %s",
+ vni, err);
+ return NB_ERR;
+ }
+
+ /* If no other FRR config for this VRF, mark accordingly. */
+ if (!zebra_vrf_has_config(zvrf))
+ vrf_reset_user_cfged(vrf);
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only
+ */
+int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ /* TODO: implement me. */
+ break;
+ }
+
+ return NB_OK;
+}
/*
* XPath:
diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c
index b5fde5f03e..19ad3769eb 100644
--- a/zebra/zebra_nb_state.c
+++ b/zebra/zebra_nb_state.c
@@ -525,54 +525,57 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys(
{
struct nexthop *nexthop = (struct nexthop *)args->list_entry;
- args->keys->num = 3;
+ args->keys->num = 4;
strlcpy(args->keys->key[0], yang_nexthop_type2str(nexthop->type),
sizeof(args->keys->key[0]));
+ snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32,
+ nexthop->vrf_id);
+
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
- snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]),
+ snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]),
"%pI4", &nexthop->gate.ipv4);
if (nexthop->ifindex)
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
else
/* no ifindex */
- strlcpy(args->keys->key[2], " ",
- sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ",
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
- snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]),
+ snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]),
"%pI6", &nexthop->gate.ipv6);
if (nexthop->ifindex)
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
else
/* no ifindex */
- strlcpy(args->keys->key[2], " ",
- sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ",
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_IFINDEX:
- strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1]));
- strlcpy(args->keys->key[2],
+ strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3],
ifindex2ifname(nexthop->ifindex, nexthop->vrf_id),
- sizeof(args->keys->key[2]));
+ sizeof(args->keys->key[3]));
break;
case NEXTHOP_TYPE_BLACKHOLE:
/* Gateway IP */
- strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1]));
- strlcpy(args->keys->key[2], " ", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2]));
+ strlcpy(args->keys->key[3], " ", sizeof(args->keys->key[3]));
break;
default:
break;
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
index 4e504c48b0..995fa6fb5a 100644
--- a/zebra/zebra_netns_notify.c
+++ b/zebra/zebra_netns_notify.c
@@ -185,7 +185,7 @@ static bool zebra_ns_notify_is_default_netns(const char *name)
{
struct stat default_netns_stat;
struct stat st;
- char netnspath[64];
+ char netnspath[PATH_MAX];
if (zebra_ns_notify_self_identify(&default_netns_stat))
return false;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 9bfd7aacb7..c058090844 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -1951,8 +1951,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
goto done_with_match;
}
- /* Examine installed nexthops */
- nhg = &match->nhe->nhg;
+ /* Examine installed nexthops; note that there
+ * may not be any installed primary nexthops if
+ * only backups are installed.
+ */
+ nhg = rib_get_fib_nhg(match);
for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
if (!nexthop_valid_resolve(nexthop, newhop))
continue;
@@ -1973,8 +1976,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
* dedicated fib list.
*/
nhg = rib_get_fib_backup_nhg(match);
- if (nhg == NULL ||
- nhg == zebra_nhg_get_backup_nhg(match->nhe))
+ if (nhg == NULL || nhg->nexthop == NULL)
goto done_with_match;
for (ALL_NEXTHOPS_PTR(nhg, newhop)) {
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index c5a7795273..1bc8d893bc 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -336,14 +336,20 @@ bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2)
return true;
}
-void zebra_pbr_iptable_free(void *arg)
+/* this function gives option to flush plugin memory contexts
+ * with all parameter. set it to true to flush all
+ * set it to false to flush only passed arg argument
+ */
+static void _zebra_pbr_iptable_free_all(void *arg, bool all)
{
struct zebra_pbr_iptable *iptable;
struct listnode *node, *nnode;
char *name;
iptable = (struct zebra_pbr_iptable *)arg;
- hook_call(zebra_pbr_iptable_update, 0, iptable);
+
+ if (all)
+ hook_call(zebra_pbr_iptable_update, 0, iptable);
if (iptable->interface_name_list) {
for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node,
@@ -356,6 +362,11 @@ void zebra_pbr_iptable_free(void *arg)
XFREE(MTYPE_TMP, iptable);
}
+void zebra_pbr_iptable_free(void *arg)
+{
+ _zebra_pbr_iptable_free_all(arg, false);
+}
+
uint32_t zebra_pbr_iptable_hash_key(const void *arg)
{
const struct zebra_pbr_iptable *iptable = arg;
@@ -529,7 +540,7 @@ static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data)
if (iptable->sock == *sock) {
if (hash_release(zrouter.iptable_hash, iptable))
- zebra_pbr_iptable_free(iptable);
+ _zebra_pbr_iptable_free_all(iptable, true);
else
hook_call(zebra_pbr_iptable_update, 0, iptable);
}
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index 4bc0f40037..dd2d7a190d 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -54,6 +54,8 @@ struct zebra_pbr_rule {
(r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT)
#define IS_RULE_FILTERING_ON_DST_PORT(r) \
(r->rule.filter.filter_bm & PBR_FILTER_DST_PORT)
+#define IS_RULE_FILTERING_ON_DSFIELD(r) \
+ (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD)
#define IS_RULE_FILTERING_ON_FWMARK(r) \
(r->rule.filter.filter_bm & PBR_FILTER_FWMARK)
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index c00cd88fe3..cdcca1e930 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -517,6 +517,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty)
struct zebra_pw *pw;
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
zvrf = vrf_info_lookup(VRF_DEFAULT);
if (!zvrf)
@@ -544,22 +545,41 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty)
vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid);
vty_out(vty, " Status: %s \n",
(zebra_pw_enabled(pw) && pw->status == PW_FORWARDING)
- ? "Up"
- : "Down");
+ ? "Up"
+ : "Down");
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
&pw->nexthop, NULL);
- if (re) {
- for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {
- snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
- nexthop);
- vty_out(vty, " Next Hop: %s\n", buf_nh);
- if (nexthop->nh_label)
- vty_out(vty, " Next Hop label: %u\n",
- nexthop->nh_label->label[0]);
- else
- vty_out(vty, " Next Hop label: %s\n",
- "-");
- }
+ if (re == NULL)
+ continue;
+
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
+ nexthop);
+ vty_out(vty, " Next Hop: %s\n", buf_nh);
+ if (nexthop->nh_label)
+ vty_out(vty, " Next Hop label: %u\n",
+ nexthop->nh_label->label[0]);
+ else
+ vty_out(vty, " Next Hop label: %s\n",
+ "-");
+ }
+
+ /* Include any installed backups */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg == NULL)
+ continue;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",
+ nexthop);
+ vty_out(vty, " Next Hop: %s\n", buf_nh);
+ if (nexthop->nh_label)
+ vty_out(vty, " Next Hop label: %u\n",
+ nexthop->nh_label->label[0]);
+ else
+ vty_out(vty, " Next Hop label: %s\n",
+ "-");
}
}
}
@@ -568,6 +588,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
{
struct route_entry *re;
struct nexthop *nexthop;
+ struct nexthop_group *nhg;
char buf_nbr[INET6_ADDRSTRLEN];
char buf_nh[100];
json_object *json_pw = NULL;
@@ -602,23 +623,48 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)
: "Down");
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
&pw->nexthop, NULL);
- if (re) {
- for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {
- json_nexthop = json_object_new_object();
- snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
- json_object_string_add(json_nexthop, "nexthop", buf_nh);
- if (nexthop->nh_label)
- json_object_int_add(
- json_nexthop, "nhLabel",
- nexthop->nh_label->label[0]);
- else
- json_object_string_add(json_nexthop, "nhLabel",
- "-");
+ if (re == NULL)
+ goto done;
+
+ nhg = rib_get_fib_nhg(re);
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ json_nexthop = json_object_new_object();
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
+ json_object_string_add(json_nexthop, "nexthop", buf_nh);
+ if (nexthop->nh_label)
+ json_object_int_add(
+ json_nexthop, "nhLabel",
+ nexthop->nh_label->label[0]);
+ else
+ json_object_string_add(json_nexthop, "nhLabel",
+ "-");
- json_object_array_add(json_nexthops, json_nexthop);
- }
- json_object_object_add(json_pw, "nexthops", json_nexthops);
+ json_object_array_add(json_nexthops, json_nexthop);
+ }
+
+ /* Include installed backup nexthops also */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg == NULL)
+ goto done;
+
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ json_nexthop = json_object_new_object();
+ snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);
+ json_object_string_add(json_nexthop, "nexthop", buf_nh);
+ if (nexthop->nh_label)
+ json_object_int_add(
+ json_nexthop, "nhLabel",
+ nexthop->nh_label->label[0]);
+ else
+ json_object_string_add(json_nexthop, "nhLabel",
+ "-");
+
+ json_object_array_add(json_nexthops, json_nexthop);
}
+
+done:
+
+ json_object_object_add(json_pw, "nexthops", json_nexthops);
json_object_array_add(json_pws, json_pw);
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 15c491ae72..ee9d5d76e0 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -472,6 +472,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
SET_FLAG(old->status, ROUTE_ENTRY_QUEUED);
/* Free old FIB nexthop group */
+ UNSET_FLAG(old->status, ROUTE_ENTRY_USE_FIB_NHG);
if (old->fib_ng.nexthop) {
nexthops_free(old->fib_ng.nexthop);
old->fib_ng.nexthop = NULL;
@@ -574,6 +575,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
nexthops_free(re->fib_ng.nexthop);
re->fib_ng.nexthop = NULL;
}
+ UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -1375,7 +1377,7 @@ static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg,
continue;
/* Check for a FIB nexthop corresponding to the RIB nexthop */
- if (nexthop_same(ctx_nexthop, nexthop) == false) {
+ if (!nexthop_same(ctx_nexthop, nexthop)) {
/* If the FIB doesn't know about the nexthop,
* it's not installed
*/
@@ -1490,7 +1492,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
VRF_LOGNAME(vrf), re->vrf_id, dest_str);
goto check_backups;
- } else if (re->fib_ng.nexthop) {
+ } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) {
/*
* Free stale fib list and move on to check the rib nhg.
*/
@@ -1501,6 +1503,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
nexthops_free(re->fib_ng.nexthop);
re->fib_ng.nexthop = NULL;
+ UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
+
/* Note that the installed nexthops have changed */
changed_p = true;
} else {
@@ -1546,20 +1550,15 @@ no_nexthops:
*/
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug(
- "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg",
+ "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg%s",
VRF_LOGNAME(vrf), re->vrf_id, dest_str,
- (changed_p ? "true" : "false"));
+ (changed_p ? "true" : "false"),
+ ctxnhg->nexthop != NULL ? "" : " (empty)");
+ /* Set the flag about the dedicated fib list */
+ SET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG);
if (ctxnhg->nexthop)
copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL);
- else {
- /* Bit of a special case when the fib has _no_ installed
- * nexthops.
- */
- nexthop = nexthop_new();
- nexthop->type = NEXTHOP_TYPE_IPV4;
- _nexthop_add(&(re->fib_ng.nexthop), nexthop);
- }
check_backups:
@@ -1611,7 +1610,7 @@ check_backups:
}
/*
- * If a FIB backup nexthop set exists: attach a copy
+ * If a FIB backup nexthop set exists, attach a copy
* to the route if any backup is installed
*/
if (ctxnhg && ctxnhg->nexthop) {
@@ -2632,6 +2631,8 @@ static void _route_entry_dump_nh(const struct route_entry *re,
char nhname[PREFIX_STRLEN];
char backup_str[50];
char wgt_str[50];
+ char temp_str[10];
+ int i;
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
@@ -2657,8 +2658,12 @@ static void _route_entry_dump_nh(const struct route_entry *re,
backup_str[0] = '\0';
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- snprintf(backup_str, sizeof(backup_str), "backup %d,",
- (int)nexthop->backup_idx);
+ snprintf(backup_str, sizeof(backup_str), "backup ");
+ for (i = 0; i < nexthop->backup_num; i++) {
+ snprintf(temp_str, sizeof(temp_str), "%d, ",
+ nexthop->backup_idx[i]);
+ strlcat(backup_str, temp_str, sizeof(backup_str));
+ }
}
wgt_str[0] = '\0';
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index b8b245cebd..b151e90a92 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -1040,22 +1040,10 @@ static bool compare_valid_nexthops(struct route_entry *r1,
* backups will be in the 'fib' list.
*/
nhg1 = rib_get_fib_backup_nhg(r1);
- if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe))
- nhg1 = NULL;
-
nhg2 = rib_get_fib_backup_nhg(r2);
- if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe))
- nhg2 = NULL;
-
- if (nhg1)
- nh1 = nhg1->nexthop;
- else
- nh1 = NULL;
- if (nhg2)
- nh2 = nhg2->nexthop;
- else
- nh2 = NULL;
+ nh1 = nhg1->nexthop;
+ nh2 = nhg2->nexthop;
while (1) {
/* Find each backup list's next valid nexthop */
@@ -1180,9 +1168,6 @@ static int send_client(struct rnh *rnh, struct zserv *client,
}
nhg = rib_get_fib_backup_nhg(re);
- if (nhg == zebra_nhg_get_backup_nhg(re->nhe))
- nhg = NULL;
-
if (nhg) {
for (ALL_NEXTHOPS_PTR(nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index b18871dd2a..2ca57f1c56 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -55,6 +55,7 @@
#include "zebra/zebra_nhg.h"
#include "zebra/interface.h"
#include "northbound_cli.h"
+#include "zebra/zebra_nb.h"
extern int allow_delete;
@@ -71,6 +72,12 @@ static void vty_show_ip_route_summary(struct vty *vty,
static void vty_show_ip_route_summary_prefix(struct vty *vty,
struct route_table *table,
bool use_json);
+/* Helper api to format a nexthop in the 'detailed' output path. */
+static void show_nexthop_detail_helper(struct vty *vty,
+ const struct route_entry *re,
+ const struct nexthop *nexthop,
+ bool is_backup);
+
DEFUN (ip_multicast_mode,
ip_multicast_mode_cmd,
@@ -166,11 +173,24 @@ DEFUN (show_ip_rpf_addr,
}
static char re_status_output_char(const struct route_entry *re,
- const struct nexthop *nhop)
+ const struct nexthop *nhop,
+ bool is_fib)
{
if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) {
- if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) &&
- !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
+ bool star_p = false;
+
+ if (nhop &&
+ !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) &&
+ !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) {
+ /* More-specific test for 'fib' output */
+ if (is_fib) {
+ star_p = !!CHECK_FLAG(nhop->flags,
+ NEXTHOP_FLAG_FIB);
+ } else
+ star_p = true;
+ }
+
+ if (star_p)
return '*';
else
return ' ';
@@ -190,19 +210,51 @@ static char re_status_output_char(const struct route_entry *re,
}
/*
- * TODO -- Show backup nexthop info
+ * Show backup nexthop info, in the 'detailed' output path
*/
static void show_nh_backup_helper(struct vty *vty,
- const struct nhg_hash_entry *nhe,
+ const struct route_entry *re,
const struct nexthop *nexthop)
{
+ const struct nexthop *start, *backup, *temp;
+ int i, idx;
+
/* Double-check that there _is_ a backup */
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) ||
+ re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL ||
+ re->nhe->backup_info->nhe->nhg.nexthop == NULL)
return;
- /* Locate the backup nexthop */
+ /* Locate the backup nexthop(s) */
+ start = re->nhe->backup_info->nhe->nhg.nexthop;
+ for (i = 0; i < nexthop->backup_num; i++) {
+ /* Format the backup(s) (indented) */
+ backup = start;
+ for (idx = 0; idx < nexthop->backup_idx[i]; idx++) {
+ backup = backup->next;
+ if (backup == NULL)
+ break;
+ }
+
+ /* It's possible for backups to be recursive too,
+ * so walk the recursive resolution list if present.
+ */
+ temp = backup;
+ while (backup) {
+ vty_out(vty, " ");
+ show_nexthop_detail_helper(vty, re, backup,
+ true /*backup*/);
+ vty_out(vty, "\n");
- /* Format the backup (indented) */
+ if (backup->resolved && temp == backup)
+ backup = backup->resolved;
+ else
+ backup = nexthop_next(backup);
+
+ if (backup == temp->next)
+ break;
+ }
+ }
}
@@ -212,14 +264,20 @@ static void show_nh_backup_helper(struct vty *vty,
*/
static void show_nexthop_detail_helper(struct vty *vty,
const struct route_entry *re,
- const struct nexthop *nexthop)
+ const struct nexthop *nexthop,
+ bool is_backup)
{
char addrstr[32];
char buf[MPLS_LABEL_STRLEN];
+ int i;
- vty_out(vty, " %c%s",
- re_status_output_char(re, nexthop),
- nexthop->rparent ? " " : "");
+ if (is_backup)
+ vty_out(vty, " b%s",
+ nexthop->rparent ? " " : "");
+ else
+ vty_out(vty, " %c%s",
+ re_status_output_char(re, nexthop, false),
+ nexthop->rparent ? " " : "");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -333,6 +391,13 @@ static void show_nexthop_detail_helper(struct vty *vty,
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
+
+ for (i = 1; i < nexthop->backup_num; i++)
+ vty_out(vty, ",%d", nexthop->backup_idx[i]);
+ }
}
/* New RIB. Detailed information for IPv4 route. */
@@ -403,12 +468,13 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
/* Use helper to format each nexthop */
- show_nexthop_detail_helper(vty, re, nexthop);
+ show_nexthop_detail_helper(vty, re, nexthop,
+ false /*not backup*/);
vty_out(vty, "\n");
- /* Include backup info, if present */
+ /* Include backup(s), if present */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- show_nh_backup_helper(vty, re->nhe, nexthop);
+ show_nh_backup_helper(vty, re, nexthop);
}
vty_out(vty, "\n");
}
@@ -422,6 +488,7 @@ static void show_route_nexthop_helper(struct vty *vty,
const struct nexthop *nexthop)
{
char buf[MPLS_LABEL_STRLEN];
+ int i;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -519,8 +586,12 @@ static void show_route_nexthop_helper(struct vty *vty,
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, ", backup %d", nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
+
+ for (i = 1; i < nexthop->backup_num; i++)
+ vty_out(vty, ",%d", nexthop->backup_idx[i]);
+ }
}
/*
@@ -534,6 +605,8 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
char buf[SRCDEST2STR_BUFFER];
struct vrf *vrf = NULL;
json_object *json_labels = NULL;
+ json_object *json_backups = NULL;
+ int i;
json_object_int_add(json_nexthop, "flags",
nexthop->flags);
@@ -645,9 +718,17 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
json_object_boolean_true_add(json_nexthop,
"recursive");
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(json_nexthop, "backupIndex",
- nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nexthop->backup_num; i++) {
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nexthop->backup_idx[i]));
+ }
+
+ json_object_object_add(json_nexthop, "backupIndex",
+ json_backups);
+ }
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
@@ -705,18 +786,19 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
struct route_entry *re, json_object *json,
bool is_fib)
{
- struct nexthop *nexthop;
+ const struct nexthop *nexthop;
int len = 0;
char buf[SRCDEST2STR_BUFFER];
json_object *json_nexthops = NULL;
json_object *json_nexthop = NULL;
json_object *json_route = NULL;
time_t uptime;
- struct vrf *vrf = NULL;
- rib_dest_t *dest = rib_dest_from_rnode(rn);
- struct nexthop_group *nhg;
+ const struct vrf *vrf = NULL;
+ const rib_dest_t *dest = rib_dest_from_rnode(rn);
+ const struct nexthop_group *nhg;
char up_str[MONOTIME_STRLEN];
- bool first_p;
+ bool first_p = true;
+ bool nhg_from_backup = false;
uptime = monotime(NULL);
uptime -= re->uptime;
@@ -791,9 +873,11 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
json_nexthop = json_object_new_object();
+ show_nexthop_json_helper(json_nexthop,
+ nexthop, re);
- show_nexthop_json_helper(json_nexthop, nexthop, re);
- json_object_array_add(json_nexthops, json_nexthop);
+ json_object_array_add(json_nexthops,
+ json_nexthop);
}
json_object_object_add(json_route, "nexthops", json_nexthops);
@@ -804,7 +888,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
else
nhg = zebra_nhg_get_backup_nhg(re->nhe);
- if (nhg) {
+ if (nhg && nhg->nexthop) {
json_nexthops = json_object_new_array();
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
@@ -824,42 +908,62 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
return;
}
+ /* Prefix information, and first nexthop. If we're showing 'fib',
+ * and there are no installed primary nexthops, see if there are any
+ * backup nexthops and start with those.
+ */
+ if (is_fib && nhg->nexthop == NULL) {
+ nhg = rib_get_fib_backup_nhg(re);
+ nhg_from_backup = true;
+ }
+
+ len = vty_out(vty, "%c", zebra_route_char(re->type));
+ if (re->instance)
+ len += vty_out(vty, "[%d]", re->instance);
+ if (nhg_from_backup && nhg->nexthop) {
+ len += vty_out(
+ vty, "%cb%c %s",
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
+ re_status_output_char(re, nhg->nexthop, is_fib),
+ srcdest_rnode2str(rn, buf, sizeof(buf)));
+ } else {
+ len += vty_out(
+ vty, "%c%c %s",
+ CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ',
+ re_status_output_char(re, nhg->nexthop, is_fib),
+ srcdest_rnode2str(rn, buf, sizeof(buf)));
+ }
+
+ /* Distance and metric display. */
+ if (((re->type == ZEBRA_ROUTE_CONNECT) &&
+ (re->distance || re->metric)) ||
+ (re->type != ZEBRA_ROUTE_CONNECT))
+ len += vty_out(vty, " [%u/%u]", re->distance,
+ re->metric);
+
/* Nexthop information. */
- first_p = true;
for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
if (first_p) {
first_p = false;
-
- /* Prefix information. */
- len = vty_out(vty, "%c", zebra_route_char(re->type));
- if (re->instance)
- len += vty_out(vty, "[%d]", re->instance);
- len += vty_out(
- vty, "%c%c %s",
- CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)
- ? '>'
- : ' ',
- re_status_output_char(re, nexthop),
- srcdest_rnode2str(rn, buf, sizeof(buf)));
-
- /* Distance and metric display. */
- if (((re->type == ZEBRA_ROUTE_CONNECT) &&
- (re->distance || re->metric)) ||
- (re->type != ZEBRA_ROUTE_CONNECT))
- len += vty_out(vty, " [%u/%u]", re->distance,
- re->metric);
+ } else if (nhg_from_backup) {
+ vty_out(vty, " b%c%*c",
+ re_status_output_char(re, nexthop, is_fib),
+ len - 3 + (2 * nexthop_level(nexthop)), ' ');
} else {
vty_out(vty, " %c%*c",
- re_status_output_char(re, nexthop),
+ re_status_output_char(re, nexthop, is_fib),
len - 3 + (2 * nexthop_level(nexthop)), ' ');
}
show_route_nexthop_helper(vty, re, nexthop);
-
vty_out(vty, ", %s\n", up_str);
}
- /* Check for backup info if present */
+ /* If we only had backup nexthops, we're done */
+ if (nhg_from_backup)
+ return;
+
+ /* Check for backup nexthop info if present */
if (is_fib)
nhg = rib_get_fib_backup_nhg(re);
else
@@ -1206,7 +1310,7 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
if (CHECK_FLAG(nexthop->flags,
NEXTHOP_FLAG_HAS_BACKUP))
vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
+ nexthop->backup_idx[0]);
vty_out(vty, "\n");
continue;
@@ -1214,22 +1318,13 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe)
/* TODO -- print more useful backup info */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- struct nexthop *backup;
int i;
- i = 0;
- for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) {
- if (i == nexthop->backup_idx)
- break;
- i++;
- }
+ vty_out(vty, "[backup");
+ for (i = 0; i < nexthop->backup_num; i++)
+ vty_out(vty, " %d", nexthop->backup_idx[i]);
- /* TODO */
- if (backup)
- vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
- else
- vty_out(vty, " [backup INVALID]");
+ vty_out(vty, "]");
}
vty_out(vty, "\n");
@@ -2304,12 +2399,9 @@ DEFUN (vrf_vni_mapping,
"VNI-ID\n"
"prefix-routes-only\n")
{
- int ret = 0;
int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
- vni_t vni = strtoul(argv[1]->arg, NULL, 10);
- char err[ERR_STR_SZ];
assert(vrf);
assert(zvrf);
@@ -2317,14 +2409,15 @@ DEFUN (vrf_vni_mapping,
if (argc == 3)
filter = 1;
- /* Mark as having FRR configuration */
- vrf_set_user_cfged(vrf);
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
- filter, 1);
- if (ret != 0) {
- vty_out(vty, "%s\n", err);
- return CMD_WARNING;
- }
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL);
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY,
+ argv[1]->arg);
+
+ if (filter)
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
+ NB_OP_MODIFY, "true");
+
+ nb_cli_apply_changes(vty, NULL);
return CMD_SUCCESS;
}
@@ -2337,12 +2430,10 @@ DEFUN (no_vrf_vni_mapping,
"VNI-ID\n"
"prefix-routes-only\n")
{
- int ret = 0;
int filter = 0;
- char err[ERR_STR_SZ];
- vni_t vni = strtoul(argv[2]->arg, NULL, 10);
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
+ vni_t vni = strtoul(argv[1]->arg, NULL, 10);
assert(vrf);
assert(zvrf);
@@ -2350,16 +2441,22 @@ DEFUN (no_vrf_vni_mapping,
if (argc == 4)
filter = 1;
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err,
- ERR_STR_SZ, filter, 0);
- if (ret != 0) {
- vty_out(vty, "%s\n", err);
+ if (zvrf->l3vni != vni) {
+ vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni,
+ zvrf->vrf->name);
return CMD_WARNING;
}
- /* If no other FRR config for this VRF, mark accordingly. */
- if (!zebra_vrf_has_config(zvrf))
- vrf_reset_user_cfged(vrf);
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY,
+ argv[2]->arg);
+
+ if (filter)
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only",
+ NB_OP_DESTROY, "true");
+
+ nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL);
+
+ nb_cli_apply_changes(vty, NULL);
return CMD_SUCCESS;
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 66441e1756..1bb673c940 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -142,7 +142,6 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac);
/* l3-vni related APIs*/
-static zebra_l3vni_t *zl3vni_lookup(vni_t vni);
static void *zl3vni_alloc(void *p);
static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id);
static int zl3vni_del(zebra_l3vni_t *zl3vni);
@@ -5110,7 +5109,7 @@ static void *zl3vni_alloc(void *p)
/*
* Look up L3 VNI hash entry.
*/
-static zebra_l3vni_t *zl3vni_lookup(vni_t vni)
+zebra_l3vni_t *zl3vni_lookup(vni_t vni)
{
zebra_l3vni_t tmp_l3vni;
zebra_l3vni_t *zl3vni = NULL;
@@ -9475,8 +9474,15 @@ int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf)
if (!zl3vni)
return 0;
- zl3vni->vrf_id = VRF_UNKNOWN;
zebra_vxlan_process_l3vni_oper_down(zl3vni);
+
+ /* delete and uninstall all rmacs */
+ hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry, zl3vni);
+ /* delete and uninstall all next-hops */
+ hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry, zl3vni);
+
+ zl3vni->vrf_id = VRF_UNKNOWN;
+
return 0;
}
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index d2b02df2ad..e4b06054b2 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -476,6 +476,7 @@ extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id);
extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni);
+extern zebra_l3vni_t *zl3vni_lookup(vni_t vni);
DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason), (rmac, zl3vni, delete, reason))