]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_clist.c
bgpd: Convert to network byte order before passing value to `community_del_val`
[mirror_frr.git] / bgpd / bgp_clist.c
index fec4397b73c99d2b42cd3aa9b1a65db64b8c0e4f..81ef03ec5827ab24a9c560bd6623f3c9a40f02c7 100644 (file)
@@ -26,6 +26,8 @@
 #include "queue.h"
 #include "filter.h"
 #include "stream.h"
+#include "jhash.h"
+#include "frrstr.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_community.h"
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_clist.h"
 
+static uint32_t bgp_clist_hash_key_community_list(void *data)
+{
+       struct community_list *cl = data;
+
+       if (cl->name_hash)
+               return cl->name_hash;
+
+       cl->name_hash = bgp_clist_hash_key(cl->name);
+       return cl->name_hash;
+}
+
+static bool bgp_clist_hash_cmp_community_list(const void *a1, const void *a2)
+{
+       const struct community_list *cl1 = a1;
+       const struct community_list *cl2 = a2;
+
+       if (cl1->name_hash != cl2->name_hash)
+               return false;
+
+       if (strcmp(cl1->name, cl2->name) == 0)
+               return true;
+
+       return false;
+}
+
 /* Lookup master structure for community-list or
    extcommunity-list.  */
 struct community_list_master *
