Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

limit community list count #17836

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions bgpd/bgp_routemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you keep formatting for route_map_rule_cmd as others? (ignoring frrbot)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

"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)
{
Expand Down Expand Up @@ -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>]]",
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions bgpd/bgp_routemap_nb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
4 changes: 4 additions & 0 deletions bgpd/bgp_routemap_nb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
51 changes: 51 additions & 0 deletions bgpd/bgp_routemap_nb_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
6 changes: 6 additions & 0 deletions doc/user/bgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also document what 0 means explicitly. Because what is now, it means unlimited, but me as an operator understand it as "do not accept the route if it has any community attached".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whatever the meaning, I will document it. thanks for advice.

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
Expand Down
1 change: 1 addition & 0 deletions lib/routemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
4 changes: 4 additions & 0 deletions lib/routemap_cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
10 changes: 10 additions & 0 deletions tests/topotests/bgp_comm_list_match/r1/bgpd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
!
2 changes: 2 additions & 0 deletions tests/topotests/bgp_comm_list_match/r1/zebra.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 64 additions & 0 deletions tests/topotests/bgp_comm_list_match/test_bgp_comm_list_match.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,70 @@ def _bgp_converge():
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))
17 changes: 17 additions & 0 deletions yang/frr-bgp-route-map.yang
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 "
Expand Down
Loading