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

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

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2025-01-10 17:29:10 +01:00
parent ba4122d6db
commit f19b8668b3
8 changed files with 160 additions and 0 deletions

View file

@ -1303,6 +1303,61 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = {
route_match_rd_free route_match_rd_free
}; };
/* `match community-limit' */
/* Match function should return :
* - RMAP_MATCH if the bgp update community list count
* is less or equal to the configured limit.
* - RMAP_NOMATCH if the community list count is greater than the
* configured limit.
*/
static enum route_map_cmd_result_t
route_match_community_limit(void *rule, const struct prefix *prefix, void *object)
{
struct bgp_path_info *path = NULL;
struct community *picomm = NULL;
uint16_t count = 0;
uint16_t *limit_rule = rule;
path = (struct bgp_path_info *)object;
picomm = bgp_attr_get_community(path->attr);
if (picomm)
count = picomm->size;
if (count <= *limit_rule)
return RMAP_MATCH;
return RMAP_NOMATCH;
}
/* Route map `community-limit' match statement. */
static void *route_match_community_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_community_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_community_limit_cmd = {
"community-limit", route_match_community_limit,
route_match_community_limit_compile, route_match_community_limit_free
};
static enum route_map_cmd_result_t static enum route_map_cmd_result_t
route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object) route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object)
{ {
@ -5708,6 +5763,25 @@ DEFPY_YANG(
return nb_cli_apply_changes(vty, NULL); return nb_cli_apply_changes(vty, NULL);
} }
DEFPY_YANG(
match_community_limit, match_community_limit_cmd,
"[no$no] match community-limit ![(0-65535)$limit]",
NO_STR
MATCH_STR
"Match BGP community limit\n"
"Community limit number\n")
{
const char *xpath = "./match-condition[condition='frr-bgp-route-map:match-community-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:community-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( DEFUN_YANG(
no_match_community, no_match_community_cmd, no_match_community, no_match_community_cmd,
"no match community [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]]", "no match community [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]]",
@ -7906,6 +7980,7 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_evpn_vni_cmd); route_map_install_match(&route_match_evpn_vni_cmd);
route_map_install_match(&route_match_evpn_route_type_cmd); 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_evpn_rd_cmd);
route_map_install_match(&route_match_community_limit_cmd);
route_map_install_match(&route_match_evpn_default_route_cmd); route_map_install_match(&route_match_evpn_default_route_cmd);
route_map_install_match(&route_match_vrl_source_vrf_cmd); route_map_install_match(&route_match_vrl_source_vrf_cmd);
@ -7978,6 +8053,7 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_match_alias_cmd); install_element(RMAP_NODE, &no_match_alias_cmd);
install_element(RMAP_NODE, &match_community_cmd); install_element(RMAP_NODE, &match_community_cmd);
install_element(RMAP_NODE, &no_match_community_cmd); install_element(RMAP_NODE, &no_match_community_cmd);
install_element(RMAP_NODE, &match_community_limit_cmd);
install_element(RMAP_NODE, &match_lcommunity_cmd); install_element(RMAP_NODE, &match_lcommunity_cmd);
install_element(RMAP_NODE, &no_match_lcommunity_cmd); install_element(RMAP_NODE, &no_match_lcommunity_cmd);
install_element(RMAP_NODE, &match_ecommunity_cmd); install_element(RMAP_NODE, &match_ecommunity_cmd);

View file

@ -165,6 +165,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy, .destroy = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy,
} }
}, },
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:community-limit",
.cbs = {
.modify = lib_route_map_entry_match_condition_rmap_match_condition_community_limit_modify,
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_community_limit_destroy,
}
},
{ {
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list", .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list",
.cbs = { .cbs = {

View file

@ -72,6 +72,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_mod
int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_community_limit_modify(
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_comm_list_create( int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create(
struct nb_cb_create_args *args); struct nb_cb_create_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy( int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_destroy(

View file

@ -1274,6 +1274,57 @@ lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_des
return NB_OK; return NB_OK;
} }
/*
* XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:community-limit
*/
int lib_route_map_entry_match_condition_rmap_match_condition_community_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 = "community-limit";
rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
ret = bgp_route_match_add(rhc->rhc_rmi, "community-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_community_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/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list * XPath = /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list
*/ */

View file

@ -2693,6 +2693,12 @@ The following commands can be used in route maps:
happen only when BGP updates have completely same communities value happen only when BGP updates have completely same communities value
specified in the community list. specified in the community list.
.. clicmd:: match community-limit (0-65535)
This command matches BGP updates that use community list, and with a community
list count less or equal than the defined limit. Setting community-limit to 0
will only match BGP updates with no community.
.. clicmd:: set community <none|COMMUNITY> additive .. clicmd:: set community <none|COMMUNITY> additive
This command sets the community value in BGP updates. If the attribute is This command sets the community value in BGP updates. If the attribute is

View file

@ -310,6 +310,7 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(C, "frr-bgp-route-map:ip-route-source")) (strmatch(C, "frr-bgp-route-map:ip-route-source"))
#define IS_MATCH_ROUTE_SRC_PL(C) \ #define IS_MATCH_ROUTE_SRC_PL(C) \
(strmatch(C, "frr-bgp-route-map:ip-route-source-prefix-list")) (strmatch(C, "frr-bgp-route-map:ip-route-source-prefix-list"))
#define IS_MATCH_COMMUNITY_LIMIT(C) (strmatch(C, "frr-bgp-route-map:match-community-limit"))
#define IS_MATCH_COMMUNITY(C) \ #define IS_MATCH_COMMUNITY(C) \
(strmatch(C, "frr-bgp-route-map:match-community")) (strmatch(C, "frr-bgp-route-map:match-community"))
#define IS_MATCH_LCOMMUNITY(C) \ #define IS_MATCH_LCOMMUNITY(C) \

View file

@ -810,6 +810,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
yang_dnode_get_string( yang_dnode_get_string(
dnode, dnode,
"./rmap-match-condition/frr-bgp-route-map:list-name")); "./rmap-match-condition/frr-bgp-route-map:list-name"));
} else if (IS_MATCH_COMMUNITY_LIMIT(condition)) {
vty_out(vty, " match community-limit %s\n",
yang_dnode_get_string(dnode,
"./rmap-match-condition/frr-bgp-route-map:community-limit"));
} else if (IS_MATCH_COMMUNITY(condition)) { } else if (IS_MATCH_COMMUNITY(condition)) {
vty_out(vty, " match community %s", vty_out(vty, " match community %s",
yang_dnode_get_string( yang_dnode_get_string(

View file

@ -802,6 +802,17 @@ identity set-extcommunity-color {
} }
} }
case community-limit {
when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-community-limit')";
description
"Match BGP updates when the list of communities count is less than the configured limit.";
leaf community-limit {
type uint16 {
range "1..1024";
}
}
}
case comm-list-name { case comm-list-name {
when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-community') or " 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 " + "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:match-large-community') or "