@@ -65,7 +92,7 @@ static void community_entry_free(struct community_entry *entry)
        switch (entry->style) {
        case COMMUNITY_LIST_STANDARD:
                if (entry->u.com)
-                       community_free(entry->u.com);
+                       community_free(&entry->u.com);
                break;
        case LARGE_COMMUNITY_LIST_STANDARD:
                if (entry->u.lcom)
@@ -74,16 +101,14 @@ static void community_entry_free(struct community_entry *entry)
        case EXTCOMMUNITY_LIST_STANDARD:
                /* In case of standard extcommunity-list, configuration string
                   is made by ecommunity_ecom2str().  */
-               if (entry->config)
-                       XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
+               XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
                if (entry->u.ecom)
                        ecommunity_free(&entry->u.ecom);
                break;
        case COMMUNITY_LIST_EXPANDED:
        case EXTCOMMUNITY_LIST_EXPANDED:
        case LARGE_COMMUNITY_LIST_EXPANDED:
-               if (entry->config)
-                       XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
+               XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
                if (entry->reg)
                        bgp_regex_free(entry->reg);
        default:
@@ -101,8 +126,7 @@ static struct community_list *community_list_new(void)
 /* Free community-list.  */
 static void community_list_free(struct community_list *list)
 {
-       if (list->name)
-               XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
+       XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
        XFREE(MTYPE_COMMUNITY_LIST, list);
 }
 
@@ -125,6 +149,10 @@ community_list_insert(struct community_list_handler *ch, const char *name,
        /* Allocate new community_list and copy given name. */
        new = community_list_new();
        new->name = XSTRDUP(MTYPE_COMMUNITY_LIST_NAME, name);
+       new->name_hash = bgp_clist_hash_key_community_list(new);
+
+       /* Save for later */
+       hash_get(cm->hash, new, hash_alloc_intern);
 
        /* If name is made by all digit character.  We treat it as
           number. */
@@ -194,9 +222,11 @@ community_list_insert(struct community_list_handler *ch, const char *name,
 }
 
 struct community_list *community_list_lookup(struct community_list_handler *ch,
-                                            const char *name, int master)
+                                            const char *name,
+                                            uint32_t name_hash,
+                                            int master)
 {
-       struct community_list *list;
+       struct community_list lookup;
        struct community_list_master *cm;
 
        if (!name)
@@ -206,14 +236,9 @@ struct community_list *community_list_lookup(struct community_list_handler *ch,
        if (!cm)
                return NULL;
 
-       for (list = cm->num.head; list; list = list->next)
-               if (strcmp(list->name, name) == 0)
-                       return list;
-       for (list = cm->str.head; list; list = list->next)
-               if (strcmp(list->name, name) == 0)
-                       return list;
-
-       return NULL;
+       lookup.name = (char *)name;
+       lookup.name_hash = name_hash;
+       return hash_get(cm->hash, &lookup, NULL);
 }
 
 static struct community_list *
@@ -222,13 +247,14 @@ community_list_get(struct community_list_handler *ch, const char *name,
 {
        struct community_list *list;
 
-       list = community_list_lookup(ch, name, master);
+       list = community_list_lookup(ch, name, 0, master);
        if (!list)
                list = community_list_insert(ch, name, master);
        return list;
 }
 
-static void community_list_delete(struct community_list *list)
+static void community_list_delete(struct community_list_master *cm,
+                                 struct community_list *list)
 {
        struct community_list_list *clist;
        struct community_entry *entry, *next;
@@ -250,6 +276,7 @@ static void community_list_delete(struct community_list *list)
        else
                clist->head = list->next;
 
+       hash_release(cm->hash, list);
        community_list_free(list);
 }
 
@@ -273,7 +300,8 @@ static void community_list_entry_add(struct community_list *list,
 }
 
 /* Delete community-list entry from the list.  */
-static void community_list_entry_delete(struct community_list *list,
+static void community_list_entry_delete(struct community_list_master *cm,
+                                       struct community_list *list,
                                        struct community_entry *entry)
 {
        if (entry->next)
@@ -289,7 +317,7 @@ static void community_list_entry_delete(struct community_list *list,
        community_entry_free(entry);
 
        if (community_list_empty_p(list))
-               community_list_delete(list);
+               community_list_delete(cm, list);
 }
 
 /* Lookup community-list entry from the list.  */
@@ -332,141 +360,70 @@ community_list_entry_lookup(struct community_list *list, const void *arg,
 
 static char *community_str_get(struct community *com, int i)
 {
-       int len;
        uint32_t comval;
        uint16_t as;
        uint16_t val;
        char *str;
-       char *pnt;
 
        memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
        comval = ntohl(comval);
 
        switch (comval) {
        case COMMUNITY_INTERNET:
-               len = strlen(" internet");
-               break;
-       case COMMUNITY_GSHUT:
-               len = strlen(" graceful-shutdown");
-               break;
-       case COMMUNITY_ACCEPT_OWN:
-               len = strlen(" accept-own");
-               break;
-       case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
-               len = strlen(" route-filter-translated-v4");
-               break;
-       case COMMUNITY_ROUTE_FILTER_v4:
-               len = strlen(" route-filter-v4");
-               break;
-       case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
-               len = strlen(" route-filter-translated-v6");
-               break;
-       case COMMUNITY_ROUTE_FILTER_v6:
-               len = strlen(" route-filter-v6");
-               break;
-       case COMMUNITY_LLGR_STALE:
-               len = strlen(" llgr-stale");
-               break;
-       case COMMUNITY_NO_LLGR:
-               len = strlen(" no-llgr");
-               break;
-       case COMMUNITY_ACCEPT_OWN_NEXTHOP:
-               len = strlen(" accept-own-nexthop");
-               break;
-       case COMMUNITY_BLACKHOLE:
-               len = strlen(" blackhole");
-               break;
-       case COMMUNITY_NO_EXPORT:
-               len = strlen(" no-export");
-               break;
-       case COMMUNITY_NO_ADVERTISE:
-               len = strlen(" no-advertise");
-               break;
-       case COMMUNITY_LOCAL_AS:
-               len = strlen(" local-AS");
-               break;
-       case COMMUNITY_NO_PEER:
-               len = strlen(" no-peer");
-               break;
-       default:
-               len = strlen(" 65536:65535");
-               break;
-       }
-
-       /* Allocate memory.  */
-       str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len);
-
-       switch (comval) {
-       case COMMUNITY_INTERNET:
-               strcpy(pnt, "internet");
-               pnt += strlen("internet");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet");
                break;
        case COMMUNITY_GSHUT:
-               strcpy(pnt, "graceful-shutdown");
-               pnt += strlen("graceful-shutdown");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown");
                break;
        case COMMUNITY_ACCEPT_OWN:
-               strcpy(pnt, "accept-own");
-               pnt += strlen("accept-own");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own");
                break;
        case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
-               strcpy(pnt, "route-filter-translated-v4");
-               pnt += strlen("route-filter-translated-v4");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR,
+                             "route-filter-translated-v4");
                break;
        case COMMUNITY_ROUTE_FILTER_v4:
-               strcpy(pnt, "route-filter-v4");
-               pnt += strlen("route-filter-v4");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v4");
                break;
        case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
-               strcpy(pnt, "route-filter-translated-v6");
-               pnt += strlen("route-filter-translated-v6");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR,
+                             "route-filter-translated-v6");
                break;
        case COMMUNITY_ROUTE_FILTER_v6:
-               strcpy(pnt, "route-filter-v6");
-               pnt += strlen("route-filter-v6");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v6");
                break;
        case COMMUNITY_LLGR_STALE:
-               strcpy(pnt, "llgr-stale");
-               pnt += strlen("llgr-stale");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "llgr-stale");
                break;
        case COMMUNITY_NO_LLGR:
-               strcpy(pnt, "no-llgr");
-               pnt += strlen("no-llgr");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-llgr");
                break;
        case COMMUNITY_ACCEPT_OWN_NEXTHOP:
-               strcpy(pnt, "accept-own-nexthop");
-               pnt += strlen("accept-own-nexthop");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own-nexthop");
                break;
        case COMMUNITY_BLACKHOLE:
-               strcpy(pnt, "blackhole");
-               pnt += strlen("blackhole");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "blackhole");
                break;
        case COMMUNITY_NO_EXPORT:
-               strcpy(pnt, "no-export");
-               pnt += strlen("no-export");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-export");
                break;
        case COMMUNITY_NO_ADVERTISE:
-               strcpy(pnt, "no-advertise");
-               pnt += strlen("no-advertise");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-advertise");
                break;
        case COMMUNITY_LOCAL_AS:
-               strcpy(pnt, "local-AS");
-               pnt += strlen("local-AS");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "local-AS");
                break;
        case COMMUNITY_NO_PEER:
-               strcpy(pnt, "no-peer");
-               pnt += strlen("no-peer");
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-peer");
                break;
        default:
+               str = XSTRDUP(MTYPE_COMMUNITY_STR, "65536:65535");
                as = (comval >> 16) & 0xFFFF;
                val = comval & 0xFFFF;
-               sprintf(pnt, "%u:%d", as, val);
-               pnt += strlen(pnt);
+               snprintf(str, strlen(str), "%u:%d", as, val);
                break;
        }
 
-       *pnt = '\0';
-
        return str;
 }
 
