]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_clist.c
Merge pull request #5717 from pguibert6WIND/flowspec_issue_redistribute
[mirror_frr.git] / bgpd / bgp_clist.c
index 7b64f349d2330d3a2f0396a4c5d8daf6c9d3d5bd..7ca48a5bea4a798106fceabd462e9f1af22f8d64 100644 (file)
@@ -27,6 +27,7 @@
 #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)
+/* Calculate new sequential number. */
+static int64_t bgp_clist_new_seq_get(struct community_list *list)
 {
-       struct community_list *cl = data;
+       int64_t maxseq;
+       int64_t newseq;
+       struct community_entry *entry;
+
+       maxseq = newseq = 0;
+
+       for (entry = list->head; entry; entry = entry->next) {
+               if (maxseq < entry->seq)
+                       maxseq = entry->seq;
+       }
+
+       newseq = ((maxseq / 5) * 5) + 5;
+
+       return (newseq > UINT_MAX) ? UINT_MAX : newseq;
+}
+
+/* Return community-list entry which has same seq number. */
+static struct community_entry *bgp_clist_seq_check(struct community_list *list,
+                                                  int64_t seq)
+{
+       struct community_entry *entry;
+
+       for (entry = list->head; entry; entry = entry->next)
+               if (entry->seq == seq)
+                       return entry;
+       return NULL;
+}
+
+static uint32_t bgp_clist_hash_key_community_list(const void *data)
+{
+       struct community_list *cl = (struct community_list *) data;
 
        if (cl->name_hash)
                return cl->name_hash;
@@ -156,7 +188,7 @@ community_list_insert(struct community_list_handler *ch, const char *name,
        /* If name is made by all digit character.  We treat it as
           number. */
        for (number = 0, i = 0; i < strlen(name); i++) {
-               if (isdigit((int)name[i]))
+               if (isdigit((unsigned char)name[i]))
                        number = (number * 10) + (name[i] - '0');
                else
                        break;
@@ -284,20 +316,6 @@ static int community_list_empty_p(struct community_list *list)
        return (list->head == NULL && list->tail == NULL) ? 1 : 0;
 }
 
-/* Add community-list entry to the list.  */
-static void community_list_entry_add(struct community_list *list,
-                                    struct community_entry *entry)
-{
-       entry->next = NULL;
-       entry->prev = list->tail;
-
-       if (list->tail)
-               list->tail->next = entry;
-       else
-               list->head = entry;
-       list->tail = entry;
-}
-
 /* Delete community-list entry from the list.  */
 static void community_list_entry_delete(struct community_list_master *cm,
                                        struct community_list *list,
@@ -319,6 +337,57 @@ static void community_list_entry_delete(struct community_list_master *cm,
                community_list_delete(cm, list);
 }
 
+/* Add community-list entry to the list.  */
+static void community_list_entry_add(struct community_list *list,
+                                    struct community_entry *entry,
+                                    struct community_list_handler *ch,
+                                    int master)
+{
+       struct community_list_master *cm = NULL;
+       struct community_entry *replace;
+       struct community_entry *point;
+
+       cm = community_list_master_lookup(ch, master);
+
+       /* Automatic assignment of seq no. */
+       if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO)
+               entry->seq = bgp_clist_new_seq_get(list);
+
+       if (list->tail && entry->seq > list->tail->seq)
+               point = NULL;
+       else {
+               replace = bgp_clist_seq_check(list, entry->seq);
+               if (replace)
+                       community_list_entry_delete(cm, list, entry);
+
+               /* Check insert point. */
+               for (point = list->head; point; point = point->next)
+                       if (point->seq >= entry->seq)
+                               break;
+       }
+
+       /* In case of this is the first element of the list. */
+       entry->next = point;
+
+       if (point) {
+               if (point->prev)
+                       point->prev->next = entry;
+               else
+                       list->head = entry;
+
+               entry->prev = point->prev;
+               point->prev = entry;
+       } else {
+               if (list->tail)
+                       list->tail->next = entry;
+               else
+                       list->head = entry;
+
+               entry->prev = list->tail;
+               list->tail = entry;
+       }
+}
+
 /* Lookup community-list entry from the list.  */
 static struct community_entry *
 community_list_entry_lookup(struct community_list *list, const void *arg,
@@ -695,6 +764,32 @@ int lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
        return 0;
 }
 
+
+/* Perform exact matching.  In case of expanded large-community-list, do
+ * same thing as lcommunity_list_match().
+ */
+int lcommunity_list_exact_match(struct lcommunity *lcom,
+                              struct community_list *list)
+{
+       struct community_entry *entry;
+
+       for (entry = list->head; entry; entry = entry->next) {
+               if (entry->any)
+                       return entry->direct == COMMUNITY_PERMIT ? 1 : 0;
+
+               if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
+                       if (lcommunity_cmp(lcom, entry->u.com))
+                               return entry->direct == COMMUNITY_PERMIT ? 1
+                                                                        : 0;
+               } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
+                       if (lcommunity_regexp_match(lcom, entry->reg))
+                               return entry->direct == COMMUNITY_PERMIT ? 1
+                                                                        : 0;
+               }
+       }
+       return 0;
+}
+
 int ecommunity_list_match(struct ecommunity *ecom, struct community_list *list)
 {
        struct community_entry *entry;
@@ -796,6 +891,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);
        }
 
