bgpd: add 'match extcommunity-count' command to restrict comm count

Add a mechanism in route-map to filter out route-map which have a list
of extended communities greater than the given number.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2025-02-14 09:24:20 +01:00
parent e27631e10a
commit c3084cacf4
8 changed files with 173 additions and 0 deletions

View File

@ -1358,6 +1358,65 @@ static const struct route_map_rule_cmd route_match_community_limit_cmd = {
route_match_community_limit_compile, route_match_community_limit_free
};
/* `match extcommunity-limit' */
/* Match function should return :
* - RMAP_MATCH if the bgp update extcommunity list count
* is less or equal to the configured limit.
* - RMAP_NOMATCH if the extcommunity list count is greater than the
* configured limit.
*/
static enum route_map_cmd_result_t
route_match_extcommunity_limit(void *rule, const struct prefix *prefix, void *object)
{
struct bgp_path_info *path = NULL;
struct ecommunity *piextcomm = NULL, *pi6extcomm = NULL;
uint16_t count = 0;
uint16_t *limit_rule = rule;
path = (struct bgp_path_info *)object;
piextcomm = bgp_attr_get_ecommunity(path->attr);
if (piextcomm)
count = piextcomm->size;
pi6extcomm = bgp_attr_get_ipv6_ecommunity(path->attr);
if (pi6extcomm)
count += pi6extcomm->size;
if (count <= *limit_rule)
return RMAP_MATCH;
return RMAP_NOMATCH;
}
/* Route map `extcommunity-limit' match statement. */
static void *route_match_extcommunity_limit_compile(const char *arg)
{
uint16_t *limit = NULL;
char *end = NULL;
limit = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint16_t));
*limit = strtoul(arg, &end, 10);
if (*end != '\0') {
XFREE(MTYPE_ROUTE_MAP_COMPILED, limit);
return NULL;
}
return limit;
}
/* Free route map's compiled `community-limit' value. */
static void route_match_extcommunity_limit_free(void *rule)
{
XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
}
/* Route map commands for community limit matching. */
static const struct route_map_rule_cmd route_match_extcommunity_limit_cmd = {
"extcommunity-limit", route_match_extcommunity_limit,
route_match_extcommunity_limit_compile, route_match_extcommunity_limit_free
};
static enum route_map_cmd_result_t
route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object)
{
@ -5885,6 +5944,27 @@ DEFPY_YANG (match_ecommunity,
}
DEFPY_YANG(
match_extcommunity_limit, match_extcommunity_limit_cmd,
"[no$no] match extcommunity-limit ![(0-65535)$limit]",
NO_STR
MATCH_STR
"Match BGP extended community limit\n"
"Extended community limit number\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:match-extcommunity-limit']";
char xpath_value[XPATH_MAXLEN];
nb_cli_enqueue_change(vty, xpath, no ? NB_OP_DESTROY : NB_OP_CREATE, NULL);
snprintf(xpath_value, sizeof(xpath_value),
"%s/rmap-match-condition/frr-bgp-route-map:extcommunity-limit", xpath);
nb_cli_enqueue_change(vty, xpath_value, no ? NB_OP_DESTROY : NB_OP_MODIFY, limit_str);
return nb_cli_apply_changes(vty, NULL);
}
DEFUN_YANG (no_match_ecommunity,
no_match_ecommunity_cmd,
"no match extcommunity [<(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME>]",
@ -7980,6 +8060,7 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_evpn_route_type_cmd);
route_map_install_match(&route_match_evpn_rd_cmd);
route_map_install_match(&route_match_community_limit_cmd);
route_map_install_match(&route_match_extcommunity_limit_cmd);
route_map_install_match(&route_match_evpn_default_route_cmd);
route_map_install_match(&route_match_vrl_source_vrf_cmd);
@ -8053,6 +8134,7 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &match_community_cmd);
install_element(RMAP_NODE, &no_match_community_cmd);
install_element(RMAP_NODE, &match_community_limit_cmd);
install_element(RMAP_NODE, &match_extcommunity_limit_cmd);
install_element(RMAP_NODE, &match_lcommunity_cmd);
install_element(RMAP_NODE, &no_match_lcommunity_cmd);
install_element(RMAP_NODE, &match_ecommunity_cmd);

