summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDonatas Abraitis <donatas@opensourcerouting.org>2025-01-17 11:44:24 +0100
committerGitHub <noreply@github.com>2025-01-17 11:44:24 +0100
commit705e6f881b34079a0e87452961b02011a5f1e044 (patch)
tree217ae25a107b94139ec14ae1c639ba7c6487ff75
parentMerge pull request #17727 from idryzhov/netns-all-daemons (diff)
parenttopotest: add a test to control the community-list count (diff)
downloadfrr-705e6f881b34079a0e87452961b02011a5f1e044.tar.xz
frr-705e6f881b34079a0e87452961b02011a5f1e044.zip
Merge pull request #17836 from pguibert6WIND/limit_comm_list_count
limit community list count
-rw-r--r--bgpd/bgp_routemap.c76
-rw-r--r--bgpd/bgp_routemap_nb.c7
-rw-r--r--bgpd/bgp_routemap_nb.h4
-rw-r--r--bgpd/bgp_routemap_nb_config.c51
-rw-r--r--doc/user/bgp.rst6
-rw-r--r--lib/routemap.h1
-rw-r--r--lib/routemap_cli.c4
-rw-r--r--tests/topotests/bgp_comm_list_match/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_comm_list_match/r1/zebra.conf2
-rw-r--r--tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py64
-rw-r--r--yang/frr-bgp-route-map.yang17
11 files changed, 242 insertions, 0 deletions
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index d8d1fa5dd..fa8701dc5 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -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)
{
@@ -5707,6 +5762,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>]]",
@@ -7905,6 +7979,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);
@@ -7977,6 +8052,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);
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index d8fdb4fbc..464559344 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -166,6 +166,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
}
},
{
+ .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 = {
.create = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create,
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index f59686f38..45689242a 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -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(
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index 0dca196ed..223c416dc 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -1275,6 +1275,57 @@ lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_des
}
/*
+ * 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
*/
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_create(
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index d07bb6503..1493c2fb9 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -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
diff --git a/lib/routemap.h b/lib/routemap.h
index 8dcc17ecc..1c0234831 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -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) \
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index 69b942064..eb0170970 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -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(
diff --git a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf
index bac841208..d7d58d22a 100644
--- a/tests/topotests/bgp_comm_list_match/r1/bgpd.conf
+++ b/tests/topotests/bgp_comm_list_match/r1/bgpd.conf
@@ -12,6 +12,8 @@ router bgp 65001
ip prefix-list p1 seq 5 permit 172.16.255.1/32
ip prefix-list p3 seq 5 permit 172.16.255.3/32
ip prefix-list p4 seq 5 permit 172.16.255.4/32
+ip prefix-list p5 seq 5 permit 172.16.255.5/32
+ip prefix-list p6 seq 5 permit 172.16.255.6/32
!
route-map r2 permit 10
match ip address prefix-list p1
@@ -24,5 +26,13 @@ route-map r2 permit 30
set community 65001:10 65001:12 65001:13
exit
route-map r2 permit 40
+ match ip address prefix-list p5
+ set community 65001:13 65001:14
+exit
+route-map r2 permit 50
+ match ip address prefix-list p6
+ set community 65001:16 65001:17 65001:18 65001:19
+exit
+route-map r2 permit 60
exit
!
diff --git a/tests/topotests/bgp_comm_list_match/r1/zebra.conf b/tests/topotests/bgp_comm_list_match/r1/zebra.conf
index 4219a7ca3..1b19a4a12 100644
--- a/tests/topotests/bgp_comm_list_match/r1/zebra.conf
+++ b/tests/topotests/bgp_comm_list_match/r1/zebra.conf
@@ -4,6 +4,8 @@ interface lo
ip address 172.16.255.2/32
ip address 172.16.255.3/32
ip address 172.16.255.4/32
+ ip address 172.16.255.5/32
+ ip address 172.16.255.6/32
!
interface r1-eth0
ip address 192.168.0.1/24
diff --git a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
index d0cab26e1..c14ef6b8c 100644
--- a/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
+++ b/tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
@@ -133,6 +133,70 @@ def test_bgp_comm_list_match_any():
assert result is None, "Failed to filter BGP UPDATES with community-list on R3"
+def test_bgp_comm_list_limit_match():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r3"]
+ router.vtysh_cmd(
+ """
+ configure terminal
+ route-map r1 permit 20
+ match community-limit 3
+ """
+ )
+
+ def _bgp_count():
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 json"))
+ expected = {
+ "vrfName": "default",
+ "routerId": "192.168.1.3",
+ "localAS": 65003,
+ "totalRoutes": 3,
+ "totalPaths": 3,
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check that 3 routes have been received on R3")
+ test_func = functools.partial(_bgp_count)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to check that 3 routes have been received on R3"
+
+
+def test_bgp_comm_list_reset_limit_match():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r3"]
+ router.vtysh_cmd(
+ """
+ configure terminal
+ route-map r1 permit 20
+ no match community-limit
+ """
+ )
+
+ def _bgp_count_two():
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 json"))
+ expected = {
+ "vrfName": "default",
+ "routerId": "192.168.1.3",
+ "localAS": 65003,
+ "totalRoutes": 4,
+ "totalPaths": 4,
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check that 4 routes have been received on R3")
+ test_func = functools.partial(_bgp_count_two)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to check that 4 routes have been received on R3"
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index 5f701d514..efb0b2fa0 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -148,6 +148,12 @@ module frr-bgp-route-map {
"Match BGP community list";
}
+ identity match-community-limit {
+ base frr-route-map:rmap-match-type;
+ description
+ "Match BGP community limit count";
+ }
+
identity match-large-community {
base frr-route-map:rmap-match-type;
description
@@ -802,6 +808,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 "