From c3084cacf4816cd0bba5ad8695bbf3365500563f Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 14 Feb 2025 09:24:20 +0100 Subject: [PATCH] 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 --- bgpd/bgp_routemap.c | 82 +++++++++++++++++++++++++++++++++++ bgpd/bgp_routemap_nb.c | 7 +++ bgpd/bgp_routemap_nb.h | 4 ++ bgpd/bgp_routemap_nb_config.c | 51 ++++++++++++++++++++++ doc/user/bgp.rst | 7 +++ lib/routemap.h | 1 + lib/routemap_cli.c | 4 ++ yang/frr-bgp-route-map.yang | 17 ++++++++ 8 files changed, 173 insertions(+) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index fa8701dc50..17b1ba730d 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -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); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 4645593441..9372647f3d 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -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 = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 45689242a0..f04429078f 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -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( diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 223c416dc5..5f5274c5e1 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -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 diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index cba539708a..5286acd792 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -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 diff --git a/lib/routemap.h b/lib/routemap.h index 1c02348313..0e41a7a856 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -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) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index eb01709707..a59d504287 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -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( diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index efb0b2fa08..f1fa93664c 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -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 "