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
};
/* `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
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);
}
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(
no_match_community, no_match_community_cmd,
"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_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_evpn_default_route_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, &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, &no_match_lcommunity_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,
}
},
{
.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",
.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_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_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(
struct nb_cb_create_args *args);
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;
}
/*
* 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
*/

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
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
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"))
#define IS_MATCH_ROUTE_SRC_PL(C) \
(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) \
(strmatch(C, "frr-bgp-route-map:match-community"))
#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(
dnode,
"./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)) {
vty_out(vty, " match community %s",
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 {
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 "