View File

@ -221,6 +221,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_set_action_rmap_set_action_distance_destroy,
}
},
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:extcommunity-limit",
.cbs = {
.modify = lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_modify,
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_destroy,
}
},
{
.xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt",
.cbs = {

View File

@ -76,6 +76,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_mod
struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_destroy(
struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_modify(
struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_destroy(
struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create(
struct nb_cb_create_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy(

View File

@ -1666,6 +1666,57 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(
return NB_OK;
}
/*
* XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:extcommunity-limit
*/
int lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_modify(
struct nb_cb_modify_args *args)
{
struct routemap_hook_context *rhc;
const char *limit;
enum rmap_compile_rets ret;
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
/* Add configuration. */
rhc = nb_running_get_entry(args->dnode, NULL, true);
limit = yang_dnode_get_string(args->dnode, NULL);
rhc->rhc_mhook = bgp_route_match_delete;
rhc->rhc_rule = "extcommunity-limit";
rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
ret = bgp_route_match_add(rhc->rhc_rmi, "extcommunity-limit", limit,
RMAP_EVENT_MATCH_ADDED, args->errmsg, args->errmsg_len);
if (ret != RMAP_COMPILE_SUCCESS) {
rhc->rhc_mhook = NULL;
return NB_ERR_INCONSISTENCY;
}
}
return NB_OK;
}
int lib_route_map_entry_match_condition_rmap_match_condition_extcommunity_limit_destroy(
struct nb_cb_destroy_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
}
return NB_OK;
}
/*
* XPath:
* /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt

View File

@ -2920,6 +2920,13 @@ BGP Extended Communities in Route Map
.. clicmd:: match extcommunity WORD
.. clicmd:: match extcommunity-limit (0-65535)
This command matches BGP updates that use extended community list and IPv6
extended community list, and with an extended community list count less or
equal than the defined limit. Setting extended community-limit to 0 will
only match BGP updates with no extended community.
.. clicmd:: set extcommunity none
This command resets the extended community value in BGP updates. If the attribute is

View File

@ -317,6 +317,7 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-bgp-route-map:match-large-community"))
#define IS_MATCH_EXTCOMMUNITY(C) \
(strmatch(C, "frr-bgp-route-map:match-extcommunity"))
#define IS_MATCH_EXTCOMMUNITY_LIMIT(C) (strmatch(C, "frr-bgp-route-map:match-extcommunity-limit"))
#define IS_MATCH_IPV4_NH(C) \
(strmatch(C, "frr-bgp-route-map:ipv4-nexthop"))
#define IS_MATCH_IPV6_NH(C) \

View File

@ -715,6 +715,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-match-condition/frr-bgp-route-map:rpki"));
} else if (IS_MATCH_EXTCOMMUNITY_LIMIT(condition)) {
vty_out(vty, " match extcommunity-limit %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-bgp-route-map:extcommunity-limit"));
} else if (IS_MATCH_RPKI_EXTCOMMUNITY(condition)) {
vty_out(vty, " match rpki-extcommunity %s\n",
yang_dnode_get_string(

View File

@ -166,6 +166,12 @@ module frr-bgp-route-map {
"Match BGP extcommunity list";
}
identity match-extcommunity-limit {
base frr-route-map:rmap-match-type;
description
"Match BGP extcommunity limit count";
}
identity as-path-list {
base frr-route-map:rmap-match-type;
description
@ -819,6 +825,17 @@ identity set-extcommunity-color {
}
}
case extcommunity-limit {
when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-extcommunity-limit')";
description
"Match BGP updates when the list of extended communities count is less than the configured limit.";
leaf extcommunity-limit {
type uint16 {
range "0..1024";
}
}
}
case comm-list-name {
when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-community') or "
+ "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-large-community') or "