diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 07b3b4143b..521c8056f5 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -222,11 +222,11 @@ static int bgp_conditional_adv_timer(struct thread *t) * validation. */ if (filter->advmap.condition == CONDITION_EXIST) - filter->advmap.advertise = + filter->advmap.update_type = (ret == RMAP_PERMITMATCH) ? ADVERTISE : WITHDRAW; else - filter->advmap.advertise = + filter->advmap.update_type = (ret == RMAP_PERMITMATCH) ? WITHDRAW : ADVERTISE; @@ -249,7 +249,7 @@ static int bgp_conditional_adv_timer(struct thread *t) /* Send update as per the conditional advertisement */ bgp_conditional_adv_routes(peer, afi, safi, table, filter->advmap.amap, - filter->advmap.advertise); + filter->advmap.update_type); } peer->advmap_table_change = false; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 17a4411c82..bb202718ac 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -10774,6 +10774,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, json_object *json_prefA = NULL; json_object *json_prefB = NULL; json_object *json_addr = NULL; + json_object *json_advmap = NULL; if (use_json) { json_addr = json_object_new_object(); @@ -11048,6 +11049,26 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, "selectiveUnsuppressRouteMap", filter->usmap.name); + /* advertise-map */ + if (filter->advmap.aname) { + json_advmap = json_object_new_object(); + json_object_string_add(json_advmap, "condition", + filter->advmap.condition + ? "EXIST" + : "NON_EXIST"); + json_object_string_add(json_advmap, "conditionMap", + filter->advmap.cname); + json_object_string_add(json_advmap, "advertiseMap", + filter->advmap.aname); + json_object_string_add(json_advmap, "advertiseStatus", + filter->advmap.update_type + == ADVERTISE + ? "Advertise" + : "Withdraw"); + json_object_object_add(json_addr, "advertiseMap", + json_advmap); + } + /* Receive prefix count */ json_object_int_add(json_addr, "acceptedPrefixCounter", p->pcount[afi][safi]); @@ -11353,7 +11374,7 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, filter->advmap.cname, filter->advmap.amap ? "*" : "", filter->advmap.aname, - filter->advmap.advertise == ADVERTISE + filter->advmap.update_type == ADVERTISE ? "Advertise" : "Withdraw"); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 0e8970d685..db72eb1775 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -6586,57 +6586,53 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi) return 0; } -static void peer_update_rmap_filter_data(struct peer *peer, afi_t afi, - safi_t safi, const char *rmap_name1, - struct route_map *rmap1, - const char *rmap_name2, - struct route_map *rmap2, - uint8_t config_flags) +static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi, + safi_t safi, const char *amap_name, + struct route_map *amap, + const char *cmap_name, + struct route_map *cmap, + bool condition, bool set) { struct bgp_filter *filter; bool filter_exists = false; filter = &peer->filter[afi][safi]; - if (CHECK_FLAG(config_flags, BGP_PEER_ADVERTISE_MAP)) { - /* advertise-map is already configured. */ - if (filter->advmap.aname) { - filter_exists = true; - XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname); - XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname); - } - route_map_counter_decrement(filter->advmap.amap); + /* advertise-map is already configured. */ + if (filter->advmap.aname) { + filter_exists = true; + XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname); + XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname); + } - /* Removed advertise-map configuration */ - if (!CHECK_FLAG(config_flags, BGP_PEER_RMAP_SET)) { - memset(filter, 0, sizeof(struct bgp_filter)); + route_map_counter_decrement(filter->advmap.amap); - /* decrement condition_filter_count delete timer if - * this is the last advertise-map to be removed. - */ - if (filter_exists) - bgp_conditional_adv_disable(peer, afi, safi); + /* Removed advertise-map configuration */ + if (!set) { + memset(filter, 0, sizeof(struct bgp_filter)); - return; - } + /* decrement condition_filter_count delete timer if + * this is the last advertise-map to be removed. + */ + if (filter_exists) + bgp_conditional_adv_disable(peer, afi, safi); - /* Update filter data with newly configured values. */ - filter->advmap.aname = - XSTRDUP(MTYPE_BGP_FILTER_NAME, rmap_name1); - filter->advmap.cname = - XSTRDUP(MTYPE_BGP_FILTER_NAME, rmap_name2); - filter->advmap.amap = rmap1; - filter->advmap.cmap = rmap2; - filter->advmap.condition = - CHECK_FLAG(config_flags, BGP_PEER_CONDITION_EXIST); - route_map_counter_increment(filter->advmap.amap); - peer->advmap_config_change[afi][safi] = true; + return; + } - /* Increment condition_filter_count and/or create timer. */ - if (!filter_exists) { - filter->advmap.advertise = ADVERTISE; - bgp_conditional_adv_enable(peer, afi, safi); - } + /* Update filter data with newly configured values. */ + filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, amap_name); + filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, cmap_name); + filter->advmap.amap = amap; + filter->advmap.cmap = cmap; + filter->advmap.condition = condition; + route_map_counter_increment(filter->advmap.amap); + peer->advmap_config_change[afi][safi] = true; + + /* Increment condition_filter_count and/or create timer. */ + if (!filter_exists) { + filter->advmap.update_type = ADVERTISE; + bgp_conditional_adv_enable(peer, afi, safi); } } @@ -6653,19 +6649,13 @@ int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi, const char *condition_name, struct route_map *condition_map, bool condition) { - uint8_t config_flags = 0; struct peer *member; struct listnode *node, *nnode; - SET_FLAG(config_flags, BGP_PEER_RMAP_SET); - SET_FLAG(config_flags, BGP_PEER_ADVERTISE_MAP); - if (condition) - SET_FLAG(config_flags, BGP_PEER_CONDITION_EXIST); - /* Set configuration on peer. */ - peer_update_rmap_filter_data(peer, afi, safi, advertise_name, - advertise_map, condition_name, - condition_map, config_flags); + peer_advertise_map_filter_update(peer, afi, safi, advertise_name, + advertise_map, condition_name, + condition_map, condition, true); /* Check if handling a regular peer & Skip peer-group mechanics. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -6686,9 +6676,9 @@ int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi, continue; /* Set configuration on peer-group member. */ - peer_update_rmap_filter_data(member, afi, safi, advertise_name, - advertise_map, condition_name, - condition_map, config_flags); + peer_advertise_map_filter_update( + member, afi, safi, advertise_name, advertise_map, + condition_name, condition_map, condition, true); } return 0; @@ -6701,13 +6691,12 @@ int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi, const char *condition_name, struct route_map *condition_map, bool condition) { - uint8_t config_flags = 0; struct peer *member; struct listnode *node, *nnode; - SET_FLAG(config_flags, BGP_PEER_ADVERTISE_MAP); - if (condition) - SET_FLAG(config_flags, BGP_PEER_CONDITION_EXIST); + /* advertise-map is not configured */ + if (!peer->filter[afi][safi].advmap.aname) + return 0; /* Unset override-flag unconditionally. */ UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT], @@ -6721,9 +6710,9 @@ int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi, PEER_ATTR_INHERIT(peer, peer->group, filter[afi][safi].advmap.amap); } else - peer_update_rmap_filter_data(peer, afi, safi, advertise_name, - advertise_map, condition_name, - condition_map, config_flags); + peer_advertise_map_filter_update( + peer, afi, safi, advertise_name, advertise_map, + condition_name, condition_map, condition, false); /* Check if handling a regular peer and skip peer-group mechanics. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -6742,9 +6731,9 @@ int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi, PEER_FT_ADVERTISE_MAP)) continue; /* Remove configuration on peer-group member. */ - peer_update_rmap_filter_data(member, afi, safi, advertise_name, - advertise_map, condition_name, - condition_map, config_flags); + peer_advertise_map_filter_update( + member, afi, safi, advertise_name, advertise_map, + condition_name, condition_map, condition, false); /* Process peer route updates. */ peer_on_policy_change(member, afi, safi, 1); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 39efab2899..4eb5bdd018 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -767,14 +767,6 @@ struct bgp_nexthop { #define CONDITION_NON_EXIST false #define CONDITION_EXIST true -/* BGP peer RMAP options */ -#define BGP_PEER_ADVERTISE_MAP (1 << 0) -#define BGP_PEER_ROUTE_MAP (1 << 1) -#define BGP_PEER_UNSUPPRESS_MAP (1 << 2) -#define BGP_PEER_CONDITION_EXIST (1 << 3) -#define BGP_PEER_RMAP_DIRECTION (1 << 4) -#define BGP_PEER_RMAP_SET (1 << 5) - enum update_type { WITHDRAW, ADVERTISE }; #include "filter.h" @@ -821,7 +813,7 @@ struct bgp_filter { char *cname; struct route_map *cmap; - enum update_type advertise; + enum update_type update_type; } advmap; }; diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index f79d33a260..339be7f4d6 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2633,16 +2633,22 @@ when the next instance of the BGP scanner occurs. .. index:: [no] neighbor A.B.C.D advertise-map NAME [exist-map|non-exist-map] NAME .. clicmd:: [no] neighbor A.B.C.D advertise-map NAME [exist-map|non-exist-map] NAME - This command enables BGP scanner process to monitor route specified by + This command enables BGP scanner process to monitor routes specified by exist-map or non-exist-map command in BGP table and conditionally advertises - the route specified by advertise-map command. + the routes specified by advertise-map command. Sample Configuration ^^^^^^^^^^^^^^^^^^^^^ .. code-block:: frr + interface enp0s9 + ip address 10.10.10.2/24 + ! + interface enp0s10 + ip address 10.10.20.2/24 + ! interface lo - ip address 2.2.2.2/24 + ip address 203.0.113.1/32 ! router bgp 2 bgp log-neighbor-changes @@ -2651,125 +2657,131 @@ Sample Configuration neighbor 10.10.20.3 remote-as 3 ! address-family ipv4 unicast - network 2.2.2.0/24 - network 20.20.0.0/16 neighbor 10.10.10.1 soft-reconfiguration inbound - neighbor 10.10.10.1 advertise-map ADVERTISE non-exist-map CONDITION neighbor 10.10.20.3 soft-reconfiguration inbound + neighbor 10.10.20.3 advertise-map ADV-MAP non-exist-map EXIST-MAP exit-address-family ! - access-list CONDITION seq 5 permit 3.3.3.0/24 - access-list ADVERTISE seq 5 permit 2.2.2.0/24 + ip prefix-list DEFAULT seq 5 permit 192.0.2.5/32 + ip prefix-list DEFAULT seq 10 permit 192.0.2.1/32 + ip prefix-list EXIST seq 5 permit 10.10.10.10/32 + ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0 + ip prefix-list IP1 seq 5 permit 10.139.224.0/20 ! - route-map ADVERTISE permit 10 - match ip address ADVERTISE + bgp community-list standard DC-ROUTES seq 5 permit 64952:3008 + bgp community-list standard DC-ROUTES seq 10 permit 64671:501 + bgp community-list standard DC-ROUTES seq 15 permit 64950:3009 + bgp community-list standard DEFAULT-ROUTE seq 5 permit 65013:200 ! - route-map CONDITION permit 10 - match ip address CONDITION + route-map ADV-MAP permit 10 + match ip address prefix-list IP1 + ! + route-map ADV-MAP permit 20 + match community DC-ROUTES + ! + route-map EXIST-MAP permit 10 + match community DEFAULT-ROUTE + match ip address prefix-list DEFAULT-ROUTE ! Sample Output ^^^^^^^^^^^^^ -When 3.3.3.0/24 route is in R2'2 BGP rable, 2.2.2/0/24 is not adevrtised to R1. +When default route is present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2.1/32 are not advertised to R3. .. code-block:: frr Router2# show ip bgp - BGP table version is 24, local router ID is 128.16.16.1, vrf id 0 + BGP table version is 20, local router ID is 203.0.113.1, vrf id 0 Default local pref 100, local AS 2 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete - Network Next Hop Metric LocPrf Weight Path - *> 1.1.1.0/24 10.10.10.1 0 0 1 i - *> 2.2.2.0/24 0.0.0.0 0 32768 i - *> 3.3.3.0/24 10.10.20.3 0 0 3 i - *> 20.20.0.0/16 0.0.0.0 0 32768 i + Network Next Hop Metric LocPrf Weight Path + *> 0.0.0.0/0 10.10.10.1 0 0 1 i + *> 10.139.224.0/20 10.10.10.1 0 0 1 ? + *> 192.0.2.1/32 10.10.10.1 0 0 1 i + *> 192.0.2.5/32 10.10.10.1 0 0 1 i Displayed 4 routes and 4 total paths - Router2# - - Router2# show ip bgp neighbors 10.10.10.1 + Router2# show ip bgp neighbors 10.10.20.3 !--- Output suppressed. For address family: IPv4 Unicast - Update group 5, subgroup 1 + Update group 7, subgroup 7 Packet Queue length 0 Inbound soft reconfiguration allowed Community attribute sent to this neighbor(all) - Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Withdraw - 1 accepted prefixes + Condition NON_EXIST, Condition-map *EXIST-MAP, Advertise-map *ADV-MAP, status: Withdraw + 0 accepted prefixes !--- Output suppressed. - Router2# show ip bgp neighbors 10.10.10.1 advertised-routes - BGP table version is 24, local router ID is 128.16.16.1, vrf id 0 + Router2# show ip bgp neighbors 10.10.20.3 advertised-routes + BGP table version is 20, local router ID is 203.0.113.1, vrf id 0 Default local pref 100, local AS 2 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, - i internal, r RIB-failure, S Stale, R Removed + i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete - Network Next Hop Metric LocPrf Weight Path - *> 1.1.1.0/24 0.0.0.0 0 1 i - *> 3.3.3.0/24 0.0.0.0 0 3 i - *> 20.20.0.0/16 0.0.0.0 0 32768 i + Network Next Hop Metric LocPrf Weight Path + *> 0.0.0.0/0 0.0.0.0 0 1 i + *> 192.0.2.5/32 0.0.0.0 0 1 i - Total number of prefixes 3 - Router2# + Total number of prefixes 2 -When 3.3.3.0/24 route is not in R2'2 BGP rable, 2.2.2/0/24 is adevrtised to R1. +When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2.1/32 are advertised to R3. .. code-block:: frr Router2# show ip bgp - BGP table version is 25, local router ID is 128.16.16.1, vrf id 0 + BGP table version is 21, local router ID is 203.0.113.1, vrf id 0 Default local pref 100, local AS 2 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete - Network Next Hop Metric LocPrf Weight Path - *> 1.1.1.0/24 10.10.10.1 0 0 1 i - *> 2.2.2.0/24 0.0.0.0 0 32768 i - *> 20.20.0.0/16 0.0.0.0 0 32768 i + Network Next Hop Metric LocPrf Weight Path + *> 10.139.224.0/20 10.10.10.1 0 0 1 ? + *> 192.0.2.1/32 10.10.10.1 0 0 1 i + *> 192.0.2.5/32 10.10.10.1 0 0 1 i Displayed 3 routes and 3 total paths - Router2# - Router2# show ip bgp neighbors 10.10.10.1 + Router2# show ip bgp neighbors 10.10.20.3 !--- Output suppressed. For address family: IPv4 Unicast - Update group 5, subgroup 1 + Update group 7, subgroup 7 Packet Queue length 0 Inbound soft reconfiguration allowed Community attribute sent to this neighbor(all) - Condition NON_EXIST, Condition-map *CONDITION, Advertise-map *ADVERTISE, status: Advertise - 1 accepted prefixes + Condition NON_EXIST, Condition-map *EXIST-MAP, Advertise-map *ADV-MAP, status: Advertise + 0 accepted prefixes !--- Output suppressed. - Router2# show ip bgp neighbors 10.10.10.1 advertised-routes - BGP table version is 25, local router ID is 128.16.16.1, vrf id 0 + Router2# show ip bgp neighbors 10.10.20.3 advertised-routes + BGP table version is 21, local router ID is 203.0.113.1, vrf id 0 Default local pref 100, local AS 2 Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete - Network Next Hop Metric LocPrf Weight Path - *> 1.1.1.0/24 0.0.0.0 0 1 i - *> 2.2.2.0/24 0.0.0.0 0 32768 i - *> 20.20.0.0/16 0.0.0.0 0 32768 i + Network Next Hop Metric LocPrf Weight Path + *> 10.139.224.0/20 0.0.0.0 0 1 ? + *> 192.0.2.1/32 0.0.0.0 0 1 i + *> 192.0.2.5/32 0.0.0.0 0 1 i Total number of prefixes 3 + Router2# .. _bgp-debugging: diff --git a/tests/topotests/bgp_conditional_advertisement/__init__.py b/tests/topotests/bgp_conditional_advertisement/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf new file mode 100644 index 0000000000..633d1832fd --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf @@ -0,0 +1,30 @@ +! +ip prefix-list CUST seq 5 permit 10.139.224.0/20 +ip prefix-list DEFAULT seq 5 permit 0.0.0.0/0 +ip prefix-list PL1 seq 5 permit 192.0.2.1/32 +! +route-map CUST permit 10 + match ip address prefix-list CUST + set community 64671:501 +! +route-map RM1 permit 10 + match ip address prefix-list PL1 + set community 64952:3008 +! +route-map DEF permit 10 + match ip address prefix-list DEFAULT + set community 64848:3011 65011:200 65013:200 +! +router bgp 1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 10.10.10.2 remote-as 2 + ! + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + network 192.0.2.1/32 route-map RM1 + network 192.0.2.5/32 + redistribute connected route-map CUST + neighbor 10.10.10.2 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf new file mode 100644 index 0000000000..bb887e41ad --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf @@ -0,0 +1,19 @@ +! +hostname Router1 +! +ip route 0.0.0.0/0 blackhole +ip route 192.0.2.1/32 blackhole +ip route 192.0.2.2/32 blackhole +ip route 192.0.2.3/32 blackhole +ip route 192.0.2.4/32 blackhole +ip route 192.0.2.5/32 blackhole +! +interface r1-eth0 + ip address 10.10.10.1/24 +! +interface lo + ip address 10.139.224.1/20 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf new file mode 100644 index 0000000000..c8f4357f99 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf @@ -0,0 +1,33 @@ +! +ip prefix-list DEFAULT seq 5 permit 192.0.2.5/32 +ip prefix-list DEFAULT seq 10 permit 192.0.2.1/32 +ip prefix-list EXIST seq 5 permit 10.10.10.10/32 +ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0 +ip prefix-list IP1 seq 5 permit 10.139.224.0/20 +! +bgp community-list standard DC-ROUTES seq 5 permit 64952:3008 +bgp community-list standard DC-ROUTES seq 10 permit 64671:501 +bgp community-list standard DC-ROUTES seq 15 permit 64950:3009 +bgp community-list standard DEFAULT-ROUTE seq 5 permit 65013:200 +! +route-map ADV-MAP permit 10 + match ip address prefix-list IP1 +! +route-map ADV-MAP permit 20 + match community DC-ROUTES +! +route-map EXIST-MAP permit 10 + match community DEFAULT-ROUTE + match ip address prefix-list DEFAULT-ROUTE +! +router bgp 2 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 10.10.10.1 remote-as 1 + neighbor 10.10.20.3 remote-as 3 + ! + address-family ipv4 unicast + neighbor 10.10.10.1 soft-reconfiguration inbound + neighbor 10.10.20.3 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf new file mode 100644 index 0000000000..434ab68e3c --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf @@ -0,0 +1,15 @@ +! +hostname Router2 +! +interface r2-eth0 + ip address 10.10.10.2/24 +! +interface r2-eth1 + ip address 10.10.20.2/24 +! +interface lo + ip address 203.0.113.1/32 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf new file mode 100644 index 0000000000..2f4f5068d8 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 3 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 10.10.20.2 remote-as 2 + ! + address-family ipv4 unicast + neighbor 10.10.20.2 soft-reconfiguration inbound + exit-address-family +! + diff --git a/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf new file mode 100644 index 0000000000..0dadfdb3a9 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf @@ -0,0 +1,12 @@ +! +hostname Router3 +! +interface r3-eth0 + ip address 10.10.20.3/24 +! +interface lo + ip address 198.51.100.1/32 +! +ip forwarding +ipv6 forwarding +! diff --git a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py new file mode 100644 index 0000000000..e509882448 --- /dev/null +++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py @@ -0,0 +1,585 @@ +#!/usr/bin/env python + +# +# test_bgp_conditional_advertisement.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# 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 BGP conditional advertisement functionality. + + +--------+ +--------+ +--------+ + | | | | | | + | R1 |------------| R2 |------------| R3 | + | | | | | | + +--------+ +--------+ +--------+ + +R2 is DUT and peers with R1 and R3 in default bgp instance. + +Following tests are covered under BGP conditional advertisement functionality. +Conditional advertisement +------------------------- +TC11: R3 BGP convergence, without advertise-map configuration. + All routes are advertised to R3. +TC21: exist-map routes present in R2's BGP table. + advertise-map routes present in R2's BGP table are advertised to R3. +TC22: exist-map routes not present in R2's BGP table + advertise-map routes present in R2's BGP table are withdrawn from R3. +TC31: non-exist-map routes not present in R2's BGP table + advertise-map routes present in R2's BGP table are advertised to R3. +TC32: non-exist-map routes present in R2's BGP table + advertise-map routes present in R2's BGP table are withdrawn from R3. +TC41: non-exist-map route-map configuration removed in R2. + advertise-map routes present in R2's BGP table are advertised to R3. +TC42: exist-map route-map configuration removed in R2 + advertise-map routes present in R2's BGP table are withdrawn from R3. + +Conditional advertisement along with Route-map Filter +----------------------------------------------------- +TC51: exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC52: exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. +TC53: non-exist-map routes present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC54: non-exist-map routes present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. +TC61: exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 including advertise-map routes. +TC62: exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 except advertise-map routes. +TC63: non-exist-map routes not present in R2's BGP table, with route-map filter. + All routes are withdrawn from R3 except advertise-map routes. +TC64: non-exist-map routes not present in R2's BGP table, without route-map filter. + All routes are advertised to R3 including advertise-map routes. + + +i.e. ++----------------+-------------------------+------------------------+ +| Routes in | exist-map status | advertise-map status | +| BGP table | | | ++----------------+-------------------------+------------------------+ +| Present | Condition matched | Advertise | ++----------------+-------------------------+------------------------+ +| Not Present | Condition not matched | Withdrawn | ++----------------+-------------------------+------------------------+ +| | non-exist-map status | advertise-map status | +| | | | ++----------------+-------------------------+------------------------+ +| Present | Condition matched | Withdrawn | ++----------------+-------------------------+------------------------+ +| Not Present | Condition not matched | Advertise | ++----------------+-------------------------+------------------------+ +Here in this topology, based on the default route presence in R2 and +the configured condition-map (exist-map/non-exist-map) 10.139.224.0/20 +will be either advertised/withdrawn to/from R3. +""" + +import os +import sys +import json +import time +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.topolog import logger +from mininet.topo import Topo + + +class BgpConditionalAdvertisementTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + r1 = tgen.add_router("r1") + r2 = tgen.add_router("r2") + r3 = tgen.add_router("r3") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(r2) + + switch = tgen.add_switch("s2") + switch.add_link(r2) + switch.add_link(r3) + + +def setup_module(mod): + 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") + + tgen = Topogen(BgpConditionalAdvertisementTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + logger.info("Running setup_module() done") + + +def teardown_module(mod): + """ + Teardown the pytest environment + * `mod`: module name + """ + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +def test_bgp_conditional_advertisement(): + """ + Test BGP conditional advertisement functionality. + """ + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + router3 = tgen.gears["r3"] + + passed = "PASSED!!!" + failed = "FAILED!!!" + + def _all_routes_advertised(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + def _all_routes_withdrawn(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": None, + "10.139.224.0/20": None, + } + return topotest.json_cmp(output, expected) + + def _exist_map_routes_present(router): + return _all_routes_advertised(router) + + def _exist_map_routes_not_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + } + return topotest.json_cmp(output, expected) + + def _non_exist_map_routes_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": [{"protocol": "bgp"}], + "192.0.2.1/32": None, + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": None, + } + return topotest.json_cmp(output, expected) + + def _non_exist_map_routes_not_present(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": [{"protocol": "bgp"}], + "10.139.224.0/20": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + def _exist_map_no_condition_route_map(router): + return _non_exist_map_routes_present(router) + + def _non_exist_map_no_condition_route_map(router): + return _all_routes_advertised(router) + + def _exist_map_routes_present_rmap_filter(router): + output = json.loads(router.vtysh_cmd("show ip route json")) + expected = { + "0.0.0.0/0": None, + "192.0.2.1/32": [{"protocol": "bgp"}], + "192.0.2.5/32": None, + "10.139.224.0/20": [{"protocol": "bgp"}], + } + return topotest.json_cmp(output, expected) + + def _exist_map_routes_present_no_rmap_filter(router): + return _all_routes_advertised(router) + + def _non_exist_map_routes_present_rmap_filter(router): + return _all_routes_withdrawn(router) + + def _non_exist_map_routes_present_no_rmap_filter(router): + return _non_exist_map_routes_present(router) + + def _exist_map_routes_not_present_rmap_filter(router): + return _all_routes_withdrawn(router) + + def _exist_map_routes_not_present_no_rmap_filter(router): + return _exist_map_routes_not_present(router) + + def _non_exist_map_routes_not_present_rmap_filter(router): + return _exist_map_routes_present_rmap_filter(router) + + def _non_exist_map_routes_not_present_no_rmap_filter(router): + return _non_exist_map_routes_not_present(router) + + # TC11: R3 BGP convergence, without advertise-map configuration. + # All routes are advertised to R3. + test_func = functools.partial(_all_routes_advertised, router3) + success, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + + msg = 'TC11: "router3" BGP convergence - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC21: exist-map routes present in R2's BGP table. + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(_exist_map_routes_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC21: exist-map routes present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC22: exist-map routes not present in R2's BGP table + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + + test_func = functools.partial(_exist_map_routes_not_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC22: exist-map routes not present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC31: non-exist-map routes not present in R2's BGP table + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(_non_exist_map_routes_not_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC31: non-exist-map routes not present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC32: non-exist-map routes present in R2's BGP table + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + network 0.0.0.0/0 route-map DEF + """ + ) + + test_func = functools.partial(_non_exist_map_routes_present, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC32: non-exist-map routes present in "router2" BGP table - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC41: non-exist-map route-map configuration removed in R2. + # advertise-map routes present in R2's BGP table are advertised to R3. + router2.vtysh_cmd( + """ + configure terminal + no route-map EXIST-MAP permit 10 + """ + ) + + test_func = functools.partial(_non_exist_map_no_condition_route_map, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC41: non-exist-map route-map removed in "router2" - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC42: exist-map route-map configuration removed in R2 + # advertise-map routes present in R2's BGP table are withdrawn from R3. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 advertise-map ADV-MAP exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(_exist_map_no_condition_route_map, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = 'TC42: exist-map route-map removed in "router2" - ' + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC51: exist-map routes present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + route-map EXIST-MAP permit 10 + match community DEFAULT-ROUTE + match ip address prefix-list DEFAULT-ROUTE + ! + route-map RMAP deny 10 + match ip address prefix-list IP1 + ! + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP out + """ + ) + + test_func = functools.partial(_exist_map_routes_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC51: exist-map routes present with route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC52: exist-map routes present in R2's BGP table, no route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP out + """ + ) + + test_func = functools.partial(_exist_map_routes_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC52: exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC53: non-exist-map routes present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP out + neighbor 10.10.20.3 advertise-map ADV-MAP non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(_non_exist_map_routes_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC53: non-exist-map routes present, with route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC54: non-exist-map routes present in R2's BGP table, no route-map filter. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP out + """ + ) + + test_func = functools.partial(_non_exist_map_routes_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC54: non-exist-map routes present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC61: exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 including advertise-map routes. + router1.vtysh_cmd( + """ + configure terminal + router bgp 1 + address-family ipv4 unicast + no network 0.0.0.0/0 route-map DEF + """ + ) + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP out + neighbor 10.10.20.3 advertise-map ADV-MAP exist-map EXIST-MAP + """ + ) + + test_func = functools.partial(_exist_map_routes_not_present_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC61: exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC62: exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP out + """ + ) + + test_func = functools.partial(_exist_map_routes_not_present_no_rmap_filter, router3) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC62: exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC63: non-exist-map routes not present in R2's BGP table, with route-map filter. + # All routes are withdrawn from R3 except advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + neighbor 10.10.20.3 route-map RMAP out + neighbor 10.10.20.3 advertise-map ADV-MAP non-exist-map EXIST-MAP + """ + ) + + test_func = functools.partial( + _non_exist_map_routes_not_present_rmap_filter, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC63: non-exist-map routes not present, route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + # TC64: non-exist-map routes not present in R2's BGP table, without route-map filter. + # All routes are advertised to R3 including advertise-map routes. + router2.vtysh_cmd( + """ + configure terminal + router bgp 2 + address-family ipv4 unicast + no neighbor 10.10.20.3 route-map RMAP out + """ + ) + + test_func = functools.partial( + _non_exist_map_routes_not_present_no_rmap_filter, router3 + ) + success, result = topotest.run_and_expect(test_func, None, count=90, wait=1) + + msg = "TC64: non-exist-map routes not present, no route-map filter - " + assert result is None, msg + failed + + logger.info(msg + passed) + + +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))