@@ -850,12 +946,16 @@ static int community_list_dup_check(struct community_list *list,
 
 /* Set community-list.  */
 int community_list_set(struct community_list_handler *ch, const char *name,
-                      const char *str, int direct, int style)
+                      const char *str, const char *seq, int direct, int style)
 {
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct community *com = NULL;
        regex_t *regex = NULL;
+       int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+       if (seq)
+               seqnum = (int64_t)atol(seq);
 
        /* Get community list. */
        list = community_list_get(ch, name, COMMUNITY_LIST_MASTER);
@@ -891,6 +991,7 @@ int community_list_set(struct community_list_handler *ch, const char *name,
        entry->any = (str ? 0 : 1);
        entry->u.com = com;
        entry->reg = regex;
+       entry->seq = seqnum;
        entry->config =
                (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
 
@@ -898,7 +999,8 @@ int community_list_set(struct community_list_handler *ch, const char *name,
        if (community_list_dup_check(list, entry))
                community_entry_free(entry);
        else {
-               community_list_entry_add(list, entry);
+               community_list_entry_add(list, entry, ch,
+                                        COMMUNITY_LIST_MASTER);
                route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED);
        }
 
@@ -907,7 +1009,8 @@ int community_list_set(struct community_list_handler *ch, const char *name,
 
 /* Unset community-list */
 int community_list_unset(struct community_list_handler *ch, const char *name,
-                        const char *str, int direct, int style)
+                        const char *str, const char *seq, int direct,
+                        int style)
 {
        struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;
@@ -1000,14 +1103,45 @@ 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)
+                       const char *str, const char *seq, int direct, int style)
 {
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct lcommunity *lcom = NULL;
        regex_t *regex = NULL;
+       int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+       if (seq)
+               seqnum = (int64_t)atol(seq);
 
        /* Get community list. */
        list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER);
@@ -1028,6 +1162,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
@@ -1043,14 +1180,18 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
        entry->any = (str ? 0 : 1);
        entry->u.lcom = lcom;
        entry->reg = regex;
+       entry->seq = seqnum;
        entry->config =
                (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
 
        /* Do not put duplicated community entry.  */
        if (community_list_dup_check(list, entry))
                community_entry_free(entry);
-       else
-               community_list_entry_add(list, entry);
+       else {
+               community_list_entry_add(list, entry, ch,
+                                        LARGE_COMMUNITY_LIST_MASTER);
+               route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
+       }
 
        return 0;
 }
@@ -1058,7 +1199,8 @@ int lcommunity_list_set(struct community_list_handler *ch, const char *name,
 /* Unset community-list.  When str is NULL, delete all of
    community-list entry belongs to the specified name.  */
 int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
-                         const char *str, int direct, int style)
+                         const char *str, const char *seq, int direct,
+                         int style)
 {
        struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;
@@ -1075,6 +1217,7 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
        /* Delete all of entry belongs to this community-list.  */
        if (!str) {
                community_list_delete(cm, list);
+               route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
                return 0;
        }
 
@@ -1100,18 +1243,24 @@ int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
                return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
 
        community_list_entry_delete(cm, list, entry);
+       route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
 
        return 0;
 }
 
 /* Set extcommunity-list.  */
 int extcommunity_list_set(struct community_list_handler *ch, const char *name,
-                         const char *str, int direct, int style)
+                         const char *str, const char *seq, int direct,
+                         int style)
 {
        struct community_entry *entry = NULL;
        struct community_list *list;
        struct ecommunity *ecom = NULL;
        regex_t *regex = NULL;
+       int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+       if (seq)
+               seqnum = (int64_t)atol(seq);
 
        if (str == NULL)
                return COMMUNITY_LIST_ERR_MALFORMED_VAL;
@@ -1158,12 +1307,14 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name,
 
        entry->u.ecom = ecom;
        entry->reg = regex;
+       entry->seq = seqnum;
 
        /* Do not put duplicated community entry.  */
        if (community_list_dup_check(list, entry))
                community_entry_free(entry);
        else {
-               community_list_entry_add(list, entry);
+               community_list_entry_add(list, entry, ch,
+                                        EXTCOMMUNITY_LIST_MASTER);
                route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED);
        }
 
@@ -1176,7 +1327,8 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name,
  * specified name.
  */
 int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
-                           const char *str, int direct, int style)
+                           const char *str, const char *seq, int direct,
+                           int style)
 {
        struct community_list_master *cm = NULL;
        struct community_entry *entry = NULL;