forked from Mirror/frr
Merge pull request #18159 from pguibert6WIND/bgp_ecommlist_count
Bgp ecommlist count
This commit is contained in:
commit
d49561e32a
|
@ -560,6 +560,11 @@ static bool community_regexp_match(struct community *com, regex_t *reg)
|
|||
return rv == 0;
|
||||
}
|
||||
|
||||
static char *ecommunity_str_get(struct ecommunity *ecom, int i)
|
||||
{
|
||||
return ecommunity_ecom2str_one(ecom, ECOMMUNITY_FORMAT_DISPLAY, i);
|
||||
}
|
||||
|
||||
static char *lcommunity_str_get(struct lcommunity *lcom, int i)
|
||||
{
|
||||
struct lcommunity_val lcomval;
|
||||
|
@ -611,6 +616,29 @@ static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Internal function to perform regular expression match for a single ecommunity. */
|
||||
static bool ecommunity_regexp_include(regex_t *reg, struct ecommunity *ecom, int i)
|
||||
{
|
||||
char *str;
|
||||
|
||||
/* When there is no communities attribute it is treated as empty string.
|
||||
*/
|
||||
if (ecom == NULL || ecom->size == 0)
|
||||
str = XSTRDUP(MTYPE_ECOMMUNITY_STR, "");
|
||||
else
|
||||
str = ecommunity_str_get(ecom, i);
|
||||
|
||||
/* Regular expression match. */
|
||||
if (regexec(reg, str, 0, NULL, 0) == 0) {
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, str);
|
||||
return true;
|
||||
}
|
||||
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, str);
|
||||
/* No match. */
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
|
||||
{
|
||||
const char *str;
|
||||
|
@ -697,6 +725,24 @@ bool lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Perform exact matching. In case of expanded extended-community-list, do
|
||||
* same thing as ecommunity_list_match().
|
||||
*/
|
||||
bool ecommunity_list_exact_match(struct ecommunity *ecom, struct community_list *list)
|
||||
{
|
||||
struct community_entry *entry;
|
||||
|
||||
for (entry = list->head; entry; entry = entry->next) {
|
||||
if (entry->style == EXTCOMMUNITY_LIST_STANDARD) {
|
||||
if (ecommunity_cmp(ecom, entry->u.lcom))
|
||||
return entry->direct == COMMUNITY_PERMIT;
|
||||
} else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) {
|
||||
if (ecommunity_regexp_match(ecom, entry->reg))
|
||||
return entry->direct == COMMUNITY_PERMIT;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Perform exact matching. In case of expanded large-community-list, do
|
||||
* same thing as lcommunity_list_match().
|
||||
|
@ -981,6 +1027,27 @@ bool lcommunity_list_any_match(struct lcommunity *lcom,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ecommunity_list_any_match(struct ecommunity *ecom, struct community_list *list)
|
||||
{
|
||||
struct community_entry *entry;
|
||||
uint8_t *ptr;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < ecom->size; i++) {
|
||||
ptr = ecom->val + (i * ecom->unit_size);
|
||||
|
||||
for (entry = list->head; entry; entry = entry->next) {
|
||||
if ((entry->style == EXTCOMMUNITY_LIST_STANDARD) &&
|
||||
ecommunity_include_one(entry->u.ecom, ptr))
|
||||
return entry->direct == COMMUNITY_PERMIT;
|
||||
if ((entry->style == EXTCOMMUNITY_LIST_EXPANDED) &&
|
||||
ecommunity_regexp_include(entry->reg, ecom, 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)
|
||||
|
|
|
@ -158,6 +158,7 @@ extern bool lcommunity_list_match(struct lcommunity *lcom,
|
|||
struct community_list *list);
|
||||
extern bool community_list_exact_match(struct community *com,
|
||||
struct community_list *list);
|
||||
extern bool ecommunity_list_exact_match(struct ecommunity *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,
|
||||
|
@ -166,6 +167,7 @@ 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 bool ecommunity_list_any_match(struct ecommunity *ecom, struct community_list *list);
|
||||
extern struct lcommunity *
|
||||
lcommunity_list_match_delete(struct lcommunity *lcom,
|
||||
struct community_list *list);
|
||||
|
|
|
@ -1145,8 +1145,10 @@ bool ecommunity_has_route_target(struct ecommunity *ecom)
|
|||
*
|
||||
* Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
|
||||
* 0 value displays all.
|
||||
* Index is a unsigned integer value, and stands for the extended community list entry
|
||||
* to display when value is not -1.
|
||||
*/
|
||||
char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
|
||||
static char *_ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter, int index)
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t *pnt;
|
||||
|
@ -1168,8 +1170,10 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
|
|||
bool unk_ecom = false;
|
||||
memset(encbuf, 0x00, sizeof(encbuf));
|
||||
|
||||
if (index != -1 && (uint32_t)index != i)
|
||||
continue;
|
||||
/* Space between each value. */
|
||||
if (i > 0)
|
||||
if (index == -1 && i > 0)
|
||||
strlcat(str_buf, " ", str_size);
|
||||
|
||||
/* Retrieve value field */
|
||||
|
@ -1496,6 +1500,29 @@ unknown:
|
|||
return str_buf;
|
||||
}
|
||||
|
||||
char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
|
||||
{
|
||||
return _ecommunity_ecom2str(ecom, format, filter, -1);
|
||||
}
|
||||
|
||||
char *ecommunity_ecom2str_one(struct ecommunity *ecom, int format, int number)
|
||||
{
|
||||
return _ecommunity_ecom2str(ecom, format, 0, number);
|
||||
}
|
||||
|
||||
bool ecommunity_include_one(struct ecommunity *ecom, uint8_t *ptr)
|
||||
{
|
||||
uint32_t i;
|
||||
uint8_t *ecom_ptr;
|
||||
|
||||
for (i = 0; i < ecom->size; i++) {
|
||||
ecom_ptr = ecom->val + (i * ecom->unit_size);
|
||||
if (memcmp(ptr, ecom_ptr, ecom->unit_size) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2)
|
||||
{
|
||||
uint32_t i, j;
|
||||
|
|
|
@ -379,9 +379,11 @@ extern unsigned int ecommunity_hash_make(const void *);
|
|||
extern struct ecommunity *ecommunity_str2com(const char *, int, int);
|
||||
extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
|
||||
int keyword_included);
|
||||
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
|
||||
extern char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter);
|
||||
extern char *ecommunity_ecom2str_one(struct ecommunity *ecom, int format, int number);
|
||||
extern bool ecommunity_has_route_target(struct ecommunity *ecom);
|
||||
extern void ecommunity_strfree(char **s);
|
||||
extern bool ecommunity_include_one(struct ecommunity *ecom, uint8_t *ptr);
|
||||
extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
|
||||
extern bool ecommunity_match(const struct ecommunity *,
|
||||
const struct ecommunity *);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -1853,8 +1912,18 @@ route_match_ecommunity(void *rule, const struct prefix *prefix, void *object)
|
|||
if (!list)
|
||||
return RMAP_NOMATCH;
|
||||
|
||||
if (ecommunity_list_match(bgp_attr_get_ecommunity(path->attr), list))
|
||||
return RMAP_MATCH;
|
||||
if (rcom->exact) {
|
||||
if (ecommunity_list_exact_match(bgp_attr_get_ecommunity(path->attr), list))
|
||||
return RMAP_MATCH;
|
||||
} else if (rcom->any) {
|
||||
if (!bgp_attr_get_ecommunity(path->attr))
|
||||
return RMAP_OKAY;
|
||||
if (ecommunity_list_any_match(bgp_attr_get_ecommunity(path->attr), list))
|
||||
return RMAP_MATCH;
|
||||
} else {
|
||||
if (ecommunity_list_match(bgp_attr_get_ecommunity(path->attr), list))
|
||||
return RMAP_MATCH;
|
||||
}
|
||||
|
||||
return RMAP_NOMATCH;
|
||||
}
|
||||
|
@ -1863,11 +1932,28 @@ route_match_ecommunity(void *rule, const struct prefix *prefix, void *object)
|
|||
static void *route_match_ecommunity_compile(const char *arg)
|
||||
{
|
||||
struct rmap_community *rcom;
|
||||
int len;
|
||||
char *p;
|
||||
|
||||
rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
|
||||
rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
|
||||
rcom->name_hash = bgp_clist_hash_key(rcom->name);
|
||||
|
||||
p = strchr(arg, ' ');
|
||||
if (p) {
|
||||
len = p - arg;
|
||||
rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1);
|
||||
memcpy(rcom->name, arg, len);
|
||||
p++;
|
||||
if (*p == 'e')
|
||||
rcom->exact = true;
|
||||
else
|
||||
rcom->any = true;
|
||||
} else {
|
||||
rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
|
||||
rcom->exact = false;
|
||||
rcom->any = false;
|
||||
}
|
||||
|
||||
rcom->name_hash = bgp_clist_hash_key(rcom->name);
|
||||
return rcom;
|
||||
}
|
||||
|
||||
|
@ -5861,16 +5947,19 @@ DEFUN_YANG(
|
|||
|
||||
DEFPY_YANG (match_ecommunity,
|
||||
match_ecommunity_cmd,
|
||||
"match extcommunity <(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME>",
|
||||
"match extcommunity <(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]",
|
||||
MATCH_STR
|
||||
"Match BGP/VPN extended community list\n"
|
||||
"Extended community-list number (standard)\n"
|
||||
"Extended community-list number (expanded)\n"
|
||||
"Extended community-list name\n")
|
||||
"Extended 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-extcommunity']";
|
||||
char xpath_value[XPATH_MAXLEN];
|
||||
char xpath_match[XPATH_MAXLEN];
|
||||
int idx_comm_list = 2;
|
||||
|
||||
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
|
||||
|
@ -5881,19 +5970,57 @@ DEFPY_YANG (match_ecommunity,
|
|||
xpath);
|
||||
nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg);
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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>]",
|
||||
"no match extcommunity [<(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME> [<exact-match$exact|any$any>]]",
|
||||
NO_STR
|
||||
MATCH_STR
|
||||
"Match BGP/VPN extended community list\n"
|
||||
"Extended community-list number (standard)\n"
|
||||
"Extended community-list number (expanded)\n"
|
||||
"Extended community-list name\n")
|
||||
"Extended 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-extcommunity']";
|
||||
|
@ -7980,6 +8107,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 +8181,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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2918,7 +2918,22 @@ Extended Community Lists
|
|||
BGP Extended Communities in Route Map
|
||||
"""""""""""""""""""""""""""""""""""""
|
||||
|
||||
.. clicmd:: match extcommunity WORD
|
||||
.. clicmd:: match extcommunity WORD [exact-match|any]
|
||||
|
||||
This command perform match to BGP updates using extended community list WORD.
|
||||
When the one of BGP extended communities value match to the one of the extended
|
||||
communities value in community list, it is match. When ``exact-match`` keyword
|
||||
is specified, match happens only when BGP updates have completely same extended
|
||||
communities value specified in the extended community list. When ``any`` keyword
|
||||
is set, match happens when any of the extended communities of the BGP updates
|
||||
matches an extended community of the specified list.
|
||||
|
||||
.. 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
|
||||
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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(
|
||||
|
@ -843,10 +847,18 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode,
|
|||
vty_out(vty, " any");
|
||||
vty_out(vty, "\n");
|
||||
} else if (IS_MATCH_EXTCOMMUNITY(condition)) {
|
||||
vty_out(vty, " match extcommunity %s\n",
|
||||
vty_out(vty, " match extcommunity %s",
|
||||
yang_dnode_get_string(
|
||||
dnode,
|
||||
"./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name"));
|
||||
if (yang_dnode_get_bool(
|
||||
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_IPV4_NH(condition)) {
|
||||
vty_out(vty, " match ip next-hop address %s\n",
|
||||
yang_dnode_get_string(
|
||||
|
|
51
tests/topotests/bgp_ecomm_list_match/r1/frr.conf
Normal file
51
tests/topotests/bgp_ecomm_list_match/r1/frr.conf
Normal file
|
@ -0,0 +1,51 @@
|
|||
!
|
||||
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
|
||||
ip address 172.16.255.5/32
|
||||
ip address 172.16.255.6/32
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 192.168.0.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
||||
router bgp 65001
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.168.0.2 remote-as external
|
||||
neighbor 192.168.0.2 timers 1 3
|
||||
neighbor 192.168.0.2 timers connect 1
|
||||
address-family ipv4
|
||||
redistribute connected
|
||||
neighbor 192.168.0.2 route-map r2 out
|
||||
exit-address-family
|
||||
!
|
||||
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
|
||||
set extcommunity rt 65001:1 65001:2
|
||||
route-map r2 permit 20
|
||||
match ip address prefix-list p3
|
||||
set extcommunity rt 65001:3
|
||||
route-map r2 permit 30
|
||||
match ip address prefix-list p4
|
||||
set extcommunity rt 65001:10 65001:12 65001:13
|
||||
exit
|
||||
route-map r2 permit 40
|
||||
match ip address prefix-list p5
|
||||
set extcommunity rt 65001:13 65001:14
|
||||
exit
|
||||
route-map r2 permit 50
|
||||
match ip address prefix-list p6
|
||||
set extcommunity rt 65001:16 65001:17 65001:18 65001:19
|
||||
exit
|
||||
route-map r2 permit 60
|
||||
exit
|
||||
!
|
32
tests/topotests/bgp_ecomm_list_match/r2/frr.conf
Normal file
32
tests/topotests/bgp_ecomm_list_match/r2/frr.conf
Normal file
|
@ -0,0 +1,32 @@
|
|||
!
|
||||
interface r2-eth0
|
||||
ip address 192.168.0.2/24
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 192.168.1.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
||||
!debug bgp updates
|
||||
!
|
||||
router bgp 65002
|
||||
no bgp ebgp-requires-policy
|
||||
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
|
||||
exit-address-family
|
||||
!
|
||||
bgp extcommunity-list 1 seq 5 permit rt 65001:1 rt 65001:2
|
||||
bgp extcommunity-list 1 seq 10 permit rt 65001:3
|
||||
!
|
||||
route-map r1 deny 10
|
||||
match extcommunity 1
|
||||
route-map r1 permit 20
|
||||
exit
|
||||
!
|
26
tests/topotests/bgp_ecomm_list_match/r3/frr.conf
Normal file
26
tests/topotests/bgp_ecomm_list_match/r3/frr.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
!
|
||||
interface r3-eth0
|
||||
ip address 192.168.1.3/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
||||
!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 extcommunity-list 2 seq 10 permit rt 65001:12
|
||||
!
|
||||
route-map r1 deny 10
|
||||
match extcommunity 2 any
|
||||
exit
|
||||
route-map r1 permit 20
|
||||
exit
|
||||
!
|
|
@ -0,0 +1,198 @@
|
|||
#!/usr/bin/env python
|
||||
# SPDX-License-Identifier: ISC
|
||||
|
||||
#
|
||||
# Copyright (c) 2025 by 6WIND
|
||||
#
|
||||
|
||||
"""
|
||||
Check if BGP extcommunity-list works as OR if multiple community entries specified,
|
||||
like:
|
||||
|
||||
bgp extcommunity-list 1 seq 5 permit rt 65001:1 rt 65002:2
|
||||
bgp community-list 1 seq 10 permit rt 65001:3
|
||||
!
|
||||
route-map test deny 10
|
||||
match extcommunity 1
|
||||
route-map test permit 20
|
||||
|
||||
Here, we should deny routes in/out if the path has:
|
||||
(ty 65001:1 AND rt 65001:2) OR rt 65001:3.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import pytest
|
||||
import functools
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
# pylint: disable=C0413
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen, logger
|
||||
from lib.common_config import step
|
||||
|
||||
pytestmark = [pytest.mark.bgpd]
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
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):
|
||||
tgen = Topogen(build_topo, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
router_list = tgen.routers()
|
||||
|
||||
for rname, router in tgen.routers().items():
|
||||
logger.info("Loading router %s" % rname)
|
||||
router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
|
||||
|
||||
# Initialize all routers.
|
||||
tgen.start_router()
|
||||
|
||||
|
||||
def teardown_module(mod):
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_bgp_extcomm_list_match():
|
||||
tgen = get_topogen()
|
||||
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
router = tgen.gears["r2"]
|
||||
|
||||
def _bgp_converge():
|
||||
output = json.loads(
|
||||
router.vtysh_cmd(
|
||||
"show bgp ipv4 unicast neighbors 192.168.0.1 filtered-routes json"
|
||||
)
|
||||
)
|
||||
expected = {
|
||||
"receivedRoutes": {
|
||||
"172.16.255.1/32": {
|
||||
"path": "65001",
|
||||
},
|
||||
"172.16.255.3/32": {
|
||||
"path": "65001",
|
||||
},
|
||||
}
|
||||
}
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
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_extcomm_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"
|
||||
|
||||
|
||||
def test_bgp_extcomm_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 extcommunity-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 extcommunity-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))
|
|
@ -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
|
||||
|
@ -814,7 +820,18 @@ identity set-extcommunity-color {
|
|||
"Match BGP updates when the list of communities count is less than the configured limit.";
|
||||
leaf community-limit {
|
||||
type uint16 {
|
||||
range "1..1024";
|
||||
range "0..1024";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue