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;
+}
+
static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin,
struct aspath *aspath,
struct community *comm,
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)) {
+ (bgp_path_info_extra_get(pi))->suppress++;
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_ATTR_CHANGED);
+ match++;
+ }
+
aggregate->count++;
/*
match++;
}
}
+
+ if (aggregate->suppress_map_name
+ && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pi)) {
+ /*
+ * We can only get here if we were suppressed
+ * before: it is a failure to not have
+ * `pi->extra`.
+ */
+ assert(pi->extra != NULL);
+ /*
+ * If we suppressed before then the value must
+ * be greater than zero.
+ */
+ assert(pi->extra->suppress > 0);
+
+ pi->extra->suppress--;
+ /* Unsuppress route if we reached `0`. */
+ if (pi->extra->suppress == 0) {
+ bgp_path_info_set_flag(
+ dest, pi,
+ BGP_PATH_ATTR_CHANGED);
+ match++;
+ }
+ }
+
aggregate->count--;
if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate))
(bgp_path_info_extra_get(pinew))->suppress++;
+ if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pinew))
+ (bgp_path_info_extra_get(pinew))->suppress++;
+
switch (pinew->attr->origin) {
case BGP_ORIGIN_INCOMPLETE:
aggregate->incomplete_origin_count++;
}
}
+ if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pi)) {
+ /*
+ * We can only get here if we were suppressed before:
+ * it is a failure to not have `pi->extra`.
+ */
+ assert(pi->extra != NULL);
+ /*
+ * If we suppressed before then the value must be
+ * greater than zero.
+ */
+ assert(pi->extra->suppress > 0);
+
+ pi->extra->suppress--;
+ /* Unsuppress when we reached `0`. */
+ if (pi->extra->suppress == 0) {
+ bgp_path_info_set_flag(pi->net, pi,
+ BGP_PATH_ATTR_CHANGED);
+ 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)
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;
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) {
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. */
"|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"
"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);
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,
"|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"
"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;
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. */
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");
}
}