]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: Improve JSON support for large communities
authorPascal Mathis <mail@pascalmathis.com>
Sun, 13 May 2018 00:29:40 +0000 (02:29 +0200)
committerPascal Mathis <mail@pascalmathis.com>
Sun, 13 May 2018 17:37:51 +0000 (19:37 +0200)
The current implementation of building JSON output is greatly different
for large communities compared to standard communities. This is mainly
noticeable by the missing 'list' attribute, which usually offers an
array of all communities present on a BGP route.

This commit adds the missing functionality of properly returning a
'list' attribute in JSON output and also tries a similar approach like
the standard communities are using to implement this feature.

Additionally, the 'format' specifier has been completely removed from
large communities string/JSON rendering, as the official RFC8092 specifies that
there is only one canonical representation:

> The canonical representation of BGP Large Communities is three
> separate unsigned integers in decimal notation in the following
> order: Global Administrator, Local Data 1, Local Data 2. Numbers
> MUST NOT contain leading zeros; a zero value MUST be represented with
> a single zero. Each number is separated from the next by a single
> colon. For example: 64496:4294967295:2, 64496:0:0.

As the 'format' specifier has not been used/checked and only one
canonical representation exists per today, there was no reason to keep
the 'format' parameter in the function signature.

Last but not least, the struct attribute 'community_entry.config' is no
longer being used for large communities and instead 'lcommunity_str' is
being called to maintain a similar approach to standard communities.

As a side effect, this also fixed a memory leak inside 'community_entry_free'
which did not free the allocated memory for the 'config' attribute when
dealing with a large community.

Signed-off-by: Pascal Mathis <mail@pascalmathis.com>
bgpd/bgp_clist.c
bgpd/bgp_lcommunity.c
bgpd/bgp_lcommunity.h
bgpd/bgp_route.c
bgpd/bgp_vty.c

index d1bc7f6e535a849bfb9530c9069792d186d1fd67..7cf14775496cdb2bb6e8f0cc9eb04f2c5bb20b31 100644 (file)
@@ -512,7 +512,7 @@ static int lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
        if (com == NULL || com->size == 0)
                str = "";
        else
-               str = lcommunity_str(com);
+               str = lcommunity_str(com, false);
 
        /* Regular expression match.  */
        if (regexec(reg, str, 0, NULL, 0) == 0)
@@ -986,13 +986,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
        entry->any = (str ? 0 : 1);
        entry->u.lcom = lcom;
        entry->reg = regex;
-       if (lcom)
-               entry->config = lcommunity_lcom2str(
-                       lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST);
-       else if (regex)
-               entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
-       else
-               entry->config = NULL;
+       entry->config =
+               (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
 
        /* Do not put duplicated community entry.  */
        if (community_list_dup_check(list, entry))
index 09b3a8718a6a1ac63872e5f491c0c86c3d687099..33f4d139b8271027baecad6b7355a119f578024b 100644 (file)
@@ -160,15 +160,6 @@ struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
        return new;
 }
 
-/* Retrun string representation of communities attribute. */
-char *lcommunity_str(struct lcommunity *lcom)
-{
-       if (!lcom->str)
-               lcom->str =
-                       lcommunity_lcom2str(lcom, LCOMMUNITY_FORMAT_DISPLAY);
-       return lcom->str;
-}
-
 /* Merge two Large Communities Attribute structure.  */
 struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
                                    struct lcommunity *lcom2)
@@ -186,6 +177,80 @@ struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
        return lcom1;
 }
 
+static void set_lcommunity_string(struct lcommunity *lcom, bool make_json)
+{
+       int i;
+       int len;
+       bool first = 1;
+       char *str_buf;
+       char *str_pnt;
+       uint8_t *pnt;
+       uint32_t global, local1, local2;
+       json_object *json_lcommunity_list = NULL;
+       json_object *json_string = NULL;
+
+#define LCOMMUNITY_STR_DEFAULT_LEN 32
+
+       if (!lcom)
+               return;
+
+       if (make_json) {
+               lcom->json = json_object_new_object();
+               json_lcommunity_list = json_object_new_array();
+       }
+
+       if (lcom->size == 0) {
+               str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1);
+               str_buf[0] = '\0';
+
+               if (make_json) {
+                       json_object_string_add(lcom->json, "string", "");
+                       json_object_object_add(lcom->json, "list",
+                                              json_lcommunity_list);
+               }
+
+               lcom->str = str_buf;
+               return;
+       }
+
+       str_buf = str_pnt =
+               XMALLOC(MTYPE_LCOMMUNITY_STR,
+                       (LCOMMUNITY_STR_DEFAULT_LEN * lcom->size) + 1);
+
+       for (i = 0; i < lcom->size; i++) {
+               if (first)
+                       first = 0;
+               else
+                       *str_pnt++ = ' ';
+
+               pnt = lcom->val + (i * LCOMMUNITY_SIZE);
+               pnt = ptr_get_be32(pnt, &global);
+               pnt = ptr_get_be32(pnt, &local1);
+               pnt = ptr_get_be32(pnt, &local2);
+               (void)pnt;
+
+               len = sprintf(str_pnt, "%u:%u:%u", global, local1, local2);
+               if (make_json) {
+                       json_string = json_object_new_string(str_pnt);
+                       json_object_array_add(json_lcommunity_list,
+                                             json_string);
+               }
+
+               str_pnt += len;
+       }
+
+       str_buf =
+               XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, str_pnt - str_buf + 1);
+
+       if (make_json) {
+               json_object_string_add(lcom->json, "string", str_buf);
+               json_object_object_add(lcom->json, "list",
+                                      json_lcommunity_list);
+       }
+
+       lcom->str = str_buf;
+}
+
 /* Intern Large Communities Attribute.  */
 struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
 {
@@ -201,8 +266,7 @@ struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
        find->refcnt++;
 
        if (!find->str)
-               find->str =
-                       lcommunity_lcom2str(find, LCOMMUNITY_FORMAT_DISPLAY);
+               set_lcommunity_string(find, false);
 
        return find;
 }
