summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c184
-rw-r--r--bgpd/bgp_attr.h2
-rw-r--r--bgpd/bgp_route.c6
-rw-r--r--bgpd/bgp_updgrp.c9
-rw-r--r--bgpd/bgp_vty.c38
-rw-r--r--bgpd/bgpd.c21
-rw-r--r--bgpd/bgpd.h6
-rw-r--r--doc/user/bfd.rst48
-rw-r--r--doc/user/bgp.rst7
-rw-r--r--lib/bfd.c451
-rw-r--r--lib/bfd.h17
-rw-r--r--lib/command.h7
-rw-r--r--staticd/static_bfd.c390
-rw-r--r--staticd/static_debug.c15
-rw-r--r--staticd/static_debug.h4
-rw-r--r--staticd/static_nb.c27
-rw-r--r--staticd/static_nb.h7
-rw-r--r--staticd/static_nb_config.c107
-rw-r--r--staticd/static_routes.c6
-rw-r--r--staticd/static_routes.h28
-rw-r--r--staticd/static_vty.c208
-rw-r--r--staticd/static_zebra.c4
-rw-r--r--staticd/subdir.am2
-rw-r--r--tests/topotests/bfd_topo3/r3/bfd-static-down.json12
-rw-r--r--tests/topotests/bfd_topo3/r3/bfd-static.json13
-rw-r--r--tests/topotests/bfd_topo3/r3/staticd.conf1
-rw-r--r--tests/topotests/bfd_topo3/r4/bfd-peers.json48
-rw-r--r--tests/topotests/bfd_topo3/r4/bgpd.conf1
-rw-r--r--tests/topotests/bfd_topo3/r4/staticd.conf2
-rw-r--r--tests/topotests/bfd_topo3/r4/zebra.conf4
-rw-r--r--tests/topotests/bfd_topo3/r5/bfd-peers.json23
-rw-r--r--tests/topotests/bfd_topo3/r5/bfdd.conf11
-rw-r--r--tests/topotests/bfd_topo3/r5/staticd.conf2
-rw-r--r--tests/topotests/bfd_topo3/r5/zebra.conf10
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-peers.json46
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-static-down.json19
-rw-r--r--tests/topotests/bfd_topo3/r6/bfd-static.json19
-rw-r--r--tests/topotests/bfd_topo3/r6/bfdd.conf11
-rw-r--r--tests/topotests/bfd_topo3/r6/staticd.conf5
-rw-r--r--tests/topotests/bfd_topo3/r6/zebra.conf10
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.dot22
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.jpgbin34705 -> 42789 bytes
-rw-r--r--tests/topotests/bfd_topo3/test_bfd_topo3.py90
-rw-r--r--tests/topotests/bgp_path_attribute_discard/__init__.py0
-rw-r--r--tests/topotests/bgp_path_attribute_discard/exabgp.env53
-rw-r--r--tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg24
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_path_attribute_discard/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py159
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf23
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf1
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf15
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py48
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py407
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py932
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py932
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py1909
-rw-r--r--tests/topotests/route_scale/scale_test_common.py2
-rwxr-xr-xtools/frr-reload.py2
-rw-r--r--yang/frr-bfdd.yang30
-rw-r--r--yang/frr-staticd.yang18
-rw-r--r--zebra/zebra_ptm.h14
63 files changed, 4541 insertions, 1987 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 908c024e9d..4306d3bd12 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -33,6 +33,7 @@
#include "filter.h"
#include "command.h"
#include "srv6.h"
+#include "frrstr.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@@ -1850,6 +1851,7 @@ stream_failure:
/* Atomic aggregate. */
static int bgp_attr_atomic(struct bgp_attr_parser_args *args)
{
+ struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
@@ -1862,10 +1864,22 @@ static int bgp_attr_atomic(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto atomic_ignore;
+
/* Set atomic aggregate flag. */
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
return BGP_ATTR_PARSE_PROCEED;
+
+atomic_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Aggregator attribute */
@@ -1891,6 +1905,9 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto aggregator_ignore;
+
if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV))
aggregator_as = stream_getl(peer->curr);
else
@@ -1917,6 +1934,15 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
}
return BGP_ATTR_PARSE_PROCEED;
+
+aggregator_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* New Aggregator attribute */
@@ -1937,6 +1963,9 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
0);
}
+ if (peer->discard_attrs[args->type])
+ goto as4_aggregator_ignore;
+
aggregator_as = stream_getl(peer->curr);
*as4_aggregator_as = aggregator_as;
@@ -1960,6 +1989,15 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
}
return BGP_ATTR_PARSE_PROCEED;
+
+as4_aggregator_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
@@ -2079,6 +2117,9 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto community_ignore;
+
bgp_attr_set_community(
attr,
community_parse((uint32_t *)stream_pnt(peer->curr), length));
@@ -2094,6 +2135,15 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Originator ID attribute. */
@@ -2117,11 +2167,23 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto originator_id_ignore;
+
attr->originator_id.s_addr = stream_get_ipv4(peer->curr);
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
return BGP_ATTR_PARSE_PROCEED;
+
+originator_id_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Cluster list attribute. */
@@ -2144,6 +2206,9 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto cluster_list_ignore;
+
bgp_attr_set_cluster(
attr, cluster_parse((struct in_addr *)stream_pnt(peer->curr),
length));
@@ -2154,6 +2219,15 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST);
return BGP_ATTR_PARSE_PROCEED;
+
+cluster_list_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Multiprotocol reachability information parse. */
@@ -2413,6 +2487,9 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto large_community_ignore;
+
bgp_attr_set_lcommunity(
attr, lcommunity_parse(stream_pnt(peer->curr), length));
/* XXX: fix ecommunity_parse to use stream API */
@@ -2423,6 +2500,15 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+large_community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Extended Community attribute. */
@@ -2514,6 +2600,9 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto ipv6_ext_community_ignore;
+
ipv6_ecomm = ecommunity_parse_ipv6(
stream_pnt(peer->curr), length,
CHECK_FLAG(peer->flags,
@@ -2528,6 +2617,15 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
args->total);
return BGP_ATTR_PARSE_PROCEED;
+
+ipv6_ext_community_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* Parse Tunnel Encap attribute in an UPDATE */
@@ -3202,6 +3300,9 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args)
goto aigp_ignore;
}
+ if (peer->discard_attrs[args->type])
+ goto aigp_ignore;
+
if (!bgp_attr_aigp_valid(s, length))
goto aigp_ignore;
@@ -3212,6 +3313,10 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args)
aigp_ignore:
stream_forward_getp(peer->curr, length);
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
return BGP_ATTR_PARSE_PROCEED;
}
@@ -3230,6 +3335,9 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
args->total);
}
+ if (peer->discard_attrs[args->type])
+ goto otc_ignore;
+
attr->otc = stream_getl(peer->curr);
if (!attr->otc) {
flog_err(EC_BGP_ATTR_MAL_AS_PATH, "OTC attribute value is 0");
@@ -3240,6 +3348,15 @@ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
return BGP_ATTR_PARSE_PROCEED;
+
+otc_ignore:
+ stream_forward_getp(peer->curr, length);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
}
/* BGP unknown attribute treatment. */
@@ -3263,6 +3380,14 @@ bgp_attr_unknown(struct bgp_attr_parser_args *args)
/* Forward read pointer of input stream. */
stream_forward_getp(peer->curr, length);
+ if (peer->discard_attrs[type]) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("%pBP: Ignoring attribute %s", peer,
+ lookup_msg(attr_str, args->type, NULL));
+
+ return BGP_ATTR_PARSE_PROCEED;
+ }
+
/* If any of the mandatory well-known attributes are not recognized,
then the Error Subcode is set to Unrecognized Well-known
Attribute. The Data field contains the unrecognized attribute
@@ -4970,3 +5095,62 @@ void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi,
len = stream_get_endp(s) - cp - 2;
stream_putw_at(s, cp, len);
}
+
+void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer,
+ const char *discard_attrs)
+{
+ int i, num_attributes;
+ char **attributes;
+ afi_t afi;
+ safi_t safi;
+
+ if (discard_attrs) {
+ frrstr_split(discard_attrs, " ", &attributes, &num_attributes);
+
+ for (i = 0; i < BGP_ATTR_MAX; i++)
+ peer->discard_attrs[i] = false;
+
+ for (i = 0; i < num_attributes; i++) {
+ uint8_t attr_num = strtoul(attributes[i], NULL, 10);
+
+ XFREE(MTYPE_TMP, attributes[i]);
+
+ /* Some of the attributes, just can't be ignored. */
+ if (attr_num == BGP_ATTR_ORIGIN ||
+ attr_num == BGP_ATTR_AS_PATH ||
+ attr_num == BGP_ATTR_NEXT_HOP ||
+ attr_num == BGP_ATTR_MULTI_EXIT_DISC ||
+ attr_num == BGP_ATTR_MP_REACH_NLRI ||
+ attr_num == BGP_ATTR_MP_UNREACH_NLRI ||
+ attr_num == BGP_ATTR_EXT_COMMUNITIES) {
+ vty_out(vty,
+ "%% Can't discard path-attribute %s, ignoring.\n",
+ lookup_msg(attr_str, attr_num, NULL));
+ continue;
+ }
+
+ /* Ignore local-pref, originator-id, cluster-list only
+ * for eBGP.
+ */
+ if (peer->sort != BGP_PEER_EBGP &&
+ (attr_num == BGP_ATTR_LOCAL_PREF ||
+ attr_num == BGP_ATTR_ORIGINATOR_ID ||
+ attr_num == BGP_ATTR_CLUSTER_LIST)) {
+ vty_out(vty,
+ "%% Can discard path-attribute %s only for eBGP, ignoring.\n",
+ lookup_msg(attr_str, attr_num, NULL));
+ continue;
+ }
+
+ peer->discard_attrs[attr_num] = true;
+ }
+ XFREE(MTYPE_TMP, attributes);
+
+ /* Configuring path attributes to be discarded will trigger
+ * an inbound Route Refresh to ensure that the routing table
+ * is up to date.
+ */
+ FOREACH_AFI_SAFI (afi, safi)
+ peer_clear_soft(peer, afi, safi, BGP_CLEAR_SOFT_IN);
+ }
+}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index a34da1a6de..cb0bf4a43c 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -414,6 +414,8 @@ extern unsigned int attrhash_key_make(const void *p);
extern void attr_show_all(struct vty *vty);
extern unsigned long int attr_count(void);
extern unsigned long int attr_unknown_count(void);
+extern void bgp_path_attribute_discard_vty(struct vty *vty, struct peer *peer,
+ const char *discard_attrs);
/* Cluster list prototypes. */
extern bool cluster_loop_check(struct cluster_list *cluster,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 87871573f0..40f29dac5c 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2188,12 +2188,12 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
}
/* AS path loop check. */
- if (onlypeer && onlypeer->as_path_loop_detection
- && aspath_loop_check(piattr->aspath, onlypeer->as)) {
+ if (peer->as_path_loop_detection &&
+ aspath_loop_check(piattr->aspath, peer->as)) {
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug(
"%pBP [Update:SEND] suppress announcement to peer AS %u that is part of AS path.",
- onlypeer, onlypeer->as);
+ peer, peer->as);
return false;
}
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index afd2107b48..9ca57f08da 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -164,6 +164,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->change_local_as = src->change_local_as;
dst->shared_network = src->shared_network;
dst->local_role = src->local_role;
+ dst->as_path_loop_detection = src->as_path_loop_detection;
if (src->soo[afi][safi]) {
ecommunity_free(&dst->soo[afi][safi]);
@@ -360,6 +361,9 @@ static unsigned int updgrp_hash_key_make(const void *p)
key = jhash_1word(peer->max_packet_size, key);
key = jhash_1word(peer->pmax_out[afi][safi], key);
+ if (peer->as_path_loop_detection)
+ key = jhash_2words(peer->as, peer->as_path_loop_detection, key);
+
if (peer->group)
key = jhash_1word(jhash(peer->group->name,
strlen(peer->group->name), SEED1),
@@ -454,12 +458,13 @@ static unsigned int updgrp_hash_key_make(const void *p)
(intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS),
(intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS));
zlog_debug(
- "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u",
+ "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u, as_path_loop_detection: %d",
peer, (uint32_t)peer->addpath_type[afi][safi],
CHECK_FLAG(peer->cap, PEER_UPDGRP_CAP_FLAGS),
CHECK_FLAG(peer->af_cap[afi][safi],
PEER_UPDGRP_AF_CAP_FLAGS),
- peer->v_routeadv, peer->change_local_as);
+ peer->v_routeadv, peer->change_local_as,
+ peer->as_path_loop_detection);
zlog_debug(
"%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s",
peer, peer->max_packet_size, peer->pmax_out[afi][safi],
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 4acf4f76aa..9724c3533a 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -8808,6 +8808,32 @@ DEFPY(
return CMD_SUCCESS;
}
+DEFPY(neighbor_path_attribute_discard,
+ neighbor_path_attribute_discard_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor path-attribute discard (1-255)...",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Manipulate path attributes from incoming UPDATE messages\n"
+ "Drop specified attributes from incoming UPDATE messages\n"
+ "Attribute number\n")
+{
+ struct peer *peer;
+ int idx = 0;
+ const char *discard_attrs = NULL;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ argv_find(argv, argc, "(1-255)", &idx);
+ if (idx)
+ discard_attrs = argv_concat(argv, argc, idx);
+
+ bgp_path_attribute_discard_vty(vty, peer, discard_attrs);
+
+ return CMD_SUCCESS;
+}
+
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
struct ecommunity **list, bool is_rt6)
{
@@ -17444,6 +17470,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
addr);
+ /* path-attribute discard */
+ char discard_attrs_str[BUFSIZ] = {0};
+ bool discard_attrs = bgp_path_attribute_discard(
+ peer, discard_attrs_str, sizeof(discard_attrs_str));
+
+ if (discard_attrs)
+ vty_out(vty, " neighbor %s path-attribute discard %s\n", addr,
+ discard_attrs_str);
+
if (!CHECK_FLAG(peer->peer_gr_new_status_flag,
PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) {
@@ -19559,6 +19594,9 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd);
install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd);
+ /* "neighbor path-attribute discard" commands. */
+ install_element(BGP_NODE, &neighbor_path_attribute_discard_cmd);
+
/* "neighbor passive" commands. */
install_element(BGP_NODE, &neighbor_passive_cmd);
install_element(BGP_NODE, &no_neighbor_passive_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 9b4aa38d7a..284321c36a 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -3576,7 +3576,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%s: Registering BGP instance %s to zebra",
- __func__, name);
+ __func__, bgp->name_pretty);
bgp_zebra_instance_register(bgp);
}
@@ -4203,6 +4203,25 @@ static void peer_drop_dynamic_neighbor(struct peer *peer)
peer->group->name, dncount);
}
+bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size)
+{
+ if (!buf)
+ return false;
+
+ buf[0] = '\0';
+
+ for (unsigned int i = 0; i < BGP_ATTR_MAX; i++) {
+ if (peer->discard_attrs[i])
+ snprintf(buf + strlen(buf), size - strlen(buf), "%s%d",
+ (strlen(buf) > 0) ? " " : "", i);
+ }
+
+ if (strlen(buf) > 0)
+ return true;
+
+ return false;
+}
+
/* If peer is configured at least one address family return 1. */
bool peer_active(struct peer *peer)
{
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index e4d04cd220..e0bf7e9416 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1806,6 +1806,10 @@ struct peer {
bool shut_during_cfg;
+#define BGP_ATTR_MAX 255
+ /* Path attributes discard */
+ bool discard_attrs[BGP_ATTR_MAX];
+
QOBJ_FIELDS;
};
DECLARE_QOBJ_TYPE(peer);
@@ -2666,6 +2670,8 @@ extern void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi,
safi_t safi);
extern void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
int outbound);
+extern bool bgp_path_attribute_discard(struct peer *peer, char *buf,
+ size_t size);
#ifdef _FRR_ATTRIBUTE_PRINTFRR
/* clang-format off */
#pragma FRR printfrr_ext "%pBP" (struct peer *)
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index 0eb1064519..1a42996771 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -353,6 +353,54 @@ The following commands are available inside the interface configuration node.
that interface.
+.. _bfd-static-peer-config:
+
+BFD Static Route Monitoring Configuration
+-----------------------------------------
+
+A monitored static route conditions the installation to the RIB on the
+BFD session running state: when BFD session is up the route is installed
+to RIB, but when the BFD session is down it is removed from the RIB.
+
+The following commands are available inside the configuration node:
+
+.. clicmd:: ip route A.B.C.D/M A.B.C.D bfd [{multi-hop|source A.B.C.D|profile BFDPROF}]
+
+ Configure a static route for ``A.B.C.D/M`` using gateway ``A.B.C.D`` and use
+ the gateway address as BFD peer destination address.
+
+.. clicmd:: ipv6 route X:X::X:X/M [from X:X::X:X/M] X:X::X:X bfd [{multi-hop|source X:X::X:X|profile BFDPROF}]
+
+ Configure a static route for ``X:X::X:X/M`` using gateway
+ ``X:X::X:X`` and use the gateway address as BFD peer destination
+ address.
+
+The static routes when uninstalled will no longer show up in the output of
+the command ``show ip route`` or ``show ipv6 route``, instead we must use the
+BFD static route show command to see these monitored route status.
+
+.. clicmd:: show bfd static route [json]
+
+ Show all monitored static routes and their status.
+
+ Example output:
+
+ ::
+
+ Showing BFD monitored static routes:
+
+ Route groups:
+ rtg1 peer 172.16.0.1 (status: uninstalled):
+ 2001:db8::100/128
+
+ Next hops:
+ VRF default IPv4 Unicast:
+ 192.168.100.0/24 peer 172.16.0.1 (status: uninstalled)
+
+ VRF default IPv4 Multicast:
+
+ VRF default IPv6 Unicast:
+
.. _bfd-configuration:
Configuration
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 7f97491630..77b4d35afb 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1704,6 +1704,13 @@ Configuring Peers
Default: disabled.
+.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> path-attribute discard (1-255)...
+
+ Drops specified path attributes from BGP UPDATE messages from the specified neighbor.
+
+ If you do not want specific attributes, you can drop them using this command, and
+ let the BGP proceed by ignoring those attributes.
+
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> graceful-shutdown
Mark all routes from this neighbor as less preferred by setting ``graceful-shutdown``
diff --git a/lib/bfd.c b/lib/bfd.c
index 29b8d85d8d..ae402ec5cb 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -29,11 +29,13 @@
#include "stream.h"
#include "vrf.h"
#include "zclient.h"
+#include "libfrr.h"
#include "table.h"
#include "vty.h"
#include "bfd.h"
DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info");
+DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache");
/**
* BFD protocol integration configuration.
@@ -48,6 +50,29 @@ enum bfd_session_event {
};
/**
+ * BFD source selection result cache.
+ *
+ * This structure will keep track of the result based on the destination
+ * prefix. When the result changes all related BFD sessions with automatic
+ * source will be updated.
+ */
+struct bfd_source_cache {
+ /** Address VRF belongs. */
+ vrf_id_t vrf_id;
+ /** Destination network address. */
+ struct prefix address;
+ /** Source selected. */
+ struct prefix source;
+ /** Is the source address valid? */
+ bool valid;
+ /** BFD sessions using this. */
+ size_t refcount;
+
+ SLIST_ENTRY(bfd_source_cache) entry;
+};
+SLIST_HEAD(bfd_source_list, bfd_source_cache);
+
+/**
* Data structure to do the necessary tricks to hide the BFD protocol
* integration internals.
*/
@@ -82,6 +107,11 @@ struct bfd_session_params {
/** BFD session installation state. */
bool installed;
+ /** Automatic source selection. */
+ bool auto_source;
+ /** Currently selected source. */
+ struct bfd_source_cache *source_cache;
+
/** Global BFD paramaters list. */
TAILQ_ENTRY(bfd_session_params) entry;
};
@@ -92,11 +122,15 @@ struct bfd_sessions_global {
* without code duplication among daemons.
*/
TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
+ /** BFD automatic source selection cache. */
+ struct bfd_source_list source_list;
/** Pointer to FRR's event manager. */
struct thread_master *tm;
/** Pointer to zebra client data structure. */
struct zclient *zc;
+ /** Zebra next hop tracking (NHT) client. */
+ struct zclient *nht_zclient;
/** Debugging state. */
bool debugging;
@@ -111,6 +145,34 @@ static struct bfd_sessions_global bsglobal;
static const struct in6_addr i6a_zero;
/*
+ * Prototypes
+ */
+static void bfd_nht_zclient_connect(struct thread *thread);
+
+static void bfd_nht_zclient_connected(struct zclient *zclient);
+static int bfd_nht_update(ZAPI_CALLBACK_ARGS);
+
+static void bfd_source_cache_get(struct bfd_session_params *session);
+static void bfd_source_cache_put(struct bfd_session_params *session);
+
+static inline void
+bfd_source_cache_register(const struct bfd_source_cache *source)
+{
+ zclient_send_rnh(bsglobal.nht_zclient, ZEBRA_NEXTHOP_REGISTER,
+ &source->address, SAFI_UNICAST, false, false,
+ source->vrf_id);
+}
+
+static inline void
+bfd_source_cache_unregister(const struct bfd_source_cache *source)
+{
+ zclient_send_rnh(bsglobal.nht_zclient, ZEBRA_NEXTHOP_UNREGISTER,
+ &source->address, SAFI_UNICAST, false, false,
+ source->vrf_id);
+}
+
+
+/*
* bfd_get_peer_info - Extract the Peer information for which the BFD session
* went down from the message sent from Zebra to clients.
*/
@@ -531,6 +593,8 @@ void bfd_sess_free(struct bfd_session_params **bsp)
/* Remove from global list. */
TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
+ bfd_source_cache_put(*bsp);
+
/* Free the memory and point to NULL. */
XFREE(MTYPE_BFD_INFO, (*bsp));
}
@@ -565,6 +629,8 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
+ /* Address changed so we must reapply auto source. */
+ bfd_source_cache_put(bsp);
bsp->args.family = AF_INET;
@@ -578,6 +644,9 @@ void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
assert(dst);
memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
+
+ if (bsp->auto_source)
+ bfd_source_cache_get(bsp);
}
void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
@@ -589,6 +658,8 @@ void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
+ /* Address changed so we must reapply auto source. */
+ bfd_source_cache_put(bsp);
bsp->args.family = AF_INET6;
@@ -600,6 +671,9 @@ void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
assert(dst);
bsp->args.dst = *dst;
+
+ if (bsp->auto_source)
+ bfd_source_cache_get(bsp);
}
void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
@@ -646,8 +720,13 @@ void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
+ /* Address changed so we must reapply auto source. */
+ bfd_source_cache_put(bsp);
bsp->args.vrf_id = vrf_id;
+
+ if (bsp->auto_source)
+ bfd_source_cache_get(bsp);
}
void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops)
@@ -677,6 +756,18 @@ void bfd_sess_set_timers(struct bfd_session_params *bsp,
bsp->args.min_tx = min_tx;
}
+void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable)
+{
+ if (bsp->auto_source == enable)
+ return;
+
+ bsp->auto_source = enable;
+ if (enable)
+ bfd_source_cache_get(bsp);
+ else
+ bfd_source_cache_put(bsp);
+}
+
void bfd_sess_install(struct bfd_session_params *bsp)
{
bsp->lastev = BSE_INSTALL;
@@ -746,6 +837,11 @@ void bfd_sess_timers(const struct bfd_session_params *bsp,
*min_tx = bsp->args.min_tx;
}
+bool bfd_sess_auto_source(const struct bfd_session_params *bsp)
+{
+ return bsp->auto_source;
+}
+
void bfd_sess_show(struct vty *vty, struct json_object *json,
struct bfd_session_params *bsp)
{
@@ -950,10 +1046,51 @@ int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
return 0;
}
+/**
+ * Frees all allocated resources and stops any activity.
+ *
+ * Must be called after every BFD session has been successfully
+ * unconfigured otherwise this function will `free()` any available
+ * session causing existing pointers to dangle.
+ *
+ * This is just a comment, in practice it will be called by the FRR
+ * library late finish hook. \see `bfd_protocol_integration_init`.
+ */
+static int bfd_protocol_integration_finish(void)
+{
+ if (bsglobal.zc == NULL)
+ return 0;
+
+ while (!TAILQ_EMPTY(&bsglobal.bsplist)) {
+ struct bfd_session_params *session =
+ TAILQ_FIRST(&bsglobal.bsplist);
+ bfd_sess_free(&session);
+ }
+
+ /*
+ * BFD source cache is linked to sessions, if all sessions are gone
+ * then the source cache must be empty.
+ */
+ if (!SLIST_EMPTY(&bsglobal.source_list))
+ zlog_warn("BFD integration source cache not empty");
+
+ zclient_stop(bsglobal.nht_zclient);
+ zclient_free(bsglobal.nht_zclient);
+
+ return 0;
+}
+
+static zclient_handler *const bfd_nht_handlers[] = {
+ [ZEBRA_NEXTHOP_UPDATE] = bfd_nht_update,
+};
+
void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
{
+ struct zclient_options bfd_nht_options = zclient_options_default;
+
/* Initialize data structure. */
TAILQ_INIT(&bsglobal.bsplist);
+ SLIST_INIT(&bsglobal.source_list);
/* Copy pointers. */
bsglobal.zc = zc;
@@ -964,6 +1101,18 @@ void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
/* Send the client registration */
bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
+
+ /* Start NHT client (for automatic source decisions). */
+ bsglobal.nht_zclient =
+ zclient_new(tm, &bfd_nht_options, bfd_nht_handlers,
+ array_size(bfd_nht_handlers));
+ bsglobal.nht_zclient->sock = -1;
+ bsglobal.nht_zclient->privs = zc->privs;
+ bsglobal.nht_zclient->zebra_connected = bfd_nht_zclient_connected;
+ thread_add_timer(tm, bfd_nht_zclient_connect, bsglobal.nht_zclient, 1,
+ &bsglobal.nht_zclient->t_connect);
+
+ hook_register(frr_fini, bfd_protocol_integration_finish);
}
void bfd_protocol_integration_set_debug(bool enable)
@@ -985,3 +1134,305 @@ bool bfd_protocol_integration_shutting_down(void)
{
return bsglobal.shutting_down;
}
+
+/*
+ * BFD automatic source selection
+ *
+ * This feature will use the next hop tracking (NHT) provided by zebra
+ * to find out the source address by looking at the output interface.
+ *
+ * When the interface address / routing table change we'll be notified
+ * and be able to update the source address accordingly.
+ *
+ * <daemon> zebra
+ * |
+ * +-----------------+
+ * | BFD session set |
+ * | to auto source |
+ * +-----------------+
+ * |
+ * \ +-----------------+
+ * --------------> | Resolves |
+ * | destination |
+ * | address |
+ * +-----------------+
+ * |
+ * +-----------------+ /
+ * | Sets resolved | <----------
+ * | source address |
+ * +-----------------+
+ */
+static bool
+bfd_source_cache_session_match(const struct bfd_source_cache *source,
+ const struct bfd_session_params *session)
+{
+ const struct in_addr *address;
+ const struct in6_addr *address_v6;
+
+ if (session->args.vrf_id != source->vrf_id)
+ return false;
+ if (session->args.family != source->address.family)
+ return false;
+
+ switch (session->args.family) {
+ case AF_INET:
+ address = (const struct in_addr *)&session->args.dst;
+ if (address->s_addr != source->address.u.prefix4.s_addr)
+ return false;
+ break;
+ case AF_INET6:
+ address_v6 = &session->args.dst;
+ if (memcmp(address_v6, &source->address.u.prefix6,
+ sizeof(struct in6_addr)))
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static struct bfd_source_cache *
+bfd_source_cache_find(vrf_id_t vrf_id, const struct prefix *prefix)
+{
+ struct bfd_source_cache *source;
+
+ SLIST_FOREACH (source, &bsglobal.source_list, entry) {
+ if (source->vrf_id != vrf_id)
+ continue;
+ if (!prefix_same(&source->address, prefix))
+ continue;
+
+ return source;
+ }
+
+ return NULL;
+}
+
+static void bfd_source_cache_get(struct bfd_session_params *session)
+{
+ struct bfd_source_cache *source;
+ struct prefix target = {};
+
+ switch (session->args.family) {
+ case AF_INET:
+ target.family = AF_INET;
+ target.prefixlen = IPV4_MAX_BITLEN;
+ memcpy(&target.u.prefix4, &session->args.dst,
+ sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ target.family = AF_INET6;
+ target.prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&target.u.prefix6, &session->args.dst,
+ sizeof(struct in6_addr));
+ break;
+ default:
+ return;
+ }
+
+ source = bfd_source_cache_find(session->args.vrf_id, &target);
+ if (source) {
+ if (session->source_cache == source)
+ return;
+
+ bfd_source_cache_put(session);
+ session->source_cache = source;
+ source->refcount++;
+ return;
+ }
+
+ source = XCALLOC(MTYPE_BFD_SOURCE, sizeof(*source));
+ prefix_copy(&source->address, &target);
+ source->vrf_id = session->args.vrf_id;
+ SLIST_INSERT_HEAD(&bsglobal.source_list, source, entry);
+
+ bfd_source_cache_put(session);
+ session->source_cache = source;
+ source->refcount = 1;
+
+ bfd_source_cache_register(source);
+
+ return;
+}
+
+static void bfd_source_cache_put(struct bfd_session_params *session)
+{
+ if (session->source_cache == NULL)
+ return;
+
+ session->source_cache->refcount--;
+ if (session->source_cache->refcount > 0) {
+ session->source_cache = NULL;
+ return;
+ }
+
+ bfd_source_cache_unregister(session->source_cache);
+ SLIST_REMOVE(&bsglobal.source_list, session->source_cache,
+ bfd_source_cache, entry);
+ XFREE(MTYPE_BFD_SOURCE, session->source_cache);
+}
+
+/** Updates BFD running session if source address has changed. */
+static void
+bfd_source_cache_update_session(const struct bfd_source_cache *source,
+ struct bfd_session_params *session)
+{
+ const struct in_addr *address;
+ const struct in6_addr *address_v6;
+
+ switch (session->args.family) {
+ case AF_INET:
+ address = (const struct in_addr *)&session->args.src;
+ if (memcmp(address, &source->source.u.prefix4,
+ sizeof(struct in_addr)) == 0)
+ return;
+
+ _bfd_sess_remove(session);
+ memcpy(&session->args.src, &source->source.u.prefix4,
+ sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ address_v6 = &session->args.src;
+ if (memcmp(address_v6, &source->source.u.prefix6,
+ sizeof(struct in6_addr)) == 0)
+ return;
+
+ _bfd_sess_remove(session);
+ memcpy(&session->args.src, &source->source.u.prefix6,
+ sizeof(struct in6_addr));
+ break;
+ default:
+ return;
+ }
+
+ bfd_sess_install(session);
+}
+
+static void
+bfd_source_cache_update_sessions(const struct bfd_source_cache *source)
+{
+ struct bfd_session_params *session;
+
+ if (!source->valid)
+ return;
+
+ TAILQ_FOREACH (session, &bsglobal.bsplist, entry) {
+ if (!session->auto_source)
+ continue;
+ if (!bfd_source_cache_session_match(source, session))
+ continue;
+
+ bfd_source_cache_update_session(source, session);
+ }
+}
+
+/**
+ * Try to translate next hop information into source address.
+ *
+ * \returns `true` if source changed otherwise `false`.
+ */
+static bool bfd_source_cache_update(struct bfd_source_cache *source,
+ const struct zapi_route *route)
+{
+ size_t nh_index;
+
+ for (nh_index = 0; nh_index < route->nexthop_num; nh_index++) {
+ const struct zapi_nexthop *nh = &route->nexthops[nh_index];
+ const struct interface *interface;
+ const struct connected *connected;
+ const struct listnode *node;
+
+ interface = if_lookup_by_index(nh->ifindex, nh->vrf_id);
+ if (interface == NULL) {
+ zlog_err("next hop interface not found (index %d)",
+ nh->ifindex);
+ continue;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(interface->connected, node,
+ connected)) {
+ if (source->address.family !=
+ connected->address->family)
+ continue;
+ if (prefix_same(connected->address, &source->source))
+ return false;
+ /*
+ * Skip link-local as it is only useful for single hop
+ * and in that case no source is specified usually.
+ */
+ if (source->address.family == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(
+ &connected->address->u.prefix6))
+ continue;
+
+ prefix_copy(&source->source, connected->address);
+ source->valid = true;
+ return true;
+ }
+ }
+
+ memset(&source->source, 0, sizeof(source->source));
+ source->valid = false;
+ return false;
+}
+
+static void bfd_nht_zclient_connect(struct thread *thread)
+{
+ struct zclient *zclient = THREAD_ARG(thread);
+
+ if (bsglobal.debugging)
+ zlog_debug("BFD NHT zclient connection attempt");
+
+ if (zclient_start(zclient) == -1) {
+ if (bsglobal.debugging)
+ zlog_debug("BFD NHT zclient connection failed");
+
+ thread_add_timer(bsglobal.tm, bfd_nht_zclient_connect, zclient,
+ 3, &zclient->t_connect);
+ return;
+ }
+
+ if (bsglobal.debugging)
+ zlog_debug("BFD NHT zclient connection succeeded");
+}
+
+static void bfd_nht_zclient_connected(struct zclient *zclient)
+{
+ struct bfd_source_cache *source;
+
+ if (bsglobal.debugging)
+ zlog_debug("BFD NHT zclient connected");
+
+ SLIST_FOREACH (source, &bsglobal.source_list, entry)
+ bfd_source_cache_register(source);
+}
+
+static int bfd_nht_update(ZAPI_CALLBACK_ARGS)
+{
+ struct bfd_source_cache *source;
+ struct zapi_route route;
+ struct prefix match;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &route)) {
+ zlog_warn("BFD NHT update decode failure");
+ return 0;
+ }
+ if (cmd != ZEBRA_NEXTHOP_UPDATE)
+ return 0;
+
+ if (bsglobal.debugging)
+ zlog_debug("BFD NHT update for %pFX", &route.prefix);
+
+ SLIST_FOREACH (source, &bsglobal.source_list, entry) {
+ if (source->vrf_id != route.vrf_id)
+ continue;
+ if (!prefix_same(&match, &source->address))
+ continue;
+ if (bfd_source_cache_update(source, &route))
+ bfd_source_cache_update_sessions(source);
+ }
+
+ return 0;
+}
diff --git a/lib/bfd.h b/lib/bfd.h
index 344ecc2c4d..b7e4eea5f3 100644
--- a/lib/bfd.h
+++ b/lib/bfd.h
@@ -222,6 +222,18 @@ void bfd_sess_set_timers(struct bfd_session_params *bsp,
uint32_t min_tx);
/**
+ * Configures the automatic source selection for the BFD session.
+ *
+ * NOTE:
+ * Setting this configuration will override the IP source value set by
+ * `bfd_sess_set_ipv4_addrs` or `bfd_sess_set_ipv6_addrs`.
+ *
+ * \param bsp BFD session parameters
+ * \param enable BFD automatic source selection state.
+ */
+void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable);
+
+/**
* Installs or updates the BFD session based on the saved session arguments.
*
* NOTE:
@@ -331,6 +343,11 @@ void bfd_sess_timers(const struct bfd_session_params *bsp,
uint32_t *min_tx);
/**
+ * Gets the automatic source selection state.
+ */
+bool bfd_sess_auto_source(const struct bfd_session_params *bsp);
+
+/**
* Show BFD session configuration and status. If `json` is provided (e.g. not
* `NULL`) then information will be inserted in object, otherwise printed to
* `vty`.
diff --git a/lib/command.h b/lib/command.h
index 0f9715e81c..5cc40f71f6 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -497,6 +497,13 @@ struct cmd_node {
#define ROLE_STR \
"Providing transit\nRoute server\nRS client\nUsing transit\nPublic/private peering\n"
+/* BFD protocol integration strings. */
+#define BFD_INTEGRATION_STR "BFD monitoring\n"
+#define BFD_INTEGRATION_MULTI_HOP_STR "Use BFD multi hop session\n"
+#define BFD_INTEGRATION_SOURCE_STR "Use source for BFD session\n"
+#define BFD_INTEGRATION_SOURCEV4_STR "Use IPv4 source for BFD session\n"
+#define BFD_INTEGRATION_SOURCEV6_STR "Use IPv4 source for BFD session\n"
+
/* Prototypes. */
extern void install_node(struct cmd_node *node);
extern void install_default(enum node_type);
diff --git a/staticd/static_bfd.c b/staticd/static_bfd.c
new file mode 100644
index 0000000000..fc8b518e11
--- /dev/null
+++ b/staticd/static_bfd.c
@@ -0,0 +1,390 @@
+/*
+ * Static daemon BFD integration.
+ *
+ * Copyright (C) 2020-2022 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <zebra.h>
+
+#include "lib/bfd.h"
+#include "lib/printfrr.h"
+#include "lib/srcdest_table.h"
+
+#include "staticd/static_routes.h"
+#include "staticd/static_zebra.h"
+#include "staticd/static_debug.h"
+
+#include "lib/openbsd-queue.h"
+
+/*
+ * Next hop BFD monitoring settings.
+ */
+static void static_next_hop_bfd_change(struct static_nexthop *sn,
+ const struct bfd_session_status *bss)
+{
+ switch (bss->state) {
+ case BSS_UNKNOWN:
+ /* FALLTHROUGH: no known state yet. */
+ case BSS_ADMIN_DOWN:
+ /* NOTHING: we or the remote end administratively shutdown. */
+ break;
+ case BSS_DOWN:
+ /* Peer went down, remove this next hop. */
+ DEBUGD(&static_dbg_bfd,
+ "%s: next hop is down, remove it from RIB", __func__);
+ sn->path_down = true;
+ static_zebra_route_add(sn->pn, true);
+ break;
+ case BSS_UP:
+ /* Peer is back up, add this next hop. */
+ DEBUGD(&static_dbg_bfd, "%s: next hop is up, add it to RIB",
+ __func__);
+ sn->path_down = false;
+ static_zebra_route_add(sn->pn, true);
+ break;
+ }
+}
+
+static void static_next_hop_bfd_updatecb(
+ __attribute__((unused)) struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss, void *arg)
+{
+ static_next_hop_bfd_change(arg, bss);
+}
+
+static inline int
+static_next_hop_type_to_family(const struct static_nexthop *sn)
+{
+ switch (sn->type) {
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ if (sn->type == STATIC_IPV4_GATEWAY ||
+ sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+ return AF_INET;
+ else
+ return AF_INET6;
+ break;
+ case STATIC_IFNAME:
+ case STATIC_BLACKHOLE:
+ default:
+ zlog_err("%s: invalid next hop type", __func__);
+ break;
+ }
+
+ return AF_UNSPEC;
+}
+
+void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+ const struct lyd_node *dnode)
+{
+ bool use_interface;
+ bool use_profile;
+ bool use_source;
+ bool onlink;
+ bool mhop;
+ int family;
+ struct ipaddr source;
+
+ use_interface = false;
+ use_source = yang_dnode_exists(dnode, "./source");
+ use_profile = yang_dnode_exists(dnode, "./profile");
+ onlink = yang_dnode_exists(dnode, "../onlink") &&
+ yang_dnode_get_bool(dnode, "../onlink");
+ mhop = yang_dnode_get_bool(dnode, "./multi-hop");
+
+
+ family = static_next_hop_type_to_family(sn);
+ if (family == AF_UNSPEC)
+ return;
+
+ if (sn->type == STATIC_IPV4_GATEWAY_IFNAME ||
+ sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+ use_interface = true;
+
+ /* Reconfigure or allocate new memory. */
+ if (sn->bsp == NULL)
+ sn->bsp = bfd_sess_new(static_next_hop_bfd_updatecb, sn);
+
+ /* Configure the session. */
+ if (use_source)
+ yang_dnode_get_ip(&source, dnode, "./source");
+
+ if (onlink || mhop == false)
+ bfd_sess_set_auto_source(sn->bsp, false);
+ else
+ bfd_sess_set_auto_source(sn->bsp, !use_source);
+
+ /* Configure the session.*/
+ if (family == AF_INET)
+ bfd_sess_set_ipv4_addrs(sn->bsp,
+ use_source ? &source.ip._v4_addr : NULL,
+ &sn->addr.ipv4);
+ else if (family == AF_INET6)
+ bfd_sess_set_ipv6_addrs(sn->bsp,
+ use_source ? &source.ip._v6_addr : NULL,
+ &sn->addr.ipv6);
+
+ bfd_sess_set_interface(sn->bsp, use_interface ? sn->ifname : NULL);
+
+ bfd_sess_set_profile(sn->bsp, use_profile ? yang_dnode_get_string(
+ dnode, "./profile")
+ : NULL);
+
+ bfd_sess_set_hop_count(sn->bsp, (onlink || mhop == false) ? 1 : 254);
+
+ /* Install or update the session. */
+ bfd_sess_install(sn->bsp);
+
+ /* Update current path status. */
+ sn->path_down = (bfd_sess_status(sn->bsp) != BSS_UP);
+}
+
+void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn)
+{
+ bfd_sess_free(&sn->bsp);
+
+ /* Reset path status. */
+ sn->path_down = false;
+}
+
+void static_next_hop_bfd_source(struct static_nexthop *sn,
+ const struct ipaddr *source)
+{
+ int family;
+
+ if (sn->bsp == NULL)
+ return;
+
+ family = static_next_hop_type_to_family(sn);
+ if (family == AF_UNSPEC)
+ return;
+
+ bfd_sess_set_auto_source(sn->bsp, false);
+ if (family == AF_INET)
+ bfd_sess_set_ipv4_addrs(sn->bsp, &source->ip._v4_addr,
+ &sn->addr.ipv4);
+ else if (family == AF_INET6)
+ bfd_sess_set_ipv6_addrs(sn->bsp, &source->ip._v6_addr,
+ &sn->addr.ipv6);
+
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_auto_source(struct static_nexthop *sn)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_auto_source(sn->bsp, true);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_hop_count(sn->bsp, mhop ? 254 : 1);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_next_hop_bfd_profile(struct static_nexthop *sn, const char *name)
+{
+ if (sn->bsp == NULL)
+ return;
+
+ bfd_sess_set_profile(sn->bsp, name);
+ bfd_sess_install(sn->bsp);
+}
+
+void static_bfd_initialize(struct zclient *zc, struct thread_master *tm)
+{
+ /* Initialize BFD integration library. */
+ bfd_protocol_integration_init(zc, tm);
+}
+
+/*
+ * Display functions
+ */
+static void static_bfd_show_nexthop_json(struct vty *vty,
+ struct json_object *jo,
+ const struct static_nexthop *sn)
+{
+ const struct prefix *dst_p, *src_p;
+ struct json_object *jo_nh;
+
+ jo_nh = json_object_new_object();
+
+ srcdest_rnode_prefixes(sn->rn, &dst_p, &src_p);
+ if (src_p)
+ json_object_string_addf(jo_nh, "from", "%pFX", src_p);
+
+ json_object_string_addf(jo_nh, "prefix", "%pFX", dst_p);
+ json_object_string_add(jo_nh, "vrf", sn->nh_vrfname);
+
+ json_object_boolean_add(jo_nh, "installed", !sn->path_down);
+
+ json_object_array_add(jo, jo_nh);
+}
+
+static void static_bfd_show_path_json(struct vty *vty, struct json_object *jo,
+ struct route_table *rt)
+{
+ struct route_node *rn;
+
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ struct static_route_info *si = static_route_info_from_rnode(rn);
+ struct static_path *sp;
+
+ if (si == NULL)
+ continue;
+
+ frr_each (static_path_list, &si->path_list, sp) {
+ struct static_nexthop *sn;
+
+ frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+ /* Skip non configured BFD sessions. */
+ if (sn->bsp == NULL)
+ continue;
+
+ static_bfd_show_nexthop_json(vty, jo, sn);
+ }
+ }
+ }
+}
+
+static void static_bfd_show_json(struct vty *vty)
+{
+ struct json_object *jo, *jo_path, *jo_afi_safi;
+ struct vrf *vrf;
+
+ jo = json_object_new_object();
+ jo_path = json_object_new_object();
+
+ json_object_object_add(jo, "path-list", jo_path);
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ const struct static_vrf *svrf = vrf->info;
+ struct route_table *rt;
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv4-unicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv4-multicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+
+ jo_afi_safi = json_object_new_array();
+ json_object_object_add(jo_path, "ipv6-unicast", jo_afi_safi);
+ rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path_json(vty, jo_afi_safi, rt);
+ }
+
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
+ json_object_free(jo);
+}
+
+static void static_bfd_show_nexthop(struct vty *vty,
+ const struct static_nexthop *sn)
+{
+ vty_out(vty, " %pRN", sn->rn);
+
+ if (sn->bsp == NULL) {
+ vty_out(vty, "\n");
+ return;
+ }
+
+ if (sn->type == STATIC_IPV4_GATEWAY ||
+ sn->type == STATIC_IPV4_GATEWAY_IFNAME)
+ vty_out(vty, " peer %pI4", &sn->addr.ipv4);
+ else if (sn->type == STATIC_IPV6_GATEWAY ||
+ sn->type == STATIC_IPV6_GATEWAY_IFNAME)
+ vty_out(vty, " peer %pI6", &sn->addr.ipv6);
+ else
+ vty_out(vty, " peer unknown");
+
+ vty_out(vty, " (status: %s)\n",
+ sn->path_down ? "uninstalled" : "installed");
+}
+
+static void static_bfd_show_path(struct vty *vty, struct route_table *rt)
+{
+ struct route_node *rn;
+
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ struct static_route_info *si = static_route_info_from_rnode(rn);
+ struct static_path *sp;
+
+ if (si == NULL)
+ continue;
+
+ frr_each (static_path_list, &si->path_list, sp) {
+ struct static_nexthop *sn;
+
+ frr_each (static_nexthop_list, &sp->nexthop_list, sn) {
+ /* Skip non configured BFD sessions. */
+ if (sn->bsp == NULL)
+ continue;
+
+ static_bfd_show_nexthop(vty, sn);
+ }
+ }
+ }
+}
+
+void static_bfd_show(struct vty *vty, bool json)
+{
+ struct vrf *vrf;
+
+ if (json) {
+ static_bfd_show_json(vty);
+ return;
+ }
+
+ vty_out(vty, "Showing BFD monitored static routes:\n");
+ vty_out(vty, "\n Next hops:\n");
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ const struct static_vrf *svrf = vrf->info;
+ struct route_table *rt;
+
+ vty_out(vty, " VRF %s IPv4 Unicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+
+ vty_out(vty, "\n VRF %s IPv4 Multicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP][SAFI_MULTICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+
+ vty_out(vty, "\n VRF %s IPv6 Unicast:\n", vrf->name);
+ rt = svrf->stable[AFI_IP6][SAFI_UNICAST];
+ if (rt)
+ static_bfd_show_path(vty, rt);
+ }
+
+ vty_out(vty, "\n");
+}
diff --git a/staticd/static_debug.c b/staticd/static_debug.c
index 45f845b40b..847e7d61a4 100644
--- a/staticd/static_debug.c
+++ b/staticd/static_debug.c
@@ -24,6 +24,7 @@
#include "lib/command.h"
#include "lib/debug.h"
+#include "lib/bfd.h"
#include "static_debug.h"
@@ -35,15 +36,18 @@
/* clang-format off */
struct debug static_dbg_events = {0, "Staticd events"};
struct debug static_dbg_route = {0, "Staticd route"};
+struct debug static_dbg_bfd = {0, "Staticd bfd"};
struct debug *static_debug_arr[] = {
&static_dbg_events,
- &static_dbg_route
+ &static_dbg_route,
+ &static_dbg_bfd
};
const char *static_debugs_conflines[] = {
"debug static events",
- "debug static route"
+ "debug static route",
+ "debug static bfd"
};
/* clang-format on */
@@ -105,7 +109,8 @@ int static_debug_status_write(struct vty *vty)
* Debug general internal events
*
*/
-void static_debug_set(int vtynode, bool onoff, bool events, bool route)
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+ bool bfd)
{
uint32_t mode = DEBUG_NODE2MODE(vtynode);
@@ -113,6 +118,10 @@ void static_debug_set(int vtynode, bool onoff, bool events, bool route)
DEBUG_MODE_SET(&static_dbg_events, mode, onoff);
if (route)
DEBUG_MODE_SET(&static_dbg_route, mode, onoff);
+ if (bfd) {
+ DEBUG_MODE_SET(&static_dbg_bfd, mode, onoff);
+ bfd_protocol_integration_set_debug(onoff);
+ }
}
/*
diff --git a/staticd/static_debug.h b/staticd/static_debug.h
index ee9f7b0537..129c096688 100644
--- a/staticd/static_debug.h
+++ b/staticd/static_debug.h
@@ -34,6 +34,7 @@ extern "C" {
/* staticd debugging records */
extern struct debug static_dbg_events;
extern struct debug static_dbg_route;
+extern struct debug static_dbg_bfd;
/*
* Initialize staticd debugging.
@@ -71,7 +72,8 @@ int static_debug_status_write(struct vty *vty);
* Debug general internal events
*
*/
-void static_debug_set(int vtynode, bool onoff, bool events, bool route);
+void static_debug_set(int vtynode, bool onoff, bool events, bool route,
+ bool bfd);
#ifdef __cplusplus
}
diff --git a/staticd/static_nb.c b/staticd/static_nb.c
index 5935364d5a..68d9ba97b4 100644
--- a/staticd/static_nb.c
+++ b/staticd/static_nb.c
@@ -117,6 +117,33 @@ const struct frr_yang_module_info frr_staticd_info = {
}
},
{
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring",
+ .cbs = {
+ .create = route_next_hop_bfd_create,
+ .destroy = route_next_hop_bfd_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source",
+ .cbs = {
+ .modify = route_next_hop_bfd_source_modify,
+ .destroy = route_next_hop_bfd_source_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop",
+ .cbs = {
+ .modify = route_next_hop_bfd_multi_hop_modify,
+ }
+ },
+ {
+ .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile",
+ .cbs = {
+ .modify = route_next_hop_bfd_profile_modify,
+ .destroy = route_next_hop_bfd_profile_destroy,
+ }
+ },
+ {
.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list",
.cbs = {
.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create,
diff --git a/staticd/static_nb.h b/staticd/static_nb.h
index 5c3030fcfa..96c9f8d9b7 100644
--- a/staticd/static_nb.h
+++ b/staticd/static_nb.h
@@ -63,6 +63,13 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy(
struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_create(struct nb_cb_create_args *args);
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args);
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args);
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
struct nb_cb_create_args *args);
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy(
diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c
index 4a3d9e17a4..cbb5b8234f 100644
--- a/staticd/static_nb_config.c
+++ b/staticd/static_nb_config.c
@@ -750,6 +750,113 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa
/*
* XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring
+ */
+int route_next_hop_bfd_create(struct nb_cb_create_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_monitor_enable(sn, args->dnode);
+ return NB_OK;
+}
+
+int route_next_hop_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_monitor_disable(sn);
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/source
+ */
+int route_next_hop_bfd_source_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+ struct ipaddr source;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ yang_dnode_get_ip(&source, args->dnode, NULL);
+ static_next_hop_bfd_source(sn, &source);
+ return NB_OK;
+}
+
+int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_auto_source(sn);
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/multi-hop
+ */
+int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_multi_hop(sn,
+ yang_dnode_get_bool(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/bfd-monitoring/profile
+ */
+int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_profile(sn,
+ yang_dnode_get_string(args->dnode, NULL));
+
+ return NB_OK;
+}
+
+int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct static_nexthop *sn;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ sn = nb_running_get_entry(args->dnode, NULL, true);
+ static_next_hop_bfd_profile(sn, NULL);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
* /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list
*/
int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create(
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
index ed4cdc51ce..ccbb98bd11 100644
--- a/staticd/static_routes.c
+++ b/staticd/static_routes.c
@@ -276,6 +276,8 @@ struct static_nexthop *static_add_nexthop(struct static_path *pn,
/* Make new static route structure. */
nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop));
+ /* Copy back pointers. */
+ nh->rn = rn;
nh->pn = pn;
nh->type = type;
@@ -393,6 +395,8 @@ void static_delete_nexthop(struct static_nexthop *nh)
struct route_node *rn = pn->rn;
static_nexthop_list_del(&(pn->nexthop_list), nh);
+ /* Remove BFD session/configuration if any. */
+ bfd_sess_free(&nh->bsp);
if (nh->nh_vrf_id == VRF_UNKNOWN)
goto EXIT;
@@ -432,6 +436,8 @@ static void static_ifindex_update_nh(struct interface *ifp, bool up,
nh->ifindex = IFINDEX_INTERNAL;
}
+ /* Remove previously configured route if any. */
+ static_uninstall_path(pn);
static_install_path(pn);
}
diff --git a/staticd/static_routes.h b/staticd/static_routes.h
index 71c3689be5..2332cfd2bf 100644
--- a/staticd/static_routes.h
+++ b/staticd/static_routes.h
@@ -20,6 +20,7 @@
#ifndef __STATIC_ROUTES_H__
#define __STATIC_ROUTES_H__
+#include "lib/bfd.h"
#include "lib/mpls.h"
#include "table.h"
#include "memory.h"
@@ -30,6 +31,8 @@ extern "C" {
DECLARE_MGROUP(STATIC);
+#include "staticd/static_vrf.h"
+
/* Static route label information */
struct static_nh_label {
uint8_t num_labels;
@@ -148,6 +151,13 @@ struct static_nexthop {
/* SR-TE color */
uint32_t color;
+
+ /** BFD integration data. */
+ struct bfd_session_params *bsp;
+ /** Back pointer for route node. */
+ struct route_node *rn;
+ /** Path connection status. */
+ bool path_down;
};
DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list);
@@ -218,6 +228,24 @@ extern void zebra_stable_node_cleanup(struct route_table *table,
extern void static_get_nh_str(struct static_nexthop *nh, char *nexthop,
size_t size);
+/*
+ * BFD integration.
+ */
+extern void static_next_hop_bfd_source(struct static_nexthop *sn,
+ const struct ipaddr *source);
+extern void static_next_hop_bfd_auto_source(struct static_nexthop *sn);
+extern void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn,
+ const struct lyd_node *dnode);
+extern void static_next_hop_bfd_monitor_disable(struct static_nexthop *sn);
+extern void static_next_hop_bfd_profile(struct static_nexthop *sn,
+ const char *name);
+extern void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop);
+
+/** Call this function after zebra client initialization. */
+extern void static_bfd_initialize(struct zclient *zc, struct thread_master *tm);
+
+extern void static_bfd_show(struct vty *vty, bool isjson);
+
#ifdef __cplusplus
}
#endif
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
index efae3c53da..ff79622038 100644
--- a/staticd/static_vty.c
+++ b/staticd/static_vty.c
@@ -65,6 +65,11 @@ struct static_route_args {
const char *label;
const char *table;
const char *color;
+
+ bool bfd;
+ bool bfd_multi_hop;
+ const char *bfd_source;
+ const char *bfd_profile;
};
static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
@@ -138,6 +143,11 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
apply_mask(&p);
prefix2str(&p, buf_prefix, sizeof(buf_prefix));
+ if (args->bfd && args->gateway == NULL) {
+ vty_out(vty, "%% Route monitoring requires a gateway\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
if (args->source)
prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));
if (args->gateway)
@@ -332,6 +342,41 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
NULL);
}
+
+ if (args->bfd) {
+ char xpath_bfd[XPATH_MAXLEN];
+
+ if (args->bfd_source) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/source",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_source);
+ }
+
+ strlcpy(xpath_bfd, xpath_nexthop, sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/multi-hop",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd, NB_OP_MODIFY,
+ args->bfd_multi_hop ? "true"
+ : "false");
+
+ if (args->bfd_profile) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/profile",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_profile);
+ }
+ }
+
ret = nb_cli_apply_changes(vty, xpath_prefix);
} else {
if (args->source)
@@ -375,14 +420,23 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
/* Static unicast routes for multicast RPF lookup. */
DEFPY_YANG (ip_mroute_dist,
ip_mroute_dist_cmd,
- "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [(1-255)$distance]",
+ "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [{"
+ "(1-255)$distance"
+ "|bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}]"
+ "}]",
NO_STR
IP_STR
"Configure static unicast route into MRIB for multicast RPF lookup\n"
"IP destination prefix (e.g. 10.0.0.0/8)\n"
"Nexthop address\n"
"Nexthop interface name\n"
- "Distance\n")
+ "Distance\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -392,6 +446,10 @@ DEFPY_YANG (ip_mroute_dist,
.gateway = gate_str,
.interface_name = ifname,
.distance = distance_str,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -506,6 +564,7 @@ DEFPY_YANG(ip_route_address_interface,
|nexthop-vrf NAME \
|onlink$onlink \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -525,7 +584,13 @@ DEFPY_YANG(ip_route_address_interface,
VRF_CMD_HELP_STR
"Treat the nexthop as directly attached to the interface\n"
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -543,6 +608,10 @@ DEFPY_YANG(ip_route_address_interface,
.onlink = !!onlink,
.vrf = vrf,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -562,6 +631,7 @@ DEFPY_YANG(ip_route_address_interface_vrf,
|nexthop-vrf NAME \
|onlink$onlink \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -580,7 +650,13 @@ DEFPY_YANG(ip_route_address_interface_vrf,
VRF_CMD_HELP_STR
"Treat the nexthop as directly attached to the interface\n"
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -598,6 +674,10 @@ DEFPY_YANG(ip_route_address_interface_vrf,
.onlink = !!onlink,
.xpath_vrf = true,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -616,6 +696,7 @@ DEFPY_YANG(ip_route,
|table (1-4294967295) \
|nexthop-vrf NAME \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -634,7 +715,13 @@ DEFPY_YANG(ip_route,
"The table number to configure\n"
VRF_CMD_HELP_STR
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -651,6 +738,10 @@ DEFPY_YANG(ip_route,
.color = color_str,
.vrf = vrf,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -668,6 +759,7 @@ DEFPY_YANG(ip_route_vrf,
|table (1-4294967295) \
|nexthop-vrf NAME \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR IP_STR
"Establish static routes\n"
@@ -685,7 +777,13 @@ DEFPY_YANG(ip_route_vrf,
"The table number to configure\n"
VRF_CMD_HELP_STR
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -702,6 +800,10 @@ DEFPY_YANG(ip_route_vrf,
.color = color_str,
.xpath_vrf = true,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -814,6 +916,7 @@ DEFPY_YANG(ipv6_route_address_interface,
|nexthop-vrf NAME \
|onlink$onlink \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR
IPV6_STR
@@ -834,7 +937,13 @@ DEFPY_YANG(ipv6_route_address_interface,
VRF_CMD_HELP_STR
"Treat the nexthop as directly attached to the interface\n"
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -852,6 +961,10 @@ DEFPY_YANG(ipv6_route_address_interface,
.onlink = !!onlink,
.vrf = vrf,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -870,6 +983,7 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
|nexthop-vrf NAME \
|onlink$onlink \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR
IPV6_STR
@@ -889,7 +1003,13 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
VRF_CMD_HELP_STR
"Treat the nexthop as directly attached to the interface\n"
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -907,6 +1027,10 @@ DEFPY_YANG(ipv6_route_address_interface_vrf,
.onlink = !!onlink,
.xpath_vrf = true,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -924,6 +1048,7 @@ DEFPY_YANG(ipv6_route,
|table (1-4294967295) \
|nexthop-vrf NAME \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR
IPV6_STR
@@ -943,7 +1068,13 @@ DEFPY_YANG(ipv6_route,
"The table number to configure\n"
VRF_CMD_HELP_STR
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -960,6 +1091,10 @@ DEFPY_YANG(ipv6_route,
.color = color_str,
.vrf = vrf,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -976,6 +1111,7 @@ DEFPY_YANG(ipv6_route_vrf,
|table (1-4294967295) \
|nexthop-vrf NAME \
|color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
}]",
NO_STR
IPV6_STR
@@ -994,7 +1130,13 @@ DEFPY_YANG(ipv6_route_vrf,
"The table number to configure\n"
VRF_CMD_HELP_STR
"SR-TE color\n"
- "The SR-TE color to configure\n")
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
{
struct static_route_args args = {
.delete = !!no,
@@ -1011,6 +1153,10 @@ DEFPY_YANG(ipv6_route_vrf,
.color = color_str,
.xpath_vrf = true,
.nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
};
return static_route_nb_run(vty, &args);
@@ -1165,6 +1311,25 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
vty_out(vty, " color %s",
yang_dnode_get_string(nexthop, "./srte-color"));
+ if (yang_dnode_exists(nexthop, "./bfd-monitoring")) {
+ const struct lyd_node *bfd_dnode =
+ yang_dnode_get(nexthop, "./bfd-monitoring");
+
+ if (yang_dnode_get_bool(bfd_dnode, "./multi-hop")) {
+ vty_out(vty, " bfd multi-hop");
+
+ if (yang_dnode_exists(bfd_dnode, "./source"))
+ vty_out(vty, " source %s",
+ yang_dnode_get_string(bfd_dnode,
+ "./source"));
+ } else
+ vty_out(vty, " bfd");
+
+ if (yang_dnode_exists(bfd_dnode, "./profile"))
+ vty_out(vty, " profile %s",
+ yang_dnode_get_string(bfd_dnode, "./profile"));
+ }
+
vty_out(vty, "\n");
}
@@ -1292,20 +1457,33 @@ int static_path_list_cli_cmp(const struct lyd_node *dnode1,
}
DEFPY_YANG(debug_staticd, debug_staticd_cmd,
- "[no] debug static [{events$events|route$route}]",
+ "[no] debug static [{events$events|route$route|bfd$bfd}]",
NO_STR DEBUG_STR STATICD_STR
"Debug events\n"
- "Debug route\n")
+ "Debug route\n"
+ "Debug bfd\n")
{
/* If no specific category, change all */
if (strmatch(argv[argc - 1]->text, "static"))
- static_debug_set(vty->node, !no, true, true);
+ static_debug_set(vty->node, !no, true, true, true);
else
- static_debug_set(vty->node, !no, !!events, !!route);
+ static_debug_set(vty->node, !no, !!events, !!route, !!bfd);
return CMD_SUCCESS;
}
+DEFPY(staticd_show_bfd_routes, staticd_show_bfd_routes_cmd,
+ "show bfd static route [json]$isjson",
+ SHOW_STR
+ BFD_INTEGRATION_STR
+ STATICD_STR
+ ROUTE_STR
+ JSON_STR)
+{
+ static_bfd_show(vty, !!isjson);
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (show_debugging_static,
show_debugging_static_cmd,
"show debugging [static]",
@@ -1352,4 +1530,6 @@ void static_vty_init(void)
install_element(ENABLE_NODE, &show_debugging_static_cmd);
install_element(ENABLE_NODE, &debug_staticd_cmd);
install_element(CONFIG_NODE, &debug_staticd_cmd);
+
+ install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd);
}
diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c
index cb36304473..316247adb3 100644
--- a/staticd/static_zebra.c
+++ b/staticd/static_zebra.c
@@ -441,6 +441,9 @@ extern void static_zebra_route_add(struct static_path *pn, bool install)
api_nh = &api.nexthops[nh_num];
if (nh->nh_vrf_id == VRF_UNKNOWN)
continue;
+ /* Skip next hop which peer is down. */
+ if (nh->path_down)
+ continue;
api_nh->vrf_id = nh->nh_vrf_id;
if (nh->onlink)
@@ -545,6 +548,7 @@ void static_zebra_init(void)
zclient->zebra_connected = zebra_connected;
static_nht_hash_init(static_nht_hash);
+ static_bfd_initialize(zclient, master);
}
/* static_zebra_stop used by tests/lib/test_grpc.cpp */
diff --git a/staticd/subdir.am b/staticd/subdir.am
index bb0fc95bc2..022428281f 100644
--- a/staticd/subdir.am
+++ b/staticd/subdir.am
@@ -10,6 +10,7 @@ man8 += $(MANBUILD)/frr-staticd.8
endif
staticd_libstatic_a_SOURCES = \
+ staticd/static_bfd.c \
staticd/static_debug.c \
staticd/static_nht.c \
staticd/static_routes.c \
@@ -38,5 +39,6 @@ staticd_staticd_SOURCES = staticd/static_main.c
staticd_staticd_LDADD = staticd/libstatic.a lib/libfrr.la $(LIBCAP)
nodist_staticd_staticd_SOURCES = \
+ yang/frr-bfdd.yang.c \
yang/frr-staticd.yang.c \
# end
diff --git a/tests/topotests/bfd_topo3/r3/bfd-static-down.json b/tests/topotests/bfd_topo3/r3/bfd-static-down.json
new file mode 100644
index 0000000000..60752d3aa1
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bfd-static-down.json
@@ -0,0 +1,12 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:5::\/64",
+ "vrf": "default",
+ "installed": false
+ }
+ ]
+ }}
diff --git a/tests/topotests/bfd_topo3/r3/bfd-static.json b/tests/topotests/bfd_topo3/r3/bfd-static.json
new file mode 100644
index 0000000000..c65060c7b0
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bfd-static.json
@@ -0,0 +1,13 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:5::\/64",
+ "vrf": "default",
+ "installed": true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo3/r3/staticd.conf b/tests/topotests/bfd_topo3/r3/staticd.conf
new file mode 100644
index 0000000000..44f91f3f34
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/staticd.conf
@@ -0,0 +1 @@
+ipv6 route 2001:db8:5::/64 2001:db8:4::3 bfd multi-hop profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r4/bfd-peers.json b/tests/topotests/bfd_topo3/r4/bfd-peers.json
index 2f41f25c58..4f71d75389 100644
--- a/tests/topotests/bfd_topo3/r4/bfd-peers.json
+++ b/tests/topotests/bfd_topo3/r4/bfd-peers.json
@@ -19,7 +19,8 @@
"remote-transmit-interval": 2000,
"status": "up",
"uptime": "*",
- "transmit-interval": 2000
+ "transmit-interval": 2000,
+ "vrf": "default"
},
{
"detect-multiplier": 3,
@@ -41,6 +42,49 @@
"remote-transmit-interval": 2000,
"status": "up",
"uptime": "*",
- "transmit-interval": 2000
+ "transmit-interval": 2000,
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.3",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
}
]
diff --git a/tests/topotests/bfd_topo3/r4/bgpd.conf b/tests/topotests/bfd_topo3/r4/bgpd.conf
index 0aab6e3017..bfad78a7ad 100644
--- a/tests/topotests/bfd_topo3/r4/bgpd.conf
+++ b/tests/topotests/bfd_topo3/r4/bgpd.conf
@@ -9,6 +9,7 @@ router bgp 400
neighbor 2001:db8:1::1 bfd profile slow-tx-mh
address-family ipv4 unicast
redistribute connected
+ redistribute static
exit-address-family
address-family ipv6 unicast
redistribute connected
diff --git a/tests/topotests/bfd_topo3/r4/staticd.conf b/tests/topotests/bfd_topo3/r4/staticd.conf
new file mode 100644
index 0000000000..3b1c5bfb66
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/staticd.conf
@@ -0,0 +1,2 @@
+ip route 10.254.254.5/32 192.168.4.2 bfd profile slow-tx
+ip route 10.254.254.6/32 192.168.4.3 bfd profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r4/zebra.conf b/tests/topotests/bfd_topo3/r4/zebra.conf
index bf0cfcf42c..2574941724 100644
--- a/tests/topotests/bfd_topo3/r4/zebra.conf
+++ b/tests/topotests/bfd_topo3/r4/zebra.conf
@@ -8,3 +8,7 @@ interface r4-eth0
ip address 192.168.3.1/24
ipv6 address 2001:db8:3::1/64
!
+interface r4-eth1
+ ip address 192.168.4.1/24
+ ipv6 address 2001:db8:4::1/64
+!
diff --git a/tests/topotests/bfd_topo3/r5/bfd-peers.json b/tests/topotests/bfd_topo3/r5/bfd-peers.json
new file mode 100644
index 0000000000..777b1dd9cc
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/bfd-peers.json
@@ -0,0 +1,23 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf
new file mode 100644
index 0000000000..6d4483acc4
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/bfdd.conf
@@ -0,0 +1,11 @@
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r5/staticd.conf b/tests/topotests/bfd_topo3/r5/staticd.conf
new file mode 100644
index 0000000000..9828cffe0c
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/staticd.conf
@@ -0,0 +1,2 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r5/zebra.conf b/tests/topotests/bfd_topo3/r5/zebra.conf
new file mode 100644
index 0000000000..f84ce7e7f0
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r5/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.5/32
+!
+interface r5-eth0
+ ip address 192.168.4.2/24
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd_topo3/r6/bfd-peers.json b/tests/topotests/bfd_topo3/r6/bfd-peers.json
new file mode 100644
index 0000000000..4de451d15b
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "multihop": false,
+ "passive-mode": false,
+ "peer": "192.168.4.1",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "id": "*",
+ "local": "2001:db8:4::3",
+ "minimum-ttl": 2,
+ "multihop": true,
+ "passive-mode": false,
+ "peer": "2001:db8:3::2",
+ "receive-interval": 2000,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "transmit-interval": 2000,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static-down.json b/tests/topotests/bfd_topo3/r6/bfd-static-down.json
new file mode 100644
index 0000000000..4dadff2251
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-static-down.json
@@ -0,0 +1,19 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [
+ {
+ "installed": true,
+ "prefix": "10.254.254.4/32",
+ "vrf": "default"
+ }
+ ],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:1::\/64",
+ "vrf": "default",
+ "installed": false
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfd-static.json b/tests/topotests/bfd_topo3/r6/bfd-static.json
new file mode 100644
index 0000000000..d042889d50
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfd-static.json
@@ -0,0 +1,19 @@
+{
+ "path-list": {
+ "ipv4-multicast": [],
+ "ipv4-unicast": [
+ {
+ "installed": true,
+ "prefix": "10.254.254.4/32",
+ "vrf": "default"
+ }
+ ],
+ "ipv6-unicast": [
+ {
+ "prefix": "2001:db8:1::\/64",
+ "vrf": "default",
+ "installed": true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf
new file mode 100644
index 0000000000..6d4483acc4
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/bfdd.conf
@@ -0,0 +1,11 @@
+debug bfd network
+debug bfd peer
+debug bfd zebra
+!
+bfd
+ profile slow-tx
+ receive-interval 2000
+ transmit-interval 2000
+ minimum-ttl 250
+ !
+!
diff --git a/tests/topotests/bfd_topo3/r6/staticd.conf b/tests/topotests/bfd_topo3/r6/staticd.conf
new file mode 100644
index 0000000000..28da508fc2
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/staticd.conf
@@ -0,0 +1,5 @@
+ip route 0.0.0.0/0 192.168.4.1
+ip route 10.254.254.4/32 192.168.4.1 bfd profile slow-tx
+!
+ipv6 route 2001:db8:3::/64 2001:db8:4::1
+ipv6 route 2001:db8:1::/64 2001:db8:3::2 bfd multi-hop source 2001:db8:4::3 profile slow-tx
diff --git a/tests/topotests/bfd_topo3/r6/zebra.conf b/tests/topotests/bfd_topo3/r6/zebra.conf
new file mode 100644
index 0000000000..b8f2ea4c03
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r6/zebra.conf
@@ -0,0 +1,10 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.6/32
+!
+interface r6-eth0
+ ip address 192.168.4.3/24
+ ipv6 address 2001:db8:4::3/64
+!
diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.dot b/tests/topotests/bfd_topo3/test_bfd_topo3.dot
index 502cea11f2..8d18783d54 100644
--- a/tests/topotests/bfd_topo3/test_bfd_topo3.dot
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.dot
@@ -40,6 +40,18 @@ graph template {
fillcolor="#f08080",
style=filled,
];
+ r5 [
+ shape=doubleoctagon
+ label="r5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r6 [
+ shape=doubleoctagon
+ label="r6",
+ fillcolor="#f08080",
+ style=filled,
+ ];
# Switches
sw1 [
@@ -60,6 +72,12 @@ graph template {
fillcolor="#d0e0d0",
style=filled,
];
+ sw4 [
+ shape=oval,
+ label="sw4\n192.168.4.0/24\n2001:db8:4::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
# Connections
r1 -- sw1 [label="eth0\n.1"];
@@ -70,4 +88,8 @@ graph template {
r4 -- sw3 [label="eth0\n.1"];
r3 -- sw3 [label="eth2\n.2"];
+
+ r4 -- sw4 [label="eth1\n.1"];
+ r5 -- sw4 [label="eth0\n.2"];
+ r6 -- sw4 [label="eth0\n.3"];
}
diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.jpg b/tests/topotests/bfd_topo3/test_bfd_topo3.jpg
index 6b532560bf..f100eae712 100644
--- a/tests/topotests/bfd_topo3/test_bfd_topo3.jpg
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.jpg
Binary files differ
diff --git a/tests/topotests/bfd_topo3/test_bfd_topo3.py b/tests/topotests/bfd_topo3/test_bfd_topo3.py
index 978593e41a..ea9f981d9b 100644
--- a/tests/topotests/bfd_topo3/test_bfd_topo3.py
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py
@@ -51,6 +51,7 @@ def setup_module(mod):
"s1": ("r1", "r2"),
"s2": ("r2", "r3"),
"s3": ("r3", "r4"),
+ "s4": ("r4", "r5", "r6"),
}
tgen = Topogen(topodef, mod.__name__)
tgen.start_topology()
@@ -69,6 +70,10 @@ def setup_module(mod):
if os.path.isfile(daemon_file):
router.load_config(TopoRouter.RD_BGP, daemon_file)
+ daemon_file = "{}/{}/staticd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_STATIC, daemon_file)
+
# Initialize all routers.
tgen.start_router()
@@ -100,6 +105,10 @@ def test_wait_bgp_convergence():
expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp")
# Wait for R1 <-> R4 convergence.
expect_loopback_route("r1", "ip", "10.254.254.4/32", "bgp")
+ # Wait for R1 <-> R5 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R1 <-> R6 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.6/32", "bgp")
# Wait for R2 <-> R1 convergence.
expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
@@ -107,6 +116,10 @@ def test_wait_bgp_convergence():
expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
# Wait for R2 <-> R4 convergence.
expect_loopback_route("r2", "ip", "10.254.254.4/32", "bgp")
+ # Wait for R2 <-> R5 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R2 <-> R6 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.6/32", "bgp")
# Wait for R3 <-> R1 convergence.
expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp")
@@ -114,6 +127,10 @@ def test_wait_bgp_convergence():
expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp")
# Wait for R3 <-> R4 convergence.
expect_loopback_route("r3", "ip", "10.254.254.4/32", "bgp")
+ # Wait for R3 <-> R5 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.5/32", "bgp")
+ # Wait for R3 <-> R6 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.6/32", "bgp")
# Wait for R4 <-> R1 convergence.
expect_loopback_route("r4", "ip", "10.254.254.1/32", "bgp")
@@ -121,6 +138,15 @@ def test_wait_bgp_convergence():
expect_loopback_route("r4", "ip", "10.254.254.2/32", "bgp")
# Wait for R4 <-> R3 convergence.
expect_loopback_route("r4", "ip", "10.254.254.3/32", "bgp")
+ # Wait for R4 <-> R5 convergence.
+ expect_loopback_route("r4", "ip", "10.254.254.5/32", "static")
+ # Wait for R4 <-> R6 convergence.
+ expect_loopback_route("r4", "ip", "10.254.254.6/32", "static")
+
+ # Wait for R5 <-> R6 convergence.
+ expect_loopback_route("r3", "ipv6", "2001:db8:5::/64", "static")
+ # Wait for R6 <-> R5 convergence.
+ expect_loopback_route("r6", "ipv6", "2001:db8:1::/64", "static")
def test_wait_bfd_convergence():
@@ -149,6 +175,70 @@ def test_wait_bfd_convergence():
expect_bfd_configuration("r2")
expect_bfd_configuration("r3")
expect_bfd_configuration("r4")
+ expect_bfd_configuration("r5")
+ expect_bfd_configuration("r6")
+
+
+def test_static_route_monitoring():
+ "Test static route monitoring output."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test BFD static route status")
+
+ def expect_static_bfd_output(router, filename):
+ "Load JSON file and compare with 'show bfd peer json'"
+ logger.info("waiting BFD configuration on router {}".format(router))
+ bfd_config = json.loads(
+ open("{}/{}/{}.json".format(CWD, router, filename)).read()
+ )
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show bfd static route json",
+ bfd_config,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" BFD static route status failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_static_bfd_output("r3", "bfd-static")
+ expect_static_bfd_output("r6", "bfd-static")
+
+ logger.info("Setting r4 link down ...")
+
+ tgen.gears["r4"].link_enable("r4-eth0", False)
+
+ expect_static_bfd_output("r3", "bfd-static-down")
+ expect_static_bfd_output("r6", "bfd-static-down")
+
+
+def test_expect_static_rib_removal():
+ "Test that route got removed from RIB (staticd and bgpd)."
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def expect_route_missing(router, iptype, route):
+ "Wait until route is present on RIB for protocol."
+ logger.info("waiting route {} to disapear in {}".format(route, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show {} route json".format(iptype),
+ {route: None},
+ )
+ rv, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_route_missing("r1", "ip", "10.254.254.5/32")
+ expect_route_missing("r2", "ip", "10.254.254.5/32")
+ expect_route_missing("r3", "ip", "10.254.254.5/32")
+ expect_route_missing("r3", "ipv6", "2001:db8:5::/64")
+ expect_route_missing("r6", "ipv6", "2001:db8:1::/64")
def teardown_module(_mod):
diff --git a/tests/topotests/bgp_path_attribute_discard/__init__.py b/tests/topotests/bgp_path_attribute_discard/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/__init__.py
diff --git a/tests/topotests/bgp_path_attribute_discard/exabgp.env b/tests/topotests/bgp_path_attribute_discard/exabgp.env
new file mode 100644
index 0000000000..28e642360a
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/exabgp.env
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
new file mode 100644
index 0000000000..7fb9210ecf
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/peer1/exabgp.cfg
@@ -0,0 +1,24 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65002;
+
+ capability {
+ route-refresh;
+ }
+
+ static {
+ route 192.168.100.101/32 {
+ atomic-aggregate;
+ community 65001:101;
+ next-hop 10.0.0.2;
+ }
+
+ route 192.168.100.102/32 {
+ originator-id 10.0.0.2;
+ community 65001:102;
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
new file mode 100644
index 0000000000..c96f354cc5
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
new file mode 100644
index 0000000000..51a1b2657c
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
new file mode 100644
index 0000000000..4badf64c37
--- /dev/null
+++ b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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 NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
+#
+
+"""
+Test if `neighbor path-attribute discard` command works correctly,
+can discard unwanted attributes from UPDATE messages, and ignore them
+by continuing to process UPDATE messages.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r1 = tgen.add_router("r1")
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(peer1)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_path_attribute_discard():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "192.168.100.101/32": [
+ {
+ "valid": True,
+ "atomicAggregate": True,
+ "community": {
+ "string": "65001:101",
+ },
+ }
+ ],
+ "192.168.100.102/32": [
+ {
+ "valid": True,
+ "originatorId": "10.0.0.2",
+ "community": {
+ "string": "65001:102",
+ },
+ }
+ ],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed bgp convergence"
+
+ step("Discard atomic-aggregate, community, and originator-id attributes from peer1")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 10.0.0.2 path-attribute discard 6 8 9
+ """
+ )
+
+ def _bgp_check_if_attributes_discarded():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json detail"))
+ expected = {
+ "routes": {
+ "192.168.100.101/32": [
+ {
+ "valid": True,
+ "atomicAggregate": None,
+ "community": None,
+ }
+ ],
+ "192.168.100.102/32": [
+ {
+ "valid": True,
+ "originatorId": None,
+ "community": None,
+ }
+ ],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_if_attributes_discarded)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert (
+ result is None
+ ), "Failed to discard path attributes (atomic-aggregate, community, and originator-id)"
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
index 719d76392d..409be74740 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
@@ -1,14 +1,19 @@
! exit1
router bgp 65001
- no bgp ebgp-requires-policy
- neighbor 192.168.255.1 remote-as 65002
- neighbor 192.168.255.1 timers 3 10
- address-family ipv4 unicast
- neighbor 192.168.255.1 route-map prepend out
- redistribute connected
- exit-address-family
- !
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.1 route-map prepend out
+ redistribute connected
+ exit-address-family
+ !
+!
+ip prefix-list p1 seq 5 permit 172.16.255.253/32
!
route-map prepend permit 10
- set as-path prepend 65003
+ match ip address prefix-list p1
+ set as-path prepend 65003
+!
+route-map prepend permit 20
!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
index 9904bb4e16..74489a0571 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
@@ -1,5 +1,6 @@
! exit1
interface lo
+ ip address 172.16.255.253/32
ip address 172.16.255.254/32
!
interface r1-eth0
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
index a4a654d7b5..dcb52a2e7d 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
@@ -1,11 +1,10 @@
! spine
router bgp 65002
- no bgp ebgp-requires-policy
- neighbor 192.168.255.2 remote-as 65001
- neighbor 192.168.255.2 timers 3 10
- neighbor 192.168.255.2 solo
- neighbor 192.168.254.2 remote-as 65003
- neighbor 192.168.254.2 timers 3 10
- neighbor 192.168.254.2 solo
- neighbor 192.168.254.2 sender-as-path-loop-detection
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 sender-as-path-loop-detection
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.254.2 timers 3 10
+ neighbor 192.168.254.2 sender-as-path-loop-detection
!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
index 2e24de0b2d..519273d30d 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
@@ -1,6 +1,6 @@
! exit2
router bgp 65003
- no bgp ebgp-requires-policy
- neighbor 192.168.254.1 remote-as 65002
- neighbor 192.168.254.1 timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.254.1 remote-as 65002
+ neighbor 192.168.254.1 timers 3 10
!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
index b5c33f359b..ebeab05648 100644
--- a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
@@ -85,20 +85,20 @@ def test_bgp_sender_as_path_loop_detection():
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
- router = tgen.gears["r2"]
+ r2 = tgen.gears["r2"]
- def _bgp_converge(router):
- output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
expected = {
"192.168.255.2": {
"bgpState": "Established",
- "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
}
}
return topotest.json_cmp(output, expected)
- def _bgp_has_route_from_r1(router):
- output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+ def _bgp_has_route_from_r1():
+ output = json.loads(r2.vtysh_cmd("show ip bgp 172.16.255.253/32 json"))
expected = {
"paths": [
{
@@ -111,31 +111,35 @@ def test_bgp_sender_as_path_loop_detection():
}
return topotest.json_cmp(output, expected)
- def _bgp_suppress_route_to_r3(router):
+ def _bgp_suppress_route_to_r1():
output = json.loads(
- router.vtysh_cmd(
- "show ip bgp neighbor 192.168.254.2 advertised-routes json"
- )
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.255.2 advertised-routes json")
)
expected = {"totalPrefixCounter": 0}
return topotest.json_cmp(output, expected)
- test_func = functools.partial(_bgp_converge, router)
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
-
- assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+ def _bgp_suppress_route_to_r3():
+ output = json.loads(
+ r2.vtysh_cmd("show ip bgp neighbor 192.168.254.2 advertised-routes json")
+ )
+ expected = {"totalPrefixCounter": 2}
+ return topotest.json_cmp(output, expected)
- test_func = functools.partial(_bgp_has_route_from_r1, router)
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed bgp to convergence"
- assert result is None, 'Failed to see a route from r1 in "{}"'.format(router)
+ test_func = functools.partial(_bgp_has_route_from_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see a route from r1"
- test_func = functools.partial(_bgp_suppress_route_to_r3, router)
- success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ test_func = functools.partial(_bgp_suppress_route_to_r3)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Route 172.16.255.253/32 should not be sent to r3 from r2"
- assert (
- result is None
- ), 'Route 172.16.255.254/32 should not be sent to r3 "{}"'.format(router)
+ test_func = functools.partial(_bgp_suppress_route_to_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Routes should not be sent to r1 from r2"
if __name__ == "__main__":
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py
new file mode 100644
index 0000000000..c51beca72a
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py
@@ -0,0 +1,407 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify recursive import among Tenant VRFs.
+2. Verify that dynamic import works fine between two different Tenant VRFs.
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+3. Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+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,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_recursive_import_tenant_vrf_p1(request):
+ """
+ Verify recursive import among Tenant VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R2 for vrf RED and redistribute in "
+ "respective BGP instance"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF RED")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that R2 has installed redistributed routes in vrf RED only")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import vrf RED's routes into vrf GREEN on R2")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R2, that it installs imported routes from vrf RED to vrf "
+ "GREEN's RIB/FIB pointing next-hop to vrf RED"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("On R3 import routes from vrf GREEN to vrf default")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R3, that it installs imported routes from vrf GREEN to "
+ "vrf default RIB/FIB pointing next-hop to vrf GREEN. "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("On R4 import routes from vrf default to vrf BLUE")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}})
+
+ import_dict = {
+ "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R4, that it installs imported routes from vrf default to "
+ "vrf BLUE RIB/FIB pointing next-hop to vrf default."
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r4": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for dut, vrf_name, vrf_import, as_num in zip(
+ ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4]
+ ):
+
+ for action, value in zip(["Delete", "Re-add"], [True, False]):
+ step("{} the import command on {} router".format(action, dut))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"import": {"vrf": vrf_import, "delete": value}}
+ }
+ }
+ )
+
+ import_dict = {
+ dut: {
+ "bgp": [
+ {"vrf": vrf_name, "local_as": as_num, "address_family": temp}
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r4": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}
+ ]
+ }
+ }
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r4", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes["r4"]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r4", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes["r4"]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", static_routes)
+ 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/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py
new file mode 100644
index 0000000000..bfeaaa17df
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py
@@ -0,0 +1,932 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify recursive import among Tenant VRFs.
+2. Verify that dynamic import works fine between two different Tenant VRFs.
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+3. Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+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,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_routes_between_two_tenant_vrf_p0(request):
+ """
+ Verify that dynamic import works fine between two different Tenant VRFs.
+
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import from vrf GREEN+BLUE into vrf RED on R3")
+
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R1, that it installs all the routes(local+imported) in "
+ "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, "
+ "as all vrfs on R1 are using same IP address for next-hop"
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0]
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", static_routes, next_hop=next_hop_1
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE "
+ "and default".format(action)
+ )
+ bgp_disable = {"r3": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "default"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r3-link1": {"shutdown": value}}
+ },
+ "r2": {
+ "dest_link": {"r3-link1": {"shutdown": value}}
+ },
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r3"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 3, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3-link1": {"shutdown": value}}},
+ "r2": {"dest_link": {"r3-link1": {"shutdown": value}}},
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import command from router R3 and configure the same on R2")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that once import commands are removed from R3, all imported "
+ "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3"
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step(
+ "Configure static routes on R2 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after import commands are re-configured on R2's vrf RED, all "
+ "those routes are installed again in vrf RED of R1,R2,R3"
+ )
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2"
+ )
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format(
+ action
+ )
+ )
+ bgp_disable = {"r2": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "default"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {"r2-link1": {"shutdown": value}}
+ }
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r2"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 2, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R2-R3 for vrf RED".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ 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/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py
new file mode 100644
index 0000000000..1d80a2a64a
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py
@@ -0,0 +1,932 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify recursive import among Tenant VRFs.
+2. Verify that dynamic import works fine between two different Tenant VRFs.
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+3. Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+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,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request):
+ """
+ Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3")
+
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R3 that it installs all the routes(imported from tenant "
+ "VRFs) in default vrf. Additionally, verify that R1 & R2 also "
+ "receive these routes from R3 and install in default vrf, next-hop "
+ "pointing to R3"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ for dut in ["r2", "r1"]:
+ next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_val
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_val
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Remove", "Add"], [True, False], ["withdraw", "re-install"]
+ ):
+ step(
+ "{} import vrf GREEN/BLUE/RED/all command from default vrf "
+ "instance on R3".format(action)
+ )
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"import": {"vrf": vrf_name, "delete": value}}
+ }
+ }
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all"
+ " in default vrf's RIB".format(status)
+ )
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN "
+ "and BLUE".format(action)
+ )
+ bgp_disable = {"r3": {"bgp": []}}
+ for vrf_name in ["RED", "GREEN", "BLUE"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r3-link4": {"shutdown": value}}
+ },
+ "r2": {
+ "dest_link": {"r3-link4": {"shutdown": value}}
+ },
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r3"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 3, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when peering is shutdown for tenant vrfs, it "
+ "doesn't impact the RIB/FIB of default vrf on router R1 and R2"
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step(
+ "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action)
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3-link4": {"shutdown": value}}},
+ "r2": {"dest_link": {"r3-link4": {"shutdown": value}}},
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from default vrf's RIB"
+ " and FIB".format(status)
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import command from router R3 and configure the same on R2")
+ temp = {}
+ for vrf_name in VRF_LIST:
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that once import commands are removed from R3, all imported "
+ "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3"
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {} on r2".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after import commands are re-configured on R2's vrf RED, all "
+ "those routes are installed again in default vrf of R1,R2,R3"
+ )
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2")
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1, R2 and R3 withdraws imported routes from default "
+ "vrf's RIB and FIB "
+ )
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2")
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format(
+ action
+ )
+ )
+ bgp_disable = {"r2": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "RED"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {"r2-link4": {"shutdown": value}}
+ }
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r2"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 2, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R2-R3 for default vrf".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r2-link4": {"shutdown": value}}}
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from default vrfs RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r3"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ 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/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py
deleted file mode 100644
index 97016caa75..0000000000
--- a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py
+++ /dev/null
@@ -1,1909 +0,0 @@
-#!/usr/bin/env python
-
-#
-# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
-1. Verify recursive import among Tenant VRFs.
-2. Verify that dynamic import works fine between two different Tenant VRFs.
- When next-hop IPs are same across all VRFs.
- When next-hop IPs are different across all VRFs.
-3. Verify that with multiple tenant VRFs, dynamic import works fine between
- Tenant VRFs to default VRF.
- When next-hop IPs and prefixes are same across all VRFs.
- When next-hop IPs and prefixes are different across all VRFs.
-"""
-
-import os
-import sys
-import time
-import pytest
-import platform
-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.topogen import Topogen, get_topogen
-from lib.topotest import version_cmp
-
-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,
- create_static_routes,
- create_prefix_lists,
- create_bgp_community_lists,
- get_frr_ipv6_linklocal,
-)
-
-from lib.topolog import logger
-from lib.bgp import (
- verify_bgp_convergence,
- create_router_bgp,
- verify_bgp_community,
- verify_bgp_rib,
-)
-from lib.topojson import build_config_from_json
-
-pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
-
-# Global variables
-NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
-NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
-NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
-NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
-NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
-NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
-
-NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
-NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
-NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
-NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
-NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
-NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
-
-NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
-NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
-
-PREFIX_LIST = {
- "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
- "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
-}
-PREFERRED_NEXT_HOP = "global"
-VRF_LIST = ["RED", "BLUE", "GREEN"]
-COMM_VAL_1 = "100:100"
-COMM_VAL_2 = "500:500"
-COMM_VAL_3 = "600:600"
-
-
-def setup_module(mod):
- """
- Sets up the pytest environment
-
- * `mod`: module name
- """
-
- 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...
- json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD)
- tgen = Topogen(json_file, mod.__name__)
- global topo
- topo = tgen.json_topo
- # ... and here it calls Mininet initialization functions.
-
- # Starting topology, create tmp files which are loaded to routers
- # to start daemons and then start routers
- start_topology(tgen)
-
- # Run these tests for kernel version 4.19 or above
- if version_cmp(platform.release(), "4.19") < 0:
- error_msg = (
- "BGP vrf dynamic route leak tests will not run "
- '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
- )
- pytest.skip(error_msg)
-
- # Creating configuration from JSON
- build_config_from_json(tgen, topo)
-
- 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("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 test_dynamic_import_recursive_import_tenant_vrf_p1(request):
- """
- Verify recursive import among Tenant VRFs.
- """
-
- tgen = get_topogen()
- tc_name = request.node.name
- write_test_header(tc_name)
- reset_config_on_routers(tgen)
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- step(
- "Configure static routes on R2 for vrf RED and redistribute in "
- "respective BGP instance"
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
-
- result = create_static_routes(tgen, static_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Redistribute static route on BGP VRF RED")
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
- )
-
- redist_dict = {
- "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step("Verify that R2 has installed redistributed routes in vrf RED only")
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {
- "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r2", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Import vrf RED's routes into vrf GREEN on R2")
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
-
- import_dict = {
- "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
- }
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step(
- "Verify on R2, that it installs imported routes from vrf RED to vrf "
- "GREEN's RIB/FIB pointing next-hop to vrf RED"
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {
- "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r2", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("On R3 import routes from vrf GREEN to vrf default")
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
-
- import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step(
- "Verify on R3, that it installs imported routes from vrf GREEN to "
- "vrf default RIB/FIB pointing next-hop to vrf GREEN. "
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
- }
- result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("On R4 import routes from vrf default to vrf BLUE")
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}})
-
- import_dict = {
- "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]}
- }
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
-
- step(
- "Verify on R4, that it installs imported routes from vrf default to "
- "vrf BLUE RIB/FIB pointing next-hop to vrf default."
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r4": {
- "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r4", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for dut, vrf_name, vrf_import, as_num in zip(
- ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4]
- ):
-
- for action, value in zip(["Delete", "Re-add"], [True, False]):
- step("{} the import command on {} router".format(action, dut))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {"import": {"vrf": vrf_import, "delete": value}}
- }
- }
- )
-
- import_dict = {
- dut: {
- "bgp": [
- {"vrf": vrf_name, "local_as": as_num, "address_family": temp}
- ]
- }
- }
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r4": {
- "static_routes": [
- {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}
- ]
- }
- }
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, "r4", static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes["r4"]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, "r4", static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name,
- result,
- static_routes["r4"]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r4", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- write_test_footer(tc_name)
-
-
-def test_dynamic_import_routes_between_two_tenant_vrf_p0(request):
- """
- Verify that dynamic import works fine between two different Tenant VRFs.
-
- When next-hop IPs are same across all VRFs.
- When next-hop IPs are different across all VRFs.
- """
-
- tgen = get_topogen()
- tc_name = request.node.name
- write_test_header(tc_name)
- reset_config_on_routers(tgen)
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- step(
- "Configure static routes on R3 for each vrf and redistribute in "
- "respective BGP instance"
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step("Configure static route for VRF : {}".format(vrf_name))
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = create_static_routes(tgen, static_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Redistribute static route on BGP VRF : {}".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
- )
-
- redist_dict = {
- "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step(
- "Verify that R3 has installed redistributed routes in respective "
- "vrfs: {}".format(vrf_name)
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = verify_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Import from vrf GREEN+BLUE into vrf RED on R3")
-
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- import_dict = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify on R1, that it installs all the routes(local+imported) in "
- "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, "
- "as all vrfs on R1 are using same IP address for next-hop"
- )
-
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
-
- next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0]
- result = verify_bgp_rib(
- tgen, addr_type, "r1", static_routes, next_hop=next_hop_1
- )
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3")
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
- )
-
- import_dict = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
- for dut in ["r1", "r2", "r3"]:
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in BGP table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in Route table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3")
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- import_dict = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for dut in ["r1", "r2", "r3"]:
- step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value in zip(["Shut", "No shut"], [True, False]):
- step(
- "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE "
- "and default".format(action)
- )
- bgp_disable = {"r3": {"bgp": []}}
- for vrf_name in ["GREEN", "BLUE", "default"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r1": {
- "dest_link": {"r3-link1": {"shutdown": value}}
- },
- "r2": {
- "dest_link": {"r3-link1": {"shutdown": value}}
- },
- }
- }
- }
- }
- )
-
- bgp_disable["r3"]["bgp"].append(
- {"vrf": vrf_name, "local_as": 3, "address_family": temp}
- )
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value, status in zip(
- ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
- ):
- step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r1": {"dest_link": {"r3-link1": {"shutdown": value}}},
- "r2": {"dest_link": {"r3-link1": {"shutdown": value}}},
- }
- }
- }
- }
- )
-
- bgp_disable = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
- " FIB".format(status)
- )
- for dut in ["r1", "r2"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
-
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Remove import command from router R3 and configure the same on R2")
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
- )
-
- import_dict = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that once import commands are removed from R3, all imported "
- "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3"
- )
-
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in BGP table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
-
- step(
- "Configure static routes on R2 for each vrf and redistribute in "
- "respective BGP instance"
- )
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step("Configure static route for VRF : {}".format(vrf_name))
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = create_static_routes(tgen, static_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Redistribute static route on BGP VRF : {}".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
- )
-
- redist_dict = {
- "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "redistribute": [{"redist_type": "static", "delete": True}]
- }
- }
- }
- )
-
- redist_dict = {
- "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- import_dict = {
- "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify after import commands are re-configured on R2's vrf RED, all "
- "those routes are installed again in vrf RED of R1,R2,R3"
- )
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step(
- "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2"
- )
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
- )
-
- redist_dict = {
- "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
- for dut in ["r1", "r2", "r3"]:
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in BGP table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
-
- step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2")
- for vrf_name in ["BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- redist_dict = {
- "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for dut in ["r1", "r2", "r3"]:
- step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value in zip(["Shut", "No shut"], [True, False]):
- step(
- "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format(
- action
- )
- )
- bgp_disable = {"r2": {"bgp": []}}
- for vrf_name in ["GREEN", "BLUE", "default"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r3": {
- "dest_link": {"r2-link1": {"shutdown": value}}
- }
- }
- }
- }
- }
- )
-
- bgp_disable["r2"]["bgp"].append(
- {"vrf": vrf_name, "local_as": 2, "address_family": temp}
- )
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value, status in zip(
- ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
- ):
- step("{} the neighborship between R2-R3 for vrf RED".format(action))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}
- }
- }
- }
- }
- )
-
- bgp_disable = {
- "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
- }
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
- " FIB".format(status)
- )
- for dut in ["r1", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- "vrf": "RED",
- }
- ]
- }
- }
-
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- write_test_footer(tc_name)
-
-
-def test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request):
- """
- Verify that with multiple tenant VRFs, dynamic import works fine between
- Tenant VRFs to default VRF.
-
- When next-hop IPs and prefixes are same across all VRFs.
- When next-hop IPs and prefixes are different across all VRFs.
- """
-
- tgen = get_topogen()
- tc_name = request.node.name
- write_test_header(tc_name)
- reset_config_on_routers(tgen)
- if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
-
- step(
- "Configure static routes on R3 for each vrf and redistribute in "
- "respective BGP instance"
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step("Configure static route for VRF : {}".format(vrf_name))
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = create_static_routes(tgen, static_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Redistribute static route on BGP VRF : {}".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
- )
-
- redist_dict = {
- "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step(
- "Verify that R3 has installed redistributed routes in respective "
- "vrfs: {}".format(vrf_name)
- )
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = verify_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3")
-
- for vrf_name in ["RED", "BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify on R3 that it installs all the routes(imported from tenant "
- "VRFs) in default vrf. Additionally, verify that R1 & R2 also "
- "receive these routes from R3 and install in default vrf, next-hop "
- "pointing to R3"
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r3": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- }
- ]
- }
- }
-
- for dut in ["r2", "r1"]:
- next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][
- addr_type
- ].split("/")[0]
-
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, next_hop=next_hop_val
- )
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, next_hop=next_hop_val
- )
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, "r3", static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value, status in zip(
- ["Remove", "Add"], [True, False], ["withdraw", "re-install"]
- ):
- step(
- "{} import vrf GREEN/BLUE/RED/all command from default vrf "
- "instance on R3".format(action)
- )
- for vrf_name in ["RED", "BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {"import": {"vrf": vrf_name, "delete": value}}
- }
- }
- )
-
- import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all"
- " in default vrf's RIB".format(status)
- )
- for dut in ["r1", "r2", "r3"]:
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
-
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value in zip(["Shut", "No shut"], [True, False]):
- step(
- "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN "
- "and BLUE".format(action)
- )
- bgp_disable = {"r3": {"bgp": []}}
- for vrf_name in ["RED", "GREEN", "BLUE"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r1": {
- "dest_link": {"r3-link4": {"shutdown": value}}
- },
- "r2": {
- "dest_link": {"r3-link4": {"shutdown": value}}
- },
- }
- }
- }
- }
- )
-
- bgp_disable["r3"]["bgp"].append(
- {"vrf": vrf_name, "local_as": 3, "address_family": temp}
- )
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that when peering is shutdown for tenant vrfs, it "
- "doesn't impact the RIB/FIB of default vrf on router R1 and R2"
- )
- for dut in ["r1", "r2"]:
- step("Verify RIB/FIB for default vrf on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value, status in zip(
- ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
- ):
- step(
- "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action)
- )
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r1": {"dest_link": {"r3-link4": {"shutdown": value}}},
- "r2": {"dest_link": {"r3-link4": {"shutdown": value}}},
- }
- }
- }
- }
- )
-
- bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1 and R2 {} all the routes from default vrf's RIB"
- " and FIB".format(status)
- )
- for dut in ["r1", "r2"]:
- step("Verify RIB/FIB for default vrf on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
-
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Remove import command from router R3 and configure the same on R2")
- temp = {}
- for vrf_name in VRF_LIST:
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
- )
-
- import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that once import commands are removed from R3, all imported "
- "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3"
- )
-
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for default vrf on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in BGP table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
- step("Configure static route for VRF : {} on r2".format(vrf_name))
- for addr_type in ADDR_TYPES:
- static_routes = {
- "r2": {
- "static_routes": [
- {
- "network": [network[addr_type]],
- "next_hop": "blackhole",
- "vrf": vrf_name,
- }
- ]
- }
- }
-
- result = create_static_routes(tgen, static_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Redistribute static route on BGP VRF : {}".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
- )
-
- redist_dict = {
- "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "redistribute": [{"redist_type": "static", "delete": True}]
- }
- }
- }
- )
-
- redist_dict = {
- "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
- }
-
- result = create_router_bgp(tgen, topo, redist_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for vrf_name in ["RED", "BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify after import commands are re-configured on R2's vrf RED, all "
- "those routes are installed again in default vrf of R1,R2,R3"
- )
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2")
- for vrf_name in ["RED", "BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
- )
-
- import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1, R2 and R3 withdraws imported routes from default "
- "vrf's RIB and FIB "
- )
- for dut in ["r1", "r2", "r3"]:
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, (
- "Testcase {} : Failed \nError {}\n"
- "Routes {} still in BGP table".format(
- tc_name, result, static_routes[dut]["static_routes"][0]["network"]
- )
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
- assert result is not True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2")
- for vrf_name in ["RED", "BLUE", "GREEN"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
-
- import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
-
- result = create_router_bgp(tgen, topo, import_dict)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- for dut in ["r1", "r2", "r3"]:
- step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value in zip(["Shut", "No shut"], [True, False]):
- step(
- "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format(
- action
- )
- )
- bgp_disable = {"r2": {"bgp": []}}
- for vrf_name in ["GREEN", "BLUE", "RED"]:
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r3": {
- "dest_link": {"r2-link4": {"shutdown": value}}
- }
- }
- }
- }
- }
- )
-
- bgp_disable["r2"]["bgp"].append(
- {"vrf": vrf_name, "local_as": 2, "address_family": temp}
- )
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
- for dut in ["r1", "r2", "r3"]:
- step("Verify RIB/FIB for vrf RED on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
-
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- for action, value, status in zip(
- ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
- ):
- step("{} the neighborship between R2-R3 for default vrf".format(action))
- temp = {}
- for addr_type in ADDR_TYPES:
- temp.update(
- {
- addr_type: {
- "unicast": {
- "neighbor": {
- "r3": {"dest_link": {"r2-link4": {"shutdown": value}}}
- }
- }
- }
- }
- )
-
- bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
- result = create_router_bgp(tgen, topo, bgp_disable)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
- )
-
- step(
- "Verify that R1 and R2 {} all the routes from default vrfs RIB and"
- " FIB".format(status)
- )
- for dut in ["r1", "r3"]:
- step("Verify RIB/FIB for default vrf on {}".format(dut))
- for addr_type in ADDR_TYPES:
- static_routes = {
- dut: {
- "static_routes": [
- {
- "network": [
- NETWORK1_1[addr_type],
- NETWORK2_1[addr_type],
- NETWORK3_1[addr_type],
- ],
- "next_hop": "blackhole",
- }
- ]
- }
- }
-
- if value:
- result = verify_bgp_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
-
- result = verify_rib(
- tgen, addr_type, dut, static_routes, expected=False
- )
- assert (
- result is not True
- ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
- tc_name,
- result,
- static_routes[dut]["static_routes"][0]["network"],
- )
- else:
- result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
- assert result is True, "Testcase {} : Failed \n Error {}".format(
- tc_name, result
- )
-
- result = verify_rib(tgen, addr_type, dut, static_routes)
- 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/route_scale/scale_test_common.py b/tests/topotests/route_scale/scale_test_common.py
index 856a2d0fa7..b9f324d561 100644
--- a/tests/topotests/route_scale/scale_test_common.py
+++ b/tests/topotests/route_scale/scale_test_common.py
@@ -178,7 +178,7 @@ def route_install_helper(iter):
# Table of defaults, used for timeout values and 'expected' objects
scale_defaults = dict(
- zip(scale_keys, [None, None, 7, 30, expected_installed, expected_removed])
+ zip(scale_keys, [None, None, 10, 50, expected_installed, expected_removed])
)
# List of params for each step in the test; note extra time given
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index bf402e1bef..dfbc9b8008 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -1914,6 +1914,7 @@ if __name__ == "__main__":
"bgpd",
"fabricd",
"isisd",
+ "babeld",
"ospf6d",
"ospfd",
"pbrd",
@@ -1925,6 +1926,7 @@ if __name__ == "__main__":
"staticd",
"vrrpd",
"ldpd",
+ "nhrpd",
"pathd",
"bfdd",
"eigrpd",
diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang
index 08b6073479..d3e756e0bf 100644
--- a/yang/frr-bfdd.yang
+++ b/yang/frr-bfdd.yang
@@ -242,6 +242,36 @@ module frr-bfdd {
}
}
+ grouping bfd-monitoring {
+ description
+ "BFD monitoring template for protocol integration.";
+
+ leaf source {
+ type inet:ip-address;
+ description
+ "Source address to use for liveness check.
+
+ When source is not set and multi-hop is `false` the source
+ address will be `0.0.0.0` (any).
+
+ When source is not set and multi-hop is `true` the source
+ address will be automatic selected through Next Hop Tracking (NHT).";
+ }
+
+ leaf multi-hop {
+ description
+ "Use multi hop session instead of single hop.";
+ type boolean;
+ default false;
+ }
+
+ leaf profile {
+ description
+ "BFD pre configured profile.";
+ type frr-bfdd:profile-ref;
+ }
+ }
+
grouping session-states {
/*
* Local settings.
diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang
index 98ff3a83c6..cb5e25b877 100644
--- a/yang/frr-staticd.yang
+++ b/yang/frr-staticd.yang
@@ -15,6 +15,10 @@ module frr-staticd {
prefix inet;
}
+ import frr-bfdd {
+ prefix frr-bfdd;
+ }
+
organization
"FRRouting";
contact
@@ -114,7 +118,19 @@ module frr-staticd {
"AFI-SAFI type.";
}
- uses staticd-prefix-attributes;
+ uses staticd-prefix-attributes {
+ augment "path-list/frr-nexthops/nexthop" {
+ container bfd-monitoring {
+ description "BFD monitoring options.";
+ presence
+ "Present if BFD configuration is available.";
+
+ when "../nh-type = 'ip4' or ../nh-type = 'ip4-ifindex' or
+ ../nh-type = 'ip6' or ../nh-type = 'ip6-ifindex'";
+ uses frr-bfdd:bfd-monitoring;
+ }
+ }
+ }
list src-list {
key "src-prefix";
diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h
index f8e843cc73..2eed7f301c 100644
--- a/zebra/zebra_ptm.h
+++ b/zebra/zebra_ptm.h
@@ -63,14 +63,12 @@ struct zebra_ptm_cb {
#define ZEBRA_IF_PTM_ENABLE_ON 1
#define ZEBRA_IF_PTM_ENABLE_UNSPEC 2
-#define IS_BFD_ENABLED_PROTOCOL(protocol) ( \
- (protocol) == ZEBRA_ROUTE_BGP || \
- (protocol) == ZEBRA_ROUTE_OSPF || \
- (protocol) == ZEBRA_ROUTE_OSPF6 || \
- (protocol) == ZEBRA_ROUTE_ISIS || \
- (protocol) == ZEBRA_ROUTE_PIM || \
- (protocol) == ZEBRA_ROUTE_OPENFABRIC \
-)
+#define IS_BFD_ENABLED_PROTOCOL(protocol) \
+ ((protocol) == ZEBRA_ROUTE_BGP || (protocol) == ZEBRA_ROUTE_OSPF || \
+ (protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS || \
+ (protocol) == ZEBRA_ROUTE_PIM || \
+ (protocol) == ZEBRA_ROUTE_OPENFABRIC || \
+ (protocol) == ZEBRA_ROUTE_STATIC)
void zebra_ptm_init(void);
void zebra_ptm_finish(void);