@@ -840,6 +797,7 @@ struct community *community_list_match_delete(struct community *com,
        /* Delete all of the communities we flagged for deletion */
        for (i = delete_index - 1; i >= 0; i--) {
                val = community_val_get(com, com_index_to_delete[i]);
+               val = htonl(val);
                community_del_val(com, &val);
        }
 
@@ -953,18 +911,20 @@ int community_list_set(struct community_list_handler *ch, const char *name,
 int community_list_unset(struct community_list_handler *ch, const char *name,
                         const char *str, int direct, int style)
 {
+       struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct community *com = NULL;
 
        /* Lookup community list.  */
-       list = community_list_lookup(ch, name, COMMUNITY_LIST_MASTER);
+       list = community_list_lookup(ch, name, 0, COMMUNITY_LIST_MASTER);
        if (list == NULL)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
+       cm = community_list_master_lookup(ch, COMMUNITY_LIST_MASTER);
        /* Delete all of entry belongs to this community-list.  */
        if (!str) {
-               community_list_delete(list);
+               community_list_delete(cm, list);
                route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
                return 0;
        }
@@ -974,14 +934,14 @@ int community_list_unset(struct community_list_handler *ch, const char *name,
 
        if (com) {
                entry = community_list_entry_lookup(list, com, direct);
-               community_free(com);
+               community_free(&com);
        } else
                entry = community_list_entry_lookup(list, str, direct);
 
        if (!entry)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
-       community_list_entry_delete(list, entry);
+       community_list_entry_delete(cm, list, entry);
        route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
 
        return 0;
@@ -1042,6 +1002,33 @@ struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
        return lcom;
 }
 
+/* Helper to check if every octet do not exceed UINT_MAX */
+static int lcommunity_list_valid(const char *community)
+{
+       int octets = 0;
+       char **splits;
+       int num;
+
+       frrstr_split(community, ":", &splits, &num);
+
+       for (int i = 0; i < num; i++) {
+               if (strtoul(splits[i], NULL, 10) > UINT_MAX)
+                       return 0;
+
+               if (strlen(splits[i]) == 0)
+                       return 0;
+
+               octets++;
+               XFREE(MTYPE_TMP, splits[i]);
+       }
+       XFREE(MTYPE_TMP, splits);
+
+       if (octets < 3)
+               return 0;
+
+       return 1;
+}
+
 /* Set lcommunity-list.  */
 int lcommunity_list_set(struct community_list_handler *ch, const char *name,
                        const char *str, int direct, int style)
@@ -1070,6 +1057,9 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
        }
 
        if (str) {
+               if (!lcommunity_list_valid(str))
+                       return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
                if (style == LARGE_COMMUNITY_LIST_STANDARD)
                        lcom = lcommunity_str2com(str);
                else
@@ -1091,8 +1081,10 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
        /* Do not put duplicated community entry.  */
        if (community_list_dup_check(list, entry))
                community_entry_free(entry);
-       else
+       else {
                community_list_entry_add(list, entry);
+               route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
+       }
 
        return 0;
 }
@@ -1102,19 +1094,22 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
 int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
                          const char *str, int direct, int style)
 {
+       struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct lcommunity *lcom = NULL;
        regex_t *regex = NULL;
 
        /* Lookup community list.  */
-       list = community_list_lookup(ch, name, LARGE_COMMUNITY_LIST_MASTER);
+       list = community_list_lookup(ch, name, 0, LARGE_COMMUNITY_LIST_MASTER);
        if (list == NULL)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
+       cm = community_list_master_lookup(ch, LARGE_COMMUNITY_LIST_MASTER);
        /* Delete all of entry belongs to this community-list.  */
        if (!str) {
-               community_list_delete(list);
+               community_list_delete(cm, list);
+               route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
                return 0;
        }
 
@@ -1139,7 +1134,8 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
        if (!entry)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
-       community_list_entry_delete(list, entry);
+       community_list_entry_delete(cm, list, entry);
+       route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
 
        return 0;
 }
@@ -1218,18 +1214,20 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name,
 int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
                            const char *str, int direct, int style)
 {
+       struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct ecommunity *ecom = NULL;
 
        /* Lookup extcommunity list.  */
-       list = community_list_lookup(ch, name, EXTCOMMUNITY_LIST_MASTER);
+       list = community_list_lookup(ch, name, 0, EXTCOMMUNITY_LIST_MASTER);
        if (list == NULL)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
+       cm = community_list_master_lookup(ch, EXTCOMMUNITY_LIST_MASTER);
        /* Delete all of entry belongs to this extcommunity-list.  */
        if (!str) {
-               community_list_delete(list);
+               community_list_delete(cm, list);
                route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
                return 0;
        }
@@ -1246,7 +1244,7 @@ int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
        if (!entry)
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
-       community_list_entry_delete(list, entry);
+       community_list_entry_delete(cm, list, entry);
        route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
 
        return 0;
@@ -1258,6 +1256,22 @@ struct community_list_handler *community_list_init(void)
        struct community_list_handler *ch;
        ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
                     sizeof(struct community_list_handler));