@@ -225,6 +289,21 @@ void lcommunity_unintern(struct lcommunity **lcom)
        }
 }
 
+/* Retrun string representation of communities attribute. */
+char *lcommunity_str(struct lcommunity *lcom, bool make_json)
+{
+       if (!lcom)
+               return NULL;
+
+       if (make_json && !lcom->json && lcom->str)
+               XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
+
+       if (!lcom->str)
+               set_lcommunity_string(lcom, make_json);
+
+       return lcom->str;
+}
+
 /* Utility function to make hash key.  */
 unsigned int lcommunity_hash_make(void *arg)
 {
@@ -388,59 +467,6 @@ int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
        return 0;
 }
 
-/* Convert large community attribute to string.
-   The large coms will be in 65535:65531:0 format.
-*/
-char *lcommunity_lcom2str(struct lcommunity *lcom, int format)
-{
-       int i;
-       uint8_t *pnt;
-#define LCOMMUNITY_STR_DEFAULT_LEN  40
-       int str_size;
-       int str_pnt;
-       char *str_buf;
-       int len = 0;
-       int first = 1;
-       uint32_t globaladmin, localdata1, localdata2;
-
-       if (lcom->size == 0) {
-               str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, 1);
-               str_buf[0] = '\0';
-               return str_buf;
-       }
-
-       /* Prepare buffer.  */
-       str_buf = XMALLOC(MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1);
-       str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1;
-       str_pnt = 0;
-
-       for (i = 0; i < lcom->size; i++) {
-               /* Make it sure size is enough.  */
-               while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size) {
-                       str_size *= 2;
-                       str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
-                                          str_size);
-               }
-
-               /* Space between each value.  */
-               if (!first)
-                       str_buf[str_pnt++] = ' ';
-
-               pnt = lcom->val + (i * LCOMMUNITY_SIZE);
-
-               pnt = ptr_get_be32(pnt, &globaladmin);
-               pnt = ptr_get_be32(pnt, &localdata1);
-               pnt = ptr_get_be32(pnt, &localdata2);
-               (void)pnt; /* consume value */
-
-               len = sprintf(str_buf + str_pnt, "%u:%u:%u", globaladmin,
-                             localdata1, localdata2);
-               str_pnt += len;
-               first = 0;
-       }
-       return str_buf;
-}
-
 int lcommunity_match(const struct lcommunity *lcom1,
                     const struct lcommunity *lcom2)
 {
index 78841accf3f2af5319f3c2127a77bdb42a9b8386..c88a016396720c8267eeba59e3593f09728d05f0 100644 (file)
 #ifndef _QUAGGA_BGP_LCOMMUNITY_H
 #define _QUAGGA_BGP_LCOMMUNITY_H
 
-/* Extended communities attribute string format.  */
-#define LCOMMUNITY_FORMAT_ROUTE_MAP            0
-#define LCOMMUNITY_FORMAT_COMMUNITY_LIST       1
-#define LCOMMUNITY_FORMAT_DISPLAY              2
+#include "lib/json.h"
 
 /* Large Communities value is twelve octets long.  */
 #define LCOMMUNITY_SIZE                        12
@@ -40,6 +37,9 @@ struct lcommunity {
        /* Large Communities value.  */
        uint8_t *val;
 
+       /* Large Communities as a json object */
+       json_object *json;
+
        /* Human readable format string.  */
        char *str;
 };
@@ -65,10 +65,9 @@ extern void lcommunity_unintern(struct lcommunity **);
 extern unsigned int lcommunity_hash_make(void *);
 extern struct hash *lcommunity_hash(void);
 extern struct lcommunity *lcommunity_str2com(const char *);
-extern char *lcommunity_lcom2str(struct lcommunity *, int);
 extern int lcommunity_match(const struct lcommunity *,
                            const struct lcommunity *);
-extern char *lcommunity_str(struct lcommunity *);
+extern char *lcommunity_str(struct lcommunity *, bool make_json);
 extern int lcommunity_include(struct lcommunity *lcom, uint8_t *ptr);
 extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr);
 #endif /* _QUAGGA_BGP_LCOMMUNITY_H */
