diff options
-rw-r--r-- | bgpd/bgp_route.c | 269 | ||||
-rw-r--r-- | bgpd/bgp_route.h | 13 | ||||
-rw-r--r-- | bgpd/bgp_routemap.c | 34 | ||||
-rw-r--r-- | doc/user/bgp.rst | 11 | ||||
-rw-r--r-- | tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg | 4 | ||||
-rw-r--r-- | tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf | 18 | ||||
-rw-r--r-- | tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py | 87 |
7 files changed, 370 insertions, 66 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index ae50808b6..eba355fad 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -115,6 +115,14 @@ DEFINE_HOOK(bgp_process, struct peer *peer, bool withdraw), (bgp, afi, safi, bn, peer, withdraw)) +/** Test if path is suppressed. */ +static bool bgp_path_suppressed(struct bgp_path_info *pi) +{ + if (pi->extra == NULL || pi->extra->aggr_suppressors == NULL) + return false; + + return listcount(pi->extra->aggr_suppressors) > 0; +} struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix *p, @@ -1704,10 +1712,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, } /* Aggregate-address suppress check. */ - if (pi->extra && pi->extra->suppress) - if (!UNSUPPRESS_MAP_NAME(filter)) { - return false; - } + if (bgp_path_suppressed(pi) && !UNSUPPRESS_MAP_NAME(filter)) + return false; /* * If we are doing VRF 2 VRF leaking via the import @@ -1944,7 +1950,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, bgp_peer_as_override(bgp, afi, safi, peer, attr); /* Route map & unsuppress-map apply. */ - if (ROUTE_MAP_OUT_NAME(filter) || (pi->extra && pi->extra->suppress)) { + if (ROUTE_MAP_OUT_NAME(filter) || bgp_path_suppressed(pi)) { struct bgp_path_info rmap_path = {0}; struct bgp_path_info_extra dummy_rmap_path_extra = {0}; struct attr dummy_attr = {0}; @@ -1969,7 +1975,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT); - if (pi->extra && pi->extra->suppress) + if (bgp_path_suppressed(pi)) ret = route_map_apply(UNSUPPRESS_MAP(filter), p, RMAP_BGP, &rmap_path); else @@ -6176,11 +6182,119 @@ static struct bgp_aggregate *bgp_aggregate_new(void) static void bgp_aggregate_free(struct bgp_aggregate *aggregate) { + XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->suppress_map_name); + route_map_counter_decrement(aggregate->suppress_map); XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->rmap.name); route_map_counter_decrement(aggregate->rmap.map); XFREE(MTYPE_BGP_AGGREGATE, aggregate); } +/** + * Helper function to avoid repeated code: prepare variables for a + * `route_map_apply` call. + * + * \returns `true` on route map match, otherwise `false`. + */ +static bool aggr_suppress_map_test(struct bgp *bgp, + struct bgp_aggregate *aggregate, + struct bgp_path_info *pi) +{ + const struct prefix *p = bgp_dest_get_prefix(pi->net); + route_map_result_t rmr = RMAP_DENYMATCH; + struct bgp_path_info rmap_path = {}; + struct attr attr = {}; + + /* No route map entries created, just don't match. */ + if (aggregate->suppress_map == NULL) + return false; + + /* Call route map matching and return result. */ + attr.aspath = aspath_empty(); + rmap_path.peer = bgp->peer_self; + rmap_path.attr = &attr; + + SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_AGGREGATE); + rmr = route_map_apply(aggregate->suppress_map, p, RMAP_BGP, &rmap_path); + bgp->peer_self->rmap_type = 0; + + bgp_attr_flush(&attr); + + return rmr == RMAP_PERMITMATCH; +} + +/** Test whether the aggregation has suppressed this path or not. */ +static bool aggr_suppress_exists(struct bgp_aggregate *aggregate, + struct bgp_path_info *pi) +{ + if (pi->extra == NULL || pi->extra->aggr_suppressors == NULL) + return false; + + return listnode_lookup(pi->extra->aggr_suppressors, aggregate) != NULL; +} + +/** + * Suppress this path and keep the reference. + * + * \returns `true` if needs processing otherwise `false`. + */ +static bool aggr_suppress_path(struct bgp_aggregate *aggregate, + struct bgp_path_info *pi) +{ + struct bgp_path_info_extra *pie; + + /* Path is already suppressed by this aggregation. */ + if (aggr_suppress_exists(aggregate, pi)) + return false; + + pie = bgp_path_info_extra_get(pi); + + /* This is the first suppression, allocate memory and list it. */ + if (pie->aggr_suppressors == NULL) + pie->aggr_suppressors = list_new(); + + listnode_add(pie->aggr_suppressors, aggregate); + + /* Only mark for processing if suppressed. */ + if (listcount(pie->aggr_suppressors) == 1) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("aggregate-address suppressing: %pFX", + bgp_dest_get_prefix(pi->net)); + + bgp_path_info_set_flag(pi->net, pi, BGP_PATH_ATTR_CHANGED); + return true; + } + + return false; +} + +/** + * Unsuppress this path and remove the reference. + * + * \returns `true` if needs processing otherwise `false`. + */ +static bool aggr_unsuppress_path(struct bgp_aggregate *aggregate, + struct bgp_path_info *pi) +{ + /* Path wasn't suppressed. */ + if (!aggr_suppress_exists(aggregate, pi)) + return false; + + listnode_delete(pi->extra->aggr_suppressors, aggregate); + + /* Unsuppress and free extra memory if last item. */ + if (listcount(pi->extra->aggr_suppressors) == 0) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("aggregate-address unsuppressing: %pFX", + bgp_dest_get_prefix(pi->net)); + + list_delete(&pi->extra->aggr_suppressors); + bgp_path_info_set_flag(pi->net, pi, BGP_PATH_ATTR_CHANGED); + return true; + } + + return false; +} + static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, struct aspath *aspath, struct community *comm, @@ -6375,13 +6489,11 @@ static bool bgp_aggregate_test_all_med(struct bgp_aggregate *aggregate, * Toggles the route suppression status for this aggregate address * configuration. */ -static void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate, - struct bgp *bgp, - const struct prefix *p, afi_t afi, - safi_t safi, bool suppress) +void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate, + struct bgp *bgp, const struct prefix *p, + afi_t afi, safi_t safi, bool suppress) { struct bgp_table *table = bgp->rib[afi][safi]; - struct bgp_path_info_extra *pie; const struct prefix *dest_p; struct bgp_dest *dest, *top; struct bgp_path_info *pi; @@ -6402,32 +6514,17 @@ static void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate, if (pi->sub_type == BGP_ROUTE_AGGREGATE) continue; - /* - * On installation it is possible that pi->extra is - * set to NULL, otherwise it must exists. - */ - assert(!suppress && pi->extra != NULL); - /* We are toggling suppression back. */ if (suppress) { - pie = bgp_path_info_extra_get(pi); /* Suppress route if not suppressed already. */ - pie->suppress++; - bgp_path_info_set_flag(dest, pi, - BGP_PATH_ATTR_CHANGED); - toggle_suppression = true; + if (aggr_suppress_path(aggregate, pi)) + toggle_suppression = true; continue; } - pie = pi->extra; - assert(pie->suppress > 0); - pie->suppress--; /* Install route if there is no more suppression. */ - if (pie->suppress == 0) { - bgp_path_info_set_flag(dest, pi, - BGP_PATH_ATTR_CHANGED); + if (aggr_unsuppress_path(aggregate, pi)) toggle_suppression = true; - } } if (toggle_suppression) @@ -6514,6 +6611,17 @@ void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, if (aggregate->match_med) bgp_aggregate_test_all_med(aggregate, bgp, p, afi, safi); + /* + * Reset aggregate count: we might've been called from route map + * update so in that case we must retest all more specific routes. + * + * \see `bgp_route_map_process_update`. + */ + aggregate->count = 0; + aggregate->incomplete_origin_count = 0; + aggregate->incomplete_origin_count = 0; + aggregate->egp_origin_count = 0; + /* ORIGIN attribute: If at least one route among routes that are aggregated has ORIGIN with the value INCOMPLETE, then the aggregated route must have the ORIGIN attribute with the value @@ -6558,10 +6666,24 @@ void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, */ if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate)) { - (bgp_path_info_extra_get(pi))->suppress++; - bgp_path_info_set_flag(dest, pi, - BGP_PATH_ATTR_CHANGED); - match++; + if (aggr_suppress_path(aggregate, pi)) + match++; + } + + /* + * Suppress more specific routes that match the route + * map results. + * + * MED matching: + * Don't suppress routes if MED matching is enabled and + * it mismatched otherwise we might end up with no + * routes for this path. + */ + if (aggregate->suppress_map_name + && AGGREGATE_MED_VALID(aggregate) + && aggr_suppress_map_test(bgp, aggregate, pi)) { + if (aggr_suppress_path(aggregate, pi)) + match++; } aggregate->count++; @@ -6701,15 +6823,17 @@ void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi, if (aggregate->summary_only && pi->extra && AGGREGATE_MED_VALID(aggregate)) { - pi->extra->suppress--; + if (aggr_unsuppress_path(aggregate, pi)) + match++; + } - if (pi->extra->suppress == 0) { - bgp_path_info_set_flag( - dest, pi, - BGP_PATH_ATTR_CHANGED); + if (aggregate->suppress_map_name + && AGGREGATE_MED_VALID(aggregate) + && aggr_suppress_map_test(bgp, aggregate, pi)) { + if (aggr_unsuppress_path(aggregate, pi)) match++; - } } + aggregate->count--; if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE) @@ -6800,7 +6924,11 @@ static void bgp_add_route_to_aggregate(struct bgp *bgp, pinew, true); if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate)) - (bgp_path_info_extra_get(pinew))->suppress++; + aggr_suppress_path(aggregate, pinew); + + if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate) + && aggr_suppress_map_test(bgp, aggregate, pinew)) + aggr_suppress_path(aggregate, pinew); switch (pinew->attr->origin) { case BGP_ORIGIN_INCOMPLETE: @@ -6896,19 +7024,17 @@ static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi, if (pi->sub_type == BGP_ROUTE_AGGREGATE) return; - if (aggregate->summary_only && pi->extra && pi->extra->suppress > 0 - && AGGREGATE_MED_VALID(aggregate)) { - pi->extra->suppress--; + if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate)) + if (aggr_unsuppress_path(aggregate, pi)) + match++; - if (pi->extra->suppress == 0) { - bgp_path_info_set_flag(pi->net, pi, - BGP_PATH_ATTR_CHANGED); + if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate) + && aggr_suppress_map_test(bgp, aggregate, pi)) + if (aggr_unsuppress_path(aggregate, pi)) match++; - } - } /* - * This must be called after `summary` check to avoid + * This must be called after `summary`, `suppress-map` check to avoid * "unsuppressing" twice. */ if (aggregate->match_med) @@ -7171,7 +7297,8 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, safi_t safi, const char *rmap, uint8_t summary_only, uint8_t as_set, - uint8_t origin, bool match_med) + uint8_t origin, bool match_med, + const char *suppress_map) { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; @@ -7180,6 +7307,12 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, struct bgp_aggregate *aggregate; uint8_t as_set_new = as_set; + if (suppress_map && summary_only) { + vty_out(vty, + "'summary-only' and 'suppress-map' can't be used at the same time\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Convert string to prefix structure. */ ret = str2prefix(prefix_str, &p); if (!ret) { @@ -7251,6 +7384,18 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, aggregate->rmap.map = route_map_lookup_by_name(rmap); route_map_counter_increment(aggregate->rmap.map); } + + if (suppress_map) { + XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->suppress_map_name); + route_map_counter_decrement(aggregate->suppress_map); + + aggregate->suppress_map_name = + XSTRDUP(MTYPE_ROUTE_MAP_NAME, suppress_map); + aggregate->suppress_map = + route_map_lookup_by_name(aggregate->suppress_map_name); + route_map_counter_increment(aggregate->suppress_map); + } + bgp_dest_set_bgp_aggregate_info(dest, aggregate); /* Aggregate address insert into BGP routing table. */ @@ -7266,6 +7411,7 @@ DEFPY(aggregate_addressv4, aggregate_addressv4_cmd, "|route-map WORD$rmap_name" "|origin <egp|igp|incomplete>$origin_s" "|matching-MED-only$match_med" + "|suppress-map WORD$suppress_map" "}", NO_STR "Configure BGP aggregate entries\n" @@ -7278,7 +7424,9 @@ DEFPY(aggregate_addressv4, aggregate_addressv4_cmd, "Remote EGP\n" "Local IGP\n" "Unknown heritage\n" - "Only aggregate routes with matching MED\n") + "Only aggregate routes with matching MED\n" + "Suppress the selected more specific routes\n" + "Route map with the route selectors\n") { const char *prefix_s = NULL; safi_t safi = bgp_node_safi(vty); @@ -7314,7 +7462,7 @@ DEFPY(aggregate_addressv4, aggregate_addressv4_cmd, return bgp_aggregate_set(vty, prefix_s, AFI_IP, safi, rmap_name, summary_only != NULL, as_set, origin, - match_med != NULL); + match_med != NULL, suppress_map); } DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, @@ -7324,6 +7472,7 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, "|route-map WORD$rmap_name" "|origin <egp|igp|incomplete>$origin_s" "|matching-MED-only$match_med" + "|suppress-map WORD$suppress_map" "}", NO_STR "Configure BGP aggregate entries\n" @@ -7336,7 +7485,9 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, "Remote EGP\n" "Local IGP\n" "Unknown heritage\n" - "Only aggregate routes with matching MED\n") + "Only aggregate routes with matching MED\n" + "Suppress the selected more specific routes\n" + "Route map with the route selectors\n") { uint8_t origin = BGP_ORIGIN_UNSPECIFIED; int as_set = AGGREGATE_AS_UNSET; @@ -7360,7 +7511,7 @@ DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, return bgp_aggregate_set(vty, prefix_str, AFI_IP6, SAFI_UNICAST, rmap_name, summary_only != NULL, as_set, - origin, match_med != NULL); + origin, match_med != NULL, suppress_map); } /* Redistribute route treatment. */ @@ -7668,7 +7819,7 @@ static void route_vty_short_status_out(struct vty *vty, if (CHECK_FLAG(path->flags, BGP_PATH_STALE)) json_object_boolean_true_add(json_path, "stale"); - if (path->extra && path->extra->suppress) + if (path->extra && bgp_path_suppressed(path)) json_object_boolean_true_add(json_path, "suppressed"); if (CHECK_FLAG(path->flags, BGP_PATH_VALID) @@ -7705,7 +7856,7 @@ static void route_vty_short_status_out(struct vty *vty, vty_out(vty, "R"); else if (CHECK_FLAG(path->flags, BGP_PATH_STALE)) vty_out(vty, "S"); - else if (path->extra && path->extra->suppress) + else if (bgp_path_suppressed(path)) vty_out(vty, "s"); else if (CHECK_FLAG(path->flags, BGP_PATH_VALID) && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) @@ -10433,7 +10584,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, count++; if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) { best = count; - if (pi->extra && pi->extra->suppress) + if (bgp_path_suppressed(pi)) suppress = 1; if (pi->attr->community == NULL) @@ -13998,6 +14149,10 @@ void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp_aggregate->match_med) vty_out(vty, " matching-MED-only"); + if (bgp_aggregate->suppress_map_name) + vty_out(vty, " suppress-map %s", + bgp_aggregate->suppress_map_name); + vty_out(vty, "\n"); } } diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 09a040209..4a4959298 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -110,8 +110,8 @@ struct bgp_path_info_extra { /* Pointer to dampening structure. */ struct bgp_damp_info *damp_info; - /* This route is suppressed with aggregation. */ - int suppress; + /** List of aggregations that suppress this path. */ + struct list *aggr_suppressors; /* Nexthop reachability check. */ uint32_t igpmetric; @@ -398,6 +398,11 @@ struct bgp_aggregate { #define AGGREGATE_MED_VALID(aggregate) \ (((aggregate)->match_med && !(aggregate)->med_mismatched) \ || !(aggregate)->match_med) + + /** Suppress map route map name (`NULL` when disabled). */ + char *suppress_map_name; + /** Suppress map route map pointer. */ + struct route_map *suppress_map; }; #define BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen) \ @@ -710,4 +715,8 @@ extern bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, struct attr *attr, struct bgp_dest *dest); extern int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, struct bgp_path_info *exist, int *paths_eq); +extern void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate, + struct bgp *bgp, + const struct prefix *p, afi_t afi, + safi_t safi, bool suppress); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 0e26576c1..c65574393 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3746,6 +3746,7 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, int route_update) { int i; + bool matched; afi_t afi; safi_t safi; struct peer *peer; @@ -3845,16 +3846,35 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, if (!aggregate) continue; - if (!aggregate->rmap.name - || (strcmp(rmap_name, aggregate->rmap.name) != 0)) - continue; + matched = false; - if (!aggregate->rmap.map) - route_map_counter_increment(map); + /* Update suppress map pointer. */ + if (aggregate->suppress_map_name + && strmatch(aggregate->suppress_map_name, + rmap_name)) { + if (aggregate->rmap.map == NULL) + route_map_counter_increment(map); - aggregate->rmap.map = map; + aggregate->suppress_map = map; + + bgp_aggregate_toggle_suppressed( + aggregate, bgp, bgp_dest_get_prefix(bn), + afi, safi, false); + + matched = true; + } + + if (aggregate->rmap.name + && strmatch(rmap_name, aggregate->rmap.name)) { + if (aggregate->rmap.map == NULL) + route_map_counter_increment(map); + + aggregate->rmap.map = map; + + matched = true; + } - if (route_update) { + if (matched && route_update) { const struct prefix *bn_p = bgp_dest_get_prefix(bn); diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index fb0ca10da..79e5afde7 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1005,6 +1005,12 @@ Route Aggregation-IPv4 Address Family Configure the aggregated address to only be created when the routes MED match, otherwise no aggregated route will be created. +.. index:: aggregate-address A.B.C.D/M suppress-map NAME +.. clicmd:: aggregate-address A.B.C.D/M suppress-map NAME + + Similar to `summary-only`, but will only suppress more specific routes that + are matched by the selected route-map. + .. index:: no aggregate-address A.B.C.D/M .. clicmd:: no aggregate-address A.B.C.D/M @@ -1063,6 +1069,11 @@ Route Aggregation-IPv6 Address Family Configure the aggregated address to only be created when the routes MED match, otherwise no aggregated route will be created. +.. index:: aggregate-address X:X::X:X/M suppress-map NAME +.. clicmd:: aggregate-address X:X::X:X/M suppress-map NAME + + Similar to `summary-only`, but will only suppress more specific routes that + are matched by the selected route-map. .. index:: no aggregate-address X:X::X:X/M .. clicmd:: no aggregate-address X:X::X:X/M diff --git a/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg index 277c6603a..e0f6ab601 100644 --- a/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg +++ b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg @@ -13,5 +13,9 @@ neighbor 10.0.0.1 { route 192.168.1.1/32 next-hop 10.0.0.2 med 10; route 192.168.1.2/32 next-hop 10.0.0.2 med 10; route 192.168.1.3/32 next-hop 10.0.0.2 med 20; + + route 192.168.2.1/32 next-hop 10.0.0.2; + route 192.168.2.2/32 next-hop 10.0.0.2; + route 192.168.2.3/32 next-hop 10.0.0.2; } } diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf index 4e1406177..fa5215008 100644 --- a/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf +++ b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf @@ -1,3 +1,20 @@ +debug bgp updates +! +access-list acl-sup-one seq 5 permit 192.168.2.1/32 +access-list acl-sup-one seq 10 deny any +! +access-list acl-sup-two seq 5 permit 192.168.2.2/32 +access-list acl-sup-two seq 10 deny any +! +access-list acl-sup-three seq 5 permit 192.168.2.3/32 +access-list acl-sup-three seq 10 deny any +! +route-map rm-sup-one permit 10 + match ip address acl-sup-one +! +route-map rm-sup-two permit 10 + match ip address acl-sup-two +! router bgp 65000 no bgp ebgp-requires-policy neighbor 10.0.0.2 remote-as 65001 @@ -8,5 +25,6 @@ router bgp 65000 redistribute connected aggregate-address 192.168.0.0/24 matching-MED-only aggregate-address 192.168.1.0/24 matching-MED-only + aggregate-address 192.168.2.0/24 suppress-map rm-sup-one exit-address-family ! diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py index d3656b870..3f3b71dea 100644 --- a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py +++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py @@ -85,6 +85,20 @@ def teardown_module(mod): tgen.stop_topology() +def expect_route(router_name, routes_expected): + "Helper function to avoid repeated code." + tgen = get_topogen() + test_func = functools.partial( + topotest.router_json_cmp, + tgen.gears[router_name], + "show ip route json", + routes_expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=120, wait=1) + assertmsg = '"{}" BGP convergence failure'.format(router_name) + assert result is None, assertmsg + + def test_expect_convergence(): "Test that BGP protocol converged." @@ -185,6 +199,79 @@ aggregate-address 192.168.1.0/24 matching-MED-only summary-only assert result is None, assertmsg +def test_bgp_aggregate_address_suppress_map(): + "Test that the command suppress-map works." + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + expect_route('r2', { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": None, + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": [{"protocol": "bgp"}], + }) + + # Change route map and test again. + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +router bgp 65000 +address-family ipv4 unicast +no aggregate-address 192.168.2.0/24 suppress-map rm-sup-one +aggregate-address 192.168.2.0/24 suppress-map rm-sup-two +""" + ) + + expect_route('r2', { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": None, + "192.168.2.3/32": [{"protocol": "bgp"}], + }) + + +def test_bgp_aggregate_address_suppress_map_update_route_map(): + "Test that the suppress-map late route map creation works." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +router bgp 65000 +address-family ipv4 unicast +no aggregate-address 192.168.2.0/24 suppress-map rm-sup-two +aggregate-address 192.168.2.0/24 suppress-map rm-sup-three +""" + ) + + expect_route('r2', { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": [{"protocol": "bgp"}], + }) + + # Create missing route map and test again. + tgen.gears["r1"].vtysh_multicmd( + """ +configure terminal +route-map rm-sup-three permit 10 +match ip address acl-sup-three +""" + ) + + expect_route('r2', { + "192.168.2.0/24": [{"protocol": "bgp"}], + "192.168.2.1/32": [{"protocol": "bgp"}], + "192.168.2.2/32": [{"protocol": "bgp"}], + "192.168.2.3/32": None, + }) + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() |