+
+       ch->community_list.hash =
+               hash_create_size(4, bgp_clist_hash_key_community_list,
+                                bgp_clist_hash_cmp_community_list,
+                                "Community List Number Quick Lookup");
+
+       ch->extcommunity_list.hash =
+               hash_create_size(4, bgp_clist_hash_key_community_list,
+                                bgp_clist_hash_cmp_community_list,
+                                "Extended Community List Quick Lookup");
+
+       ch->lcommunity_list.hash =
+               hash_create_size(4, bgp_clist_hash_key_community_list,
+                                bgp_clist_hash_cmp_community_list,
+                                "Large Community List Quick Lookup");
+
        return ch;
 }
 
@@ -1269,21 +1283,24 @@ void community_list_terminate(struct community_list_handler *ch)
 
        cm = &ch->community_list;
        while ((list = cm->num.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
        while ((list = cm->str.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
+       hash_free(cm->hash);
 
        cm = &ch->lcommunity_list;
        while ((list = cm->num.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
        while ((list = cm->str.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
+       hash_free(cm->hash);
 
        cm = &ch->extcommunity_list;
        while ((list = cm->num.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
        while ((list = cm->str.head) != NULL)
-               community_list_delete(list);
+               community_list_delete(cm, list);
+       hash_free(cm->hash);
 
        XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
 }