index 997d708baf5d11eaebc15dbf7192ebd2fc33cce6..c0683b84ac4502d45b556e473359661918ccf734 100644 (file)
@@ -7414,7 +7414,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
        json_object *json_cluster_list = NULL;
        json_object *json_cluster_list_list = NULL;
        json_object *json_ext_community = NULL;
-       json_object *json_lcommunity = NULL;
        json_object *json_last_update = NULL;
        json_object *json_pmsi = NULL;
        json_object *json_nexthop_global = NULL;
@@ -8041,13 +8040,12 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
                /* Line 6 display Large community */
                if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) {
                        if (json_paths) {
-                               json_lcommunity = json_object_new_object();
-                               json_object_string_add(json_lcommunity,
-                                                      "string",
-                                                      attr->lcommunity->str);
+                               if (!attr->lcommunity->json)
+                                       lcommunity_str(attr->lcommunity, true);
+                               json_object_lock(attr->lcommunity->json);
                                json_object_object_add(json_path,
                                                       "largeCommunity",
-                                                      json_lcommunity);
+                                                      attr->lcommunity->json);
                        } else {
                                vty_out(vty, "      Large Community: %s\n",
                                        attr->lcommunity->str);
index b8c81232bb24147f22fa57e57455132f3370bc9f..35477ba164af38ed6b490ad0f6e092ce94630f87 100644 (file)
@@ -10859,7 +10859,7 @@ static void lcommunity_show_all_iterator(struct hash_backet *backet,
 
        lcom = (struct lcommunity *)backet->data;
        vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt,
-               lcommunity_str(lcom));
+               lcommunity_str(lcom, false));
 }
 
 /* Show BGP's community internal data. */
@@ -13606,6 +13606,24 @@ DEFUN (no_ip_community_list_expanded_all,
        return CMD_SUCCESS;
 }
 
+/* Return configuration string of community-list entry.  */
+static const char *community_list_config_str(struct community_entry *entry)
+{
+       const char *str;
+
+       if (entry->any)
+               str = "";
+       else {
+               if (entry->style == COMMUNITY_LIST_STANDARD)
+                       str = community_str(entry->u.com, false);
+               else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+                       str = lcommunity_str(entry->u.lcom, false);
+               else
+                       str = entry->config;
+       }
+       return str;
+}
+
 static void community_list_show(struct vty *vty, struct community_list *list)
 {
        struct community_entry *entry;
@@ -13631,9 +13649,7 @@ static void community_list_show(struct vty *vty, struct community_list *list)
                else
                        vty_out(vty, "    %s %s\n",
                                community_direct_str(entry->direct),
-                               entry->style == COMMUNITY_LIST_STANDARD
-                                       ? community_str(entry->u.com, false)
-                                       : entry->config);
+                               community_list_config_str(entry));
        }
 }
 
@@ -13985,9 +14001,7 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list)
                else
                        vty_out(vty, "    %s %s\n",
                                community_direct_str(entry->direct),
-                               entry->style == EXTCOMMUNITY_LIST_STANDARD
-                                       ? entry->u.ecom->str
-                                       : entry->config);
+                               community_list_config_str(entry));
        }
 }
 
@@ -14222,9 +14236,7 @@ static void extcommunity_list_show(struct vty *vty, struct community_list *list)
                else
                        vty_out(vty, "    %s %s\n",
                                community_direct_str(entry->direct),
-                               entry->style == EXTCOMMUNITY_LIST_STANDARD
-                                       ? entry->u.ecom->str
-                                       : entry->config);
+                               community_list_config_str(entry));
        }
 }
 
@@ -14275,22 +14287,6 @@ DEFUN (show_ip_extcommunity_list_arg,
        return CMD_SUCCESS;
 }
 
-/* Return configuration string of community-list entry.  */
-static const char *community_list_config_str(struct community_entry *entry)
-{
-       const char *str;
-
-       if (entry->any)
-               str = "";
-       else {
-               if (entry->style == COMMUNITY_LIST_STANDARD)
-                       str = community_str(entry->u.com, false);
-               else
-                       str = entry->config;
-       }
-       return str;
-}
-
 /* Display community-list and extcommunity-list configuration.  */
 static int community_list_config_write(struct vty *vty)
 {