]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: add match ecommunity <exact|any> options
authorPhilippe Guibert <philippe.guibert@6wind.com>
Fri, 14 Feb 2025 09:27:11 +0000 (10:27 +0100)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 27 Feb 2025 21:25:52 +0000 (22:25 +0100)
The exact-match and the any options are missing for the extended
communities. Add missing options that are present on the match
operations for communities and large-communities.

> route-map rmap permit 1
>  match extcommunity 1
> exit
> !
> route-map rmap permit 2
>  match extcommunity 2 any
> exit
> !
> route-map rmap permit 3
>  match extcommunity 3 exact-match
> exit

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
bgpd/bgp_clist.c
bgpd/bgp_clist.h
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_routemap.c
doc/user/bgp.rst
lib/routemap_cli.c

index ca9c428b47b698ae336bb6ec5479ecdf888a4448..335bccf34f9ed34d6bfcf2a77938d4f239fbaffa 100644 (file)
@@ -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)
index 7f35a6d53e4b0bdf4ae49f99f6849a6960ee3383..736c3d1195c3c721b50fb65ceb9318c0a47a5ab9 100644 (file)
@@ -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);
index 2c6ae65f85f0dcb2068c595e29b93e21a99c7382..dcdf2ba25db4b77fcd96d8a8e04acb5077f9bf22 100644 (file)
@@ -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;
index 0e68b158074955968ce84482e348b466af957b44..d3708d53d4863a96fb68dbc9161877879b66ee5c 100644 (file)
@@ -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 *);
index 17b1ba730d3cf1ab460e1539ae67ff06daa59c7d..f3192a4734cc466969a2188c9f7a6b54420ce2f4 100644 (file)
@@ -1912,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;
 }
@@ -1922,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;
 }
 
@@ -5920,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);
@@ -5940,6 +5970,21 @@ 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);
 }
 
@@ -5967,13 +6012,15 @@ DEFPY_YANG(
 
 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']";
index 5286acd792d0be1b836e39a09d3eb0484c555edd..ca676cd7baec7dddf85aee724b7a87ed33cba0e6 100644 (file)
@@ -2918,9 +2918,17 @@ Extended Community Lists
 BGP Extended Communities in Route Map
 """""""""""""""""""""""""""""""""""""
 
-.. clicmd:: match extcommunity WORD
+.. clicmd:: match extcommunity WORD [exact-match|any]
 
-.. clicmd:: match extcommunity-limit (0-65535)
+   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
index a59d50428725e9c41eb8dfb1e585c6455e1006da..f045bc7e4c0636e4e3dbe5a2f24f3558d615045e 100644 (file)
@@ -847,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(