bgpd: add 'match community-list any' function

There is no match mechanism to match one community from the
incoming community-list. Add the 'any' keyword to the 'match
route-map' command of communit-list and large-community-list.

> match community-list AAA any
> match large-community-list AAA any

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2023-06-27 21:36:01 +02:00
parent 3651803221
commit aa511000e0
15 changed files with 281 additions and 71 deletions

View File

@ -735,6 +735,27 @@ bool community_list_exact_match(struct community *com,
return false;
}
bool community_list_any_match(struct community *com, struct community_list *list)
{
struct community_entry *entry;
uint32_t val;
int i;
for (i = 0; i < com->size; i++) {
val = community_val_get(com, i);
for (entry = list->head; entry; entry = entry->next) {
if (entry->style == COMMUNITY_LIST_STANDARD &&
community_include(entry->u.com, val))
return entry->direct == COMMUNITY_PERMIT;
if ((entry->style == COMMUNITY_LIST_EXPANDED) &&
community_regexp_include(entry->reg, com, i))
return entry->direct == COMMUNITY_PERMIT;
}
}
return false;
}
/* Delete all permitted communities in the list from com. */
struct community *community_list_match_delete(struct community *com,
struct community_list *list)
@ -922,6 +943,28 @@ int community_list_unset(struct community_list_handler *ch, const char *name,
return 0;
}
bool lcommunity_list_any_match(struct lcommunity *lcom,
struct community_list *list)
{
struct community_entry *entry;
uint8_t *ptr;
int i;
for (i = 0; i < lcom->size; i++) {
ptr = lcom->val + (i * LCOMMUNITY_SIZE);
for (entry = list->head; entry; entry = entry->next) {
if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) &&
lcommunity_include(entry->u.lcom, ptr))
return entry->direct == COMMUNITY_PERMIT;
if ((entry->style == LARGE_COMMUNITY_LIST_EXPANDED) &&
lcommunity_regexp_include(entry->reg, lcom, i))
return entry->direct == COMMUNITY_PERMIT;
}
}
return false;
}
/* Delete all permitted large communities in the list from com. */
struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
struct community_list *list)

View File

@ -158,8 +158,12 @@ extern bool community_list_exact_match(struct community *com,
struct community_list *list);
extern bool lcommunity_list_exact_match(struct lcommunity *lcom,
struct community_list *list);
extern bool community_list_any_match(struct community *com,
struct community_list *list);
extern struct community *
community_list_match_delete(struct community *com, struct community_list *list);
extern bool lcommunity_list_any_match(struct lcommunity *lcom,
struct community_list *list);
extern struct lcommunity *
lcommunity_list_match_delete(struct lcommunity *lcom,
struct community_list *list);

View File

@ -1528,7 +1528,8 @@ static const struct route_map_rule_cmd route_match_aspath_cmd = {
struct rmap_community {
char *name;
uint32_t name_hash;
int exact;
bool exact;
bool any;
};
/* Match function for community match. */
@ -1551,6 +1552,12 @@ route_match_community(void *rule, const struct prefix *prefix, void *object)
if (community_list_exact_match(
bgp_attr_get_community(path->attr), list))
return RMAP_MATCH;
} else if (rcom->any) {
if (!bgp_attr_get_community(path->attr))
return RMAP_OKAY;
if (community_list_any_match(bgp_attr_get_community(path->attr),
list))
return RMAP_MATCH;
} else {
if (community_list_match(bgp_attr_get_community(path->attr),
list))
@ -1574,10 +1581,15 @@ static void *route_match_community_compile(const char *arg)
len = p - arg;
rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1);
memcpy(rcom->name, arg, len);
rcom->exact = 1;
p++;
if (*p == 'e')
rcom->exact = true;
else
rcom->any = true;
} else {
rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
rcom->exact = 0;
rcom->exact = false;
rcom->any = false;
}
rcom->name_hash = bgp_clist_hash_key(rcom->name);
@ -1637,6 +1649,12 @@ route_match_lcommunity(void *rule, const struct prefix *prefix, void *object)
if (lcommunity_list_exact_match(
bgp_attr_get_lcommunity(path->attr), list))
return RMAP_MATCH;
} else if (rcom->any) {
if (!bgp_attr_get_lcommunity(path->attr))
return RMAP_OKAY;
if (lcommunity_list_any_match(bgp_attr_get_lcommunity(path->attr),
list))
return RMAP_MATCH;
} else {
if (lcommunity_list_match(bgp_attr_get_lcommunity(path->attr),
list))
@ -1660,10 +1678,15 @@ static void *route_match_lcommunity_compile(const char *arg)
len = p - arg;
rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1);
memcpy(rcom->name, arg, len);
rcom->exact = 1;
p++;
if (*p == 'e')
rcom->exact = true;
else
rcom->any = true;
} else {
rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
rcom->exact = 0;
rcom->exact = false;
rcom->any = false;
}
rcom->name_hash = bgp_clist_hash_key(rcom->name);
@ -5493,15 +5516,15 @@ DEFUN_YANG(no_match_alias, no_match_alias_cmd, "no match alias [ALIAS_NAME]",
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG (match_community,
match_community_cmd,
"match community <(1-99)|(100-500)|COMMUNITY_LIST_NAME> [exact-match]",
MATCH_STR
"Match BGP community list\n"
"Community-list number (standard)\n"
"Community-list number (expanded)\n"
"Community-list name\n"
"Do exact matching of communities\n")
DEFPY_YANG(
match_community, match_community_cmd,
"match community <(1-99)|(100-500)|COMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]",
MATCH_STR "Match BGP community list\n"
"Community-list number (standard)\n"
"Community-list number (expanded)\n"
"Community-list name\n"
"Do exact matching of communities\n"
"Do matching of any community\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:match-community']";
@ -5517,35 +5540,35 @@ DEFPY_YANG (match_community,
xpath);
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg);
if (argc == 4) {
snprintf(
xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
snprintf(xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
if (exact)
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
"true");
} else {
snprintf(
xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
"false");
}
else
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "false");
snprintf(xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any",
xpath);
if (any)
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "true");
else
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "false");
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]]",
NO_STR
MATCH_STR
"Match BGP community list\n"
"Community-list number (standard)\n"
"Community-list number (expanded)\n"
"Community-list name\n"
"Do exact matching of communities\n")
DEFUN_YANG(
no_match_community, no_match_community_cmd,
"no match community [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]]",
NO_STR MATCH_STR "Match BGP community list\n"
"Community-list number (standard)\n"
"Community-list number (expanded)\n"
"Community-list name\n"
"Do exact matching of communities\n"
"Do matching of any community\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:match-community']";
@ -5554,15 +5577,15 @@ DEFUN_YANG (no_match_community,
return nb_cli_apply_changes(vty, NULL);
}
DEFPY_YANG (match_lcommunity,
match_lcommunity_cmd,
"match large-community <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [exact-match]",
MATCH_STR
"Match BGP large community list\n"
"Large Community-list number (standard)\n"
"Large Community-list number (expanded)\n"
"Large Community-list name\n"
"Do exact matching of communities\n")
DEFPY_YANG(
match_lcommunity, match_lcommunity_cmd,
"match large-community <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]",
MATCH_STR "Match BGP large community list\n"
"Large Community-list number (standard)\n"
"Large Community-list number (expanded)\n"
"Large Community-list name\n"
"Do exact matching of communities\n"
"Do matching of any community\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:match-large-community']";
@ -5578,35 +5601,35 @@ DEFPY_YANG (match_lcommunity,
xpath);
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_lcomm_list]->arg);
if (argc == 4) {
snprintf(
xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
snprintf(xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
if (exact)
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
"true");
} else {
snprintf(
xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
xpath);
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
"false");
}
else
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "false");
snprintf(xpath_match, sizeof(xpath_match),
"%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any",
xpath);
if (any)
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "true");
else
nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, "false");
return nb_cli_apply_changes(vty, NULL);
}
DEFUN_YANG (no_match_lcommunity,
no_match_lcommunity_cmd,
"no match large-community [<(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [exact-match]]",
NO_STR
MATCH_STR
"Match BGP large community list\n"
"Large Community-list number (standard)\n"
"Large Community-list number (expanded)\n"
"Large Community-list name\n"
"Do exact matching of communities\n")
DEFUN_YANG(
no_match_lcommunity, no_match_lcommunity_cmd,
"no match large-community [<(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [<exact-match|any>]]",
NO_STR MATCH_STR "Match BGP large community list\n"
"Large Community-list number (standard)\n"
"Large Community-list number (expanded)\n"
"Large Community-list name\n"
"Do exact matching of communities\n"
"Do matching of any community\n")
{
const char *xpath =
"./match-condition[condition='frr-bgp-route-map:match-large-community']";

View File

@ -164,6 +164,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy,
}
},
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any",
.cbs = {
.modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_modify,
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_destroy,
}
},
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv4-address",
.cbs = {

View File

@ -65,6 +65,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_modify(
struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_destroy(
struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify(struct nb_cb_modify_args *args);

View File

@ -1127,6 +1127,7 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(
struct routemap_hook_context *rhc;
const char *value;
bool exact_match = false;
bool any = false;
char *argstr;
const char *condition;
route_map_event_t event;
@ -1140,12 +1141,21 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(
exact_match = yang_dnode_get_bool(
args->dnode, "./comm-list-name-exact-match");
if (yang_dnode_exists(args->dnode, "./comm-list-name-any"))
any = yang_dnode_get_bool(args->dnode, "./comm-list-name-any");
if (exact_match) {
argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
strlen(value) + strlen("exact-match") + 2);
snprintf(argstr, (strlen(value) + strlen("exact-match") + 2),
"%s exact-match", value);
} else if (any) {
argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
strlen(value) + strlen("any") + 2);
snprintf(argstr, (strlen(value) + strlen("any") + 2), "%s any",
value);
} else
argstr = (char *)value;
@ -1217,6 +1227,39 @@ lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_nam
}
/*
* XPath:
* /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any
*/
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_modify(
struct nb_cb_modify_args *args)
{
switch (args->event) {
case NB_EV_VALIDATE:
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_APPLY:
break;
}
return NB_OK;
}
int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_any_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/comm-list-name-exact-match
*/

View File

@ -729,6 +729,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match"))
vty_out(vty, " exact-match");
if (yang_dnode_get_bool(
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any"))
vty_out(vty, " any");
vty_out(vty, "\n");
} else if (IS_MATCH_LCOMMUNITY(condition)) {
vty_out(vty, " match large-community %s",
@ -739,6 +743,10 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match"))
vty_out(vty, " exact-match");
if (yang_dnode_get_bool(
dnode,
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-any"))
vty_out(vty, " any");
vty_out(vty, "\n");
} else if (IS_MATCH_EXTCOMMUNITY(condition)) {
vty_out(vty, " match extcommunity %s\n",

View File

@ -11,6 +11,7 @@ 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
!
route-map r2 permit 10
match ip address prefix-list p1
@ -19,5 +20,9 @@ route-map r2 permit 20
match ip address prefix-list p3
set community 65001:3
route-map r2 permit 30
match ip address prefix-list p4
set community 65001:10 65001:12 65001:13
exit
route-map r2 permit 40
exit
!

View File

@ -3,6 +3,7 @@ interface lo
ip address 172.16.255.1/32
ip address 172.16.255.2/32
ip address 172.16.255.3/32
ip address 172.16.255.4/32
!
interface r1-eth0
ip address 192.168.0.1/24

View File

@ -6,6 +6,9 @@ router bgp 65002
neighbor 192.168.0.1 remote-as external
neighbor 192.168.0.1 timers 1 3
neighbor 192.168.0.1 timers connect 1
neighbor 192.168.1.3 remote-as external
neighbor 192.168.1.3 timers 1 3
neighbor 192.168.1.3 timers connect 1
address-family ipv4
neighbor 192.168.0.1 route-map r1 in
neighbor 192.168.0.1 soft-reconfiguration inbound

View File

@ -2,5 +2,8 @@
interface r2-eth0
ip address 192.168.0.2/24
!
interface r2-eth1
ip address 192.168.1.2/24
!
ip forwarding
!

View File

@ -0,0 +1,21 @@
!
!debug bgp updates
!
router bgp 65003
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
neighbor 192.168.1.2 timers 1 3
neighbor 192.168.1.2 timers connect 1
address-family ipv4
neighbor 192.168.1.2 route-map r1 in
neighbor 192.168.1.2 soft-reconfiguration inbound
exit-address-family
!
bgp community-list 2 seq 10 permit 65001:12
!
route-map r1 deny 10
match community 2 any
exit
route-map r1 permit 20
exit
!

View File

@ -0,0 +1,6 @@
!
interface r3-eth0
ip address 192.168.1.3/24
!
ip forwarding
!

View File

@ -39,12 +39,15 @@ pytestmark = [pytest.mark.bgpd]
def build_topo(tgen):
for routern in range(1, 3):
for routern in range(1, 4):
tgen.add_router("r{}".format(routern))
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["r1"])
switch.add_link(tgen.gears["r2"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["r3"])
switch.add_link(tgen.gears["r2"])
def setup_module(mod):
@ -95,12 +98,41 @@ def test_bgp_comm_list_match():
}
return topotest.json_cmp(output, expected)
step("Initial BGP converge")
step("Initial BGP converge between R1 and R2")
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to filter BGP UPDATES with community-list on R2"
def test_bgp_comm_list_match_any():
tgen = get_topogen()
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
router = tgen.gears["r3"]
def _bgp_converge():
output = json.loads(
router.vtysh_cmd(
"show bgp ipv4 unicast neighbors 192.168.1.2 filtered-routes json"
)
)
expected = {
"receivedRoutes": {
"172.16.255.4/32": {
"path": "65002 65001",
},
}
}
return topotest.json_cmp(output, expected)
step("Initial BGP converge between R3 and R2")
test_func = functools.partial(_bgp_converge)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed to filter BGP UPDATES with community-list on R3"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))

View File

@ -777,6 +777,13 @@ identity set-extcommunity-color {
description
"Do exact matching of communities";
}
leaf comm-list-name-any {
type boolean;
description
"Do matching of any community";
}
}
}