back to topotato report
topotato coverage report
Current view: top level - bgpd - bgp_clist.c (source / functions) Hit Total Coverage
Test: test_exabgp_demo.py::ExaBGPDemo Lines: 32 730 4.4 %
Date: 2023-02-24 18:37:55 Functions: 3 43 7.0 %

          Line data    Source code
       1             : /* BGP community-list and extcommunity-list.
       2             :  * Copyright (C) 1999 Kunihiro Ishiguro
       3             :  *
       4             :  * This file is part of GNU Zebra.
       5             :  *
       6             :  * GNU Zebra is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the
       8             :  * Free Software Foundation; either version 2, or (at your option) any
       9             :  * later version.
      10             :  *
      11             :  * GNU Zebra is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : #include <zebra.h>
      22             : 
      23             : #include "command.h"
      24             : #include "prefix.h"
      25             : #include "memory.h"
      26             : #include "queue.h"
      27             : #include "filter.h"
      28             : #include "stream.h"
      29             : #include "jhash.h"
      30             : #include "frrstr.h"
      31             : 
      32             : #include "bgpd/bgpd.h"
      33             : #include "bgpd/bgp_community.h"
      34             : #include "bgpd/bgp_ecommunity.h"
      35             : #include "bgpd/bgp_lcommunity.h"
      36             : #include "bgpd/bgp_community_alias.h"
      37             : #include "bgpd/bgp_aspath.h"
      38             : #include "bgpd/bgp_regex.h"
      39             : #include "bgpd/bgp_clist.h"
      40             : 
      41             : /* Calculate new sequential number. */
      42           0 : static int64_t bgp_clist_new_seq_get(struct community_list *list)
      43             : {
      44           0 :         int64_t maxseq;
      45           0 :         int64_t newseq;
      46           0 :         struct community_entry *entry;
      47             : 
      48           0 :         maxseq = 0;
      49             : 
      50           0 :         for (entry = list->head; entry; entry = entry->next) {
      51           0 :                 if (maxseq < entry->seq)
      52             :                         maxseq = entry->seq;
      53             :         }
      54             : 
      55           0 :         newseq = ((maxseq / 5) * 5) + 5;
      56             : 
      57           0 :         return (newseq > UINT_MAX) ? UINT_MAX : newseq;
      58             : }
      59             : 
      60             : /* Return community-list entry which has same seq number. */
      61           0 : static struct community_entry *bgp_clist_seq_check(struct community_list *list,
      62             :                                                    int64_t seq)
      63             : {
      64           0 :         struct community_entry *entry;
      65             : 
      66           0 :         for (entry = list->head; entry; entry = entry->next)
      67           0 :                 if (entry->seq == seq)
      68             :                         return entry;
      69             :         return NULL;
      70             : }
      71             : 
      72           0 : static uint32_t bgp_clist_hash_key_community_list(const void *data)
      73             : {
      74           0 :         struct community_list *cl = (struct community_list *) data;
      75             : 
      76           0 :         if (cl->name_hash)
      77             :                 return cl->name_hash;
      78             : 
      79           0 :         cl->name_hash = bgp_clist_hash_key(cl->name);
      80           0 :         return cl->name_hash;
      81             : }
      82             : 
      83           0 : static bool bgp_clist_hash_cmp_community_list(const void *a1, const void *a2)
      84             : {
      85           0 :         const struct community_list *cl1 = a1;
      86           0 :         const struct community_list *cl2 = a2;
      87             : 
      88           0 :         if (cl1->name_hash != cl2->name_hash)
      89             :                 return false;
      90             : 
      91           0 :         if (strcmp(cl1->name, cl2->name) == 0)
      92           0 :                 return true;
      93             : 
      94             :         return false;
      95             : }
      96             : 
      97             : /* Lookup master structure for community-list or
      98             :    extcommunity-list.  */
      99             : struct community_list_master *
     100           0 : community_list_master_lookup(struct community_list_handler *ch, int master)
     101             : {
     102           0 :         if (ch)
     103           0 :                 switch (master) {
     104           0 :                 case COMMUNITY_LIST_MASTER:
     105           0 :                         return &ch->community_list;
     106           0 :                 case EXTCOMMUNITY_LIST_MASTER:
     107           0 :                         return &ch->extcommunity_list;
     108           0 :                 case LARGE_COMMUNITY_LIST_MASTER:
     109           0 :                         return &ch->lcommunity_list;
     110             :                 }
     111             :         return NULL;
     112             : }
     113             : 
     114             : /* Allocate a new community list entry.  */
     115           0 : static struct community_entry *community_entry_new(void)
     116             : {
     117           0 :         return XCALLOC(MTYPE_COMMUNITY_LIST_ENTRY,
     118             :                        sizeof(struct community_entry));
     119             : }
     120             : 
     121             : /* Free community list entry.  */
     122           0 : static void community_entry_free(struct community_entry *entry)
     123             : {
     124           0 :         switch (entry->style) {
     125           0 :         case COMMUNITY_LIST_STANDARD:
     126           0 :                 if (entry->u.com)
     127           0 :                         community_free(&entry->u.com);
     128             :                 break;
     129           0 :         case LARGE_COMMUNITY_LIST_STANDARD:
     130           0 :                 if (entry->u.lcom)
     131           0 :                         lcommunity_free(&entry->u.lcom);
     132             :                 break;
     133           0 :         case EXTCOMMUNITY_LIST_STANDARD:
     134             :                 /* In case of standard extcommunity-list, configuration string
     135             :                    is made by ecommunity_ecom2str().  */
     136           0 :                 XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
     137           0 :                 if (entry->u.ecom)
     138           0 :                         ecommunity_free(&entry->u.ecom);
     139             :                 break;
     140           0 :         case COMMUNITY_LIST_EXPANDED:
     141             :         case EXTCOMMUNITY_LIST_EXPANDED:
     142             :         case LARGE_COMMUNITY_LIST_EXPANDED:
     143           0 :                 XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
     144           0 :                 if (entry->reg)
     145           0 :                         bgp_regex_free(entry->reg);
     146             :         default:
     147             :                 break;
     148             :         }
     149           0 :         XFREE(MTYPE_COMMUNITY_LIST_ENTRY, entry);
     150           0 : }
     151             : 
     152             : /* Allocate a new community-list.  */
     153           0 : static struct community_list *community_list_new(void)
     154             : {
     155           0 :         return XCALLOC(MTYPE_COMMUNITY_LIST, sizeof(struct community_list));
     156             : }
     157             : 
     158             : /* Free community-list.  */
     159           0 : static void community_list_free(struct community_list *list)
     160             : {
     161           0 :         XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
     162           0 :         XFREE(MTYPE_COMMUNITY_LIST, list);
     163           0 : }
     164             : 
     165             : static struct community_list *
     166           0 : community_list_insert(struct community_list_handler *ch, const char *name,
     167             :                       int master)
     168             : {
     169           0 :         size_t i;
     170           0 :         long number;
     171           0 :         struct community_list *new;
     172           0 :         struct community_list *point;
     173           0 :         struct community_list_list *list;
     174           0 :         struct community_list_master *cm;
     175             : 
     176             :         /* Lookup community-list master.  */
     177           0 :         cm = community_list_master_lookup(ch, master);
     178           0 :         if (!cm)
     179             :                 return NULL;
     180             : 
     181             :         /* Allocate new community_list and copy given name. */
     182           0 :         new = community_list_new();
     183           0 :         new->name = XSTRDUP(MTYPE_COMMUNITY_LIST_NAME, name);
     184           0 :         new->name_hash = bgp_clist_hash_key_community_list(new);
     185             : 
     186             :         /* Save for later */
     187           0 :         (void)hash_get(cm->hash, new, hash_alloc_intern);
     188             : 
     189             :         /* If name is made by all digit character.  We treat it as
     190             :            number. */
     191           0 :         for (number = 0, i = 0; i < strlen(name); i++) {
     192           0 :                 if (isdigit((unsigned char)name[i]))
     193           0 :                         number = (number * 10) + (name[i] - '0');
     194             :                 else
     195             :                         break;
     196             :         }
     197             : 
     198             :         /* In case of name is all digit character */
     199           0 :         if (i == strlen(name)) {
     200           0 :                 new->sort = COMMUNITY_LIST_NUMBER;
     201             : 
     202             :                 /* Set access_list to number list. */
     203           0 :                 list = &cm->num;
     204             : 
     205           0 :                 for (point = list->head; point; point = point->next)
     206           0 :                         if (atol(point->name) >= number)
     207             :                                 break;
     208             :         } else {
     209           0 :                 new->sort = COMMUNITY_LIST_STRING;
     210             : 
     211             :                 /* Set access_list to string list. */
     212           0 :                 list = &cm->str;
     213             : 
     214             :                 /* Set point to insertion point. */
     215           0 :                 for (point = list->head; point; point = point->next)
     216           0 :                         if (strcmp(point->name, name) >= 0)
     217             :                                 break;
     218             :         }
     219             : 
     220             :         /* Link to upper list.  */
     221           0 :         new->parent = list;
     222             : 
     223             :         /* In case of this is the first element of master. */
     224           0 :         if (list->head == NULL) {
     225           0 :                 list->head = list->tail = new;
     226           0 :                 return new;
     227             :         }
     228             : 
     229             :         /* In case of insertion is made at the tail of access_list. */
     230           0 :         if (point == NULL) {
     231           0 :                 new->prev = list->tail;
     232           0 :                 list->tail->next = new;
     233           0 :                 list->tail = new;
     234           0 :                 return new;
     235             :         }
     236             : 
     237             :         /* In case of insertion is made at the head of access_list. */
     238           0 :         if (point == list->head) {
     239           0 :                 new->next = list->head;
     240           0 :                 list->head->prev = new;
     241           0 :                 list->head = new;
     242           0 :                 return new;
     243             :         }
     244             : 
     245             :         /* Insertion is made at middle of the access_list. */
     246           0 :         new->next = point;
     247           0 :         new->prev = point->prev;
     248             : 
     249           0 :         if (point->prev)
     250           0 :                 point->prev->next = new;
     251           0 :         point->prev = new;
     252             : 
     253           0 :         return new;
     254             : }
     255             : 
     256           0 : struct community_list *community_list_lookup(struct community_list_handler *ch,
     257             :                                              const char *name,
     258             :                                              uint32_t name_hash,
     259             :                                              int master)
     260             : {
     261           0 :         struct community_list lookup;
     262           0 :         struct community_list_master *cm;
     263             : 
     264           0 :         if (!name)
     265             :                 return NULL;
     266             : 
     267           0 :         cm = community_list_master_lookup(ch, master);
     268           0 :         if (!cm)
     269             :                 return NULL;
     270             : 
     271           0 :         lookup.name = (char *)name;
     272           0 :         lookup.name_hash = name_hash;
     273           0 :         return hash_get(cm->hash, &lookup, NULL);
     274             : }
     275             : 
     276             : static struct community_list *
     277           0 : community_list_get(struct community_list_handler *ch, const char *name,
     278             :                    int master)
     279             : {
     280           0 :         struct community_list *list;
     281             : 
     282           0 :         list = community_list_lookup(ch, name, 0, master);
     283           0 :         if (!list)
     284           0 :                 list = community_list_insert(ch, name, master);
     285           0 :         return list;
     286             : }
     287             : 
     288           0 : static void community_list_delete(struct community_list_master *cm,
     289             :                                   struct community_list *list)
     290             : {
     291           0 :         struct community_list_list *clist;
     292           0 :         struct community_entry *entry, *next;
     293             : 
     294           0 :         for (entry = list->head; entry; entry = next) {
     295           0 :                 next = entry->next;
     296           0 :                 community_entry_free(entry);
     297             :         }
     298             : 
     299           0 :         clist = list->parent;
     300             : 
     301           0 :         if (list->next)
     302           0 :                 list->next->prev = list->prev;
     303             :         else
     304           0 :                 clist->tail = list->prev;
     305             : 
     306           0 :         if (list->prev)
     307           0 :                 list->prev->next = list->next;
     308             :         else
     309           0 :                 clist->head = list->next;
     310             : 
     311           0 :         hash_release(cm->hash, list);
     312           0 :         community_list_free(list);
     313           0 : }
     314             : 
     315           0 : static bool community_list_empty_p(struct community_list *list)
     316             : {
     317           0 :         return list->head == NULL && list->tail == NULL;
     318             : }
     319             : 
     320             : /* Delete community-list entry from the list.  */
     321           0 : static void community_list_entry_delete(struct community_list_master *cm,
     322             :                                         struct community_list *list,
     323             :                                         struct community_entry *entry)
     324             : {
     325           0 :         if (entry->next)
     326           0 :                 entry->next->prev = entry->prev;
     327             :         else
     328           0 :                 list->tail = entry->prev;
     329             : 
     330           0 :         if (entry->prev)
     331           0 :                 entry->prev->next = entry->next;
     332             :         else
     333           0 :                 list->head = entry->next;
     334             : 
     335           0 :         community_entry_free(entry);
     336             : 
     337           0 :         if (community_list_empty_p(list))
     338           0 :                 community_list_delete(cm, list);
     339           0 : }
     340             : 
     341             : /*
     342             :  * Replace community-list entry in the list. Note that entry is the new one
     343             :  * and replace is one one being replaced.
     344             :  */
     345           0 : static void community_list_entry_replace(struct community_list *list,
     346             :                                          struct community_entry *replace,
     347             :                                          struct community_entry *entry)
     348             : {
     349           0 :         if (replace->next) {
     350           0 :                 entry->next = replace->next;
     351           0 :                 replace->next->prev = entry;
     352             :         } else {
     353           0 :                 entry->next = NULL;
     354           0 :                 list->tail = entry;
     355             :         }
     356             : 
     357           0 :         if (replace->prev) {
     358           0 :                 entry->prev = replace->prev;
     359           0 :                 replace->prev->next = entry;
     360             :         } else {
     361           0 :                 entry->prev = NULL;
     362           0 :                 list->head = entry;
     363             :         }
     364             : 
     365           0 :         community_entry_free(replace);
     366           0 : }
     367             : 
     368             : /* Add community-list entry to the list.  */
     369           0 : static void community_list_entry_add(struct community_list *list,
     370             :                                      struct community_entry *entry,
     371             :                                      struct community_list_handler *ch,
     372             :                                      int master)
     373             : {
     374           0 :         struct community_entry *replace;
     375           0 :         struct community_entry *point;
     376             : 
     377             :         /* Automatic assignment of seq no. */
     378           0 :         if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO)
     379           0 :                 entry->seq = bgp_clist_new_seq_get(list);
     380             : 
     381           0 :         if (list->tail && entry->seq > list->tail->seq)
     382             :                 point = NULL;
     383             :         else {
     384           0 :                 replace = bgp_clist_seq_check(list, entry->seq);
     385           0 :                 if (replace) {
     386           0 :                         community_list_entry_replace(list, replace, entry);
     387           0 :                         return;
     388             :                 }
     389             : 
     390             :                 /* Check insert point. */
     391           0 :                 for (point = list->head; point; point = point->next)
     392           0 :                         if (point->seq >= entry->seq)
     393             :                                 break;
     394             :         }
     395             : 
     396             :         /* In case of this is the first element of the list. */
     397           0 :         entry->next = point;
     398             : 
     399           0 :         if (point) {
     400           0 :                 if (point->prev)
     401           0 :                         point->prev->next = entry;
     402             :                 else
     403           0 :                         list->head = entry;
     404             : 
     405           0 :                 entry->prev = point->prev;
     406           0 :                 point->prev = entry;
     407             :         } else {
     408           0 :                 if (list->tail)
     409           0 :                         list->tail->next = entry;
     410             :                 else
     411           0 :                         list->head = entry;
     412             : 
     413           0 :                 entry->prev = list->tail;
     414           0 :                 list->tail = entry;
     415             :         }
     416             : }
     417             : 
     418             : /* Lookup community-list entry from the list.  */
     419             : static struct community_entry *
     420           0 : community_list_entry_lookup(struct community_list *list, const void *arg,
     421             :                             int direct)
     422             : {
     423           0 :         struct community_entry *entry;
     424             : 
     425           0 :         for (entry = list->head; entry; entry = entry->next) {
     426           0 :                 switch (entry->style) {
     427           0 :                 case COMMUNITY_LIST_STANDARD:
     428           0 :                         if (entry->direct == direct
     429           0 :                             && community_cmp(entry->u.com, arg))
     430           0 :                                 return entry;
     431             :                         break;
     432           0 :                 case EXTCOMMUNITY_LIST_STANDARD:
     433           0 :                         if (entry->direct == direct
     434           0 :                             && ecommunity_cmp(entry->u.ecom, arg))
     435           0 :                                 return entry;
     436             :                         break;
     437           0 :                 case LARGE_COMMUNITY_LIST_STANDARD:
     438           0 :                         if (entry->direct == direct
     439           0 :                             && lcommunity_cmp(entry->u.lcom, arg))
     440           0 :                                 return entry;
     441             :                         break;
     442           0 :                 case COMMUNITY_LIST_EXPANDED:
     443             :                 case EXTCOMMUNITY_LIST_EXPANDED:
     444             :                 case LARGE_COMMUNITY_LIST_EXPANDED:
     445           0 :                         if (entry->direct == direct
     446           0 :                             && strcmp(entry->config, arg) == 0)
     447           0 :                                 return entry;
     448             :                         break;
     449             :                 default:
     450             :                         break;
     451             :                 }
     452             :         }
     453             :         return NULL;
     454             : }
     455             : 
     456           0 : static char *community_str_get(struct community *com, int i)
     457             : {
     458           0 :         uint32_t comval;
     459           0 :         uint16_t as;
     460           0 :         uint16_t val;
     461           0 :         char *str;
     462             : 
     463           0 :         memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
     464           0 :         comval = ntohl(comval);
     465             : 
     466           0 :         switch (comval) {
     467           0 :         case COMMUNITY_INTERNET:
     468           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet");
     469           0 :                 break;
     470           0 :         case COMMUNITY_GSHUT:
     471           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown");
     472           0 :                 break;
     473           0 :         case COMMUNITY_ACCEPT_OWN:
     474           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own");
     475           0 :                 break;
     476           0 :         case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
     477           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR,
     478             :                               "route-filter-translated-v4");
     479           0 :                 break;
     480           0 :         case COMMUNITY_ROUTE_FILTER_v4:
     481           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v4");
     482           0 :                 break;
     483           0 :         case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
     484           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR,
     485             :                               "route-filter-translated-v6");
     486           0 :                 break;
     487           0 :         case COMMUNITY_ROUTE_FILTER_v6:
     488           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v6");
     489           0 :                 break;
     490           0 :         case COMMUNITY_LLGR_STALE:
     491           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "llgr-stale");
     492           0 :                 break;
     493           0 :         case COMMUNITY_NO_LLGR:
     494           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-llgr");
     495           0 :                 break;
     496           0 :         case COMMUNITY_ACCEPT_OWN_NEXTHOP:
     497           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own-nexthop");
     498           0 :                 break;
     499           0 :         case COMMUNITY_BLACKHOLE:
     500           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "blackhole");
     501           0 :                 break;
     502           0 :         case COMMUNITY_NO_EXPORT:
     503           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-export");
     504           0 :                 break;
     505           0 :         case COMMUNITY_NO_ADVERTISE:
     506           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-advertise");
     507           0 :                 break;
     508           0 :         case COMMUNITY_LOCAL_AS:
     509           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "local-AS");
     510           0 :                 break;
     511           0 :         case COMMUNITY_NO_PEER:
     512           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-peer");
     513           0 :                 break;
     514           0 :         default:
     515           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "65536:65535");
     516           0 :                 as = (comval >> 16) & 0xFFFF;
     517           0 :                 val = comval & 0xFFFF;
     518           0 :                 snprintf(str, strlen(str), "%u:%d", as, val);
     519           0 :                 break;
     520             :         }
     521             : 
     522           0 :         return str;
     523             : }
     524             : 
     525             : /* Internal function to perform regular expression match for
     526             :  * a single community. */
     527           0 : static bool community_regexp_include(regex_t *reg, struct community *com, int i)
     528             : {
     529           0 :         char *str;
     530           0 :         int rv;
     531             : 
     532             :         /* When there is no communities attribute it is treated as empty string.
     533             :          */
     534           0 :         if (com == NULL || com->size == 0)
     535           0 :                 str = XSTRDUP(MTYPE_COMMUNITY_STR, "");
     536             :         else
     537           0 :                 str = community_str_get(com, i);
     538             : 
     539             :         /* Regular expression match.  */
     540           0 :         rv = regexec(reg, str, 0, NULL, 0);
     541             : 
     542           0 :         XFREE(MTYPE_COMMUNITY_STR, str);
     543             : 
     544           0 :         return rv == 0;
     545             : }
     546             : 
     547             : /* Internal function to perform regular expression match for community
     548             :    attribute.  */
     549           0 : static bool community_regexp_match(struct community *com, regex_t *reg)
     550             : {
     551           0 :         const char *str;
     552           0 :         char *regstr;
     553           0 :         int rv;
     554             : 
     555             :         /* When there is no communities attribute it is treated as empty
     556             :            string.  */
     557           0 :         if (com == NULL || com->size == 0)
     558             :                 str = "";
     559             :         else
     560           0 :                 str = community_str(com, false, true);
     561             : 
     562           0 :         regstr = bgp_alias2community_str(str);
     563             : 
     564             :         /* Regular expression match.  */
     565           0 :         rv = regexec(reg, regstr, 0, NULL, 0);
     566             : 
     567           0 :         XFREE(MTYPE_TMP, regstr);
     568             : 
     569           0 :         return rv == 0;
     570             : }
     571             : 
     572           0 : static char *lcommunity_str_get(struct lcommunity *lcom, int i)
     573             : {
     574           0 :         struct lcommunity_val lcomval;
     575           0 :         uint32_t globaladmin;
     576           0 :         uint32_t localdata1;
     577           0 :         uint32_t localdata2;
     578           0 :         char *str;
     579           0 :         const uint8_t *ptr;
     580             : 
     581           0 :         ptr = lcom->val + (i * LCOMMUNITY_SIZE);
     582             : 
     583           0 :         memcpy(&lcomval, ptr, LCOMMUNITY_SIZE);
     584             : 
     585             :         /* Allocate memory.  48 bytes taken off bgp_lcommunity.c */
     586           0 :         ptr = (uint8_t *)lcomval.val;
     587           0 :         ptr = ptr_get_be32(ptr, &globaladmin);
     588           0 :         ptr = ptr_get_be32(ptr, &localdata1);
     589           0 :         ptr = ptr_get_be32(ptr, &localdata2);
     590           0 :         (void)ptr; /* consume value */
     591             : 
     592           0 :         str = XMALLOC(MTYPE_LCOMMUNITY_STR, 48);
     593           0 :         snprintf(str, 48, "%u:%u:%u", globaladmin, localdata1, localdata2);
     594             : 
     595           0 :         return str;
     596             : }
     597             : 
     598             : /* Internal function to perform regular expression match for
     599             :  * a single community. */
     600           0 : static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom,
     601             :                                       int i)
     602             : {
     603           0 :         char *str;
     604             : 
     605             :         /* When there is no communities attribute it is treated as empty string.
     606             :          */
     607           0 :         if (lcom == NULL || lcom->size == 0)
     608           0 :                 str = XSTRDUP(MTYPE_LCOMMUNITY_STR, "");
     609             :         else
     610           0 :                 str = lcommunity_str_get(lcom, i);
     611             : 
     612             :         /* Regular expression match.  */
     613           0 :         if (regexec(reg, str, 0, NULL, 0) == 0) {
     614           0 :                 XFREE(MTYPE_LCOMMUNITY_STR, str);
     615           0 :                 return true;
     616             :         }
     617             : 
     618           0 :         XFREE(MTYPE_LCOMMUNITY_STR, str);
     619             :         /* No match.  */
     620           0 :         return false;
     621             : }
     622             : 
     623           0 : static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
     624             : {
     625           0 :         const char *str;
     626           0 :         char *regstr;
     627           0 :         int rv;
     628             : 
     629             :         /* When there is no communities attribute it is treated as empty
     630             :            string.  */
     631           0 :         if (com == NULL || com->size == 0)
     632             :                 str = "";
     633             :         else
     634           0 :                 str = lcommunity_str(com, false, true);
     635             : 
     636           0 :         regstr = bgp_alias2community_str(str);
     637             : 
     638             :         /* Regular expression match.  */
     639           0 :         rv = regexec(reg, regstr, 0, NULL, 0);
     640             : 
     641           0 :         XFREE(MTYPE_TMP, regstr);
     642             : 
     643           0 :         return rv == 0;
     644             : }
     645             : 
     646             : 
     647           0 : static bool ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg)
     648             : {
     649           0 :         const char *str;
     650             : 
     651             :         /* When there is no communities attribute it is treated as empty
     652             :            string.  */
     653           0 :         if (ecom == NULL || ecom->size == 0)
     654             :                 str = "";
     655             :         else
     656           0 :                 str = ecommunity_str(ecom);
     657             : 
     658             :         /* Regular expression match.  */
     659           0 :         if (regexec(reg, str, 0, NULL, 0) == 0)
     660           0 :                 return true;
     661             : 
     662             :         /* No match.  */
     663             :         return false;
     664             : }
     665             : 
     666             : /* When given community attribute matches to the community-list return
     667             :    1 else return 0.  */
     668           0 : bool community_list_match(struct community *com, struct community_list *list)
     669             : {
     670           0 :         struct community_entry *entry;
     671             : 
     672           0 :         for (entry = list->head; entry; entry = entry->next) {
     673           0 :                 if (entry->any)
     674           0 :                         return entry->direct == COMMUNITY_PERMIT;
     675             : 
     676           0 :                 if (entry->style == COMMUNITY_LIST_STANDARD) {
     677           0 :                         if (community_include(entry->u.com, COMMUNITY_INTERNET))
     678           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     679             : 
     680           0 :                         if (community_match(com, entry->u.com))
     681           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     682           0 :                 } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
     683           0 :                         if (community_regexp_match(com, entry->reg))
     684           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     685             :                 }
     686             :         }
     687             :         return false;
     688             : }
     689             : 
     690           0 : bool lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
     691             : {
     692           0 :         struct community_entry *entry;
     693             : 
     694           0 :         for (entry = list->head; entry; entry = entry->next) {
     695           0 :                 if (entry->any)
     696           0 :                         return entry->direct == COMMUNITY_PERMIT;
     697             : 
     698           0 :                 if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
     699           0 :                         if (lcommunity_match(lcom, entry->u.lcom))
     700           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     701           0 :                 } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
     702           0 :                         if (lcommunity_regexp_match(lcom, entry->reg))
     703           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     704             :                 }
     705             :         }
     706             :         return false;
     707             : }
     708             : 
     709             : 
     710             : /* Perform exact matching.  In case of expanded large-community-list, do
     711             :  * same thing as lcommunity_list_match().
     712             :  */
     713           0 : bool lcommunity_list_exact_match(struct lcommunity *lcom,
     714             :                                  struct community_list *list)
     715             : {
     716           0 :         struct community_entry *entry;
     717             : 
     718           0 :         for (entry = list->head; entry; entry = entry->next) {
     719           0 :                 if (entry->any)
     720           0 :                         return entry->direct == COMMUNITY_PERMIT;
     721             : 
     722           0 :                 if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
     723           0 :                         if (lcommunity_cmp(lcom, entry->u.lcom))
     724           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     725           0 :                 } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
     726           0 :                         if (lcommunity_regexp_match(lcom, entry->reg))
     727           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     728             :                 }
     729             :         }
     730             :         return false;
     731             : }
     732             : 
     733           0 : bool ecommunity_list_match(struct ecommunity *ecom, struct community_list *list)
     734             : {
     735           0 :         struct community_entry *entry;
     736             : 
     737           0 :         for (entry = list->head; entry; entry = entry->next) {
     738           0 :                 if (entry->any)
     739           0 :                         return entry->direct == COMMUNITY_PERMIT;
     740             : 
     741           0 :                 if (entry->style == EXTCOMMUNITY_LIST_STANDARD) {
     742           0 :                         if (ecommunity_match(ecom, entry->u.ecom))
     743           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     744           0 :                 } else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) {
     745           0 :                         if (ecommunity_regexp_match(ecom, entry->reg))
     746           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     747             :                 }
     748             :         }
     749             :         return false;
     750             : }
     751             : 
     752             : /* Perform exact matching.  In case of expanded community-list, do
     753             :    same thing as community_list_match().  */
     754           0 : bool community_list_exact_match(struct community *com,
     755             :                                 struct community_list *list)
     756             : {
     757           0 :         struct community_entry *entry;
     758             : 
     759           0 :         for (entry = list->head; entry; entry = entry->next) {
     760           0 :                 if (entry->any)
     761           0 :                         return entry->direct == COMMUNITY_PERMIT;
     762             : 
     763           0 :                 if (entry->style == COMMUNITY_LIST_STANDARD) {
     764           0 :                         if (community_include(entry->u.com, COMMUNITY_INTERNET))
     765           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     766             : 
     767           0 :                         if (community_cmp(com, entry->u.com))
     768           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     769           0 :                 } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
     770           0 :                         if (community_regexp_match(com, entry->reg))
     771           0 :                                 return entry->direct == COMMUNITY_PERMIT;
     772             :                 }
     773             :         }
     774             :         return false;
     775             : }
     776             : 
     777             : /* Delete all permitted communities in the list from com.  */
     778           0 : struct community *community_list_match_delete(struct community *com,
     779             :                                               struct community_list *list)
     780           0 : {
     781           0 :         struct community_entry *entry;
     782           0 :         uint32_t val;
     783           0 :         uint32_t com_index_to_delete[com->size];
     784           0 :         int delete_index = 0;
     785           0 :         int i;
     786             : 
     787             :         /* Loop over each community value and evaluate each against the
     788             :          * community-list.  If we need to delete a community value add its index
     789             :          * to com_index_to_delete.
     790             :          */
     791           0 :         for (i = 0; i < com->size; i++) {
     792           0 :                 val = community_val_get(com, i);
     793             : 
     794           0 :                 for (entry = list->head; entry; entry = entry->next) {
     795           0 :                         if (entry->any) {
     796           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
     797           0 :                                         com_index_to_delete[delete_index] = i;
     798           0 :                                         delete_index++;
     799             :                                 }
     800             :                                 break;
     801             :                         }
     802             : 
     803           0 :                         else if ((entry->style == COMMUNITY_LIST_STANDARD)
     804           0 :                                  && (community_include(entry->u.com,
     805             :                                                        COMMUNITY_INTERNET)
     806           0 :                                      || community_include(entry->u.com, val))) {
     807           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
     808           0 :                                         com_index_to_delete[delete_index] = i;
     809           0 :                                         delete_index++;
     810             :                                 }
     811             :                                 break;
     812             :                         }
     813             : 
     814           0 :                         else if ((entry->style == COMMUNITY_LIST_EXPANDED)
     815           0 :                                  && community_regexp_include(entry->reg, com,
     816             :                                                              i)) {
     817           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
     818           0 :                                         com_index_to_delete[delete_index] = i;
     819           0 :                                         delete_index++;
     820             :                                 }
     821             :                                 break;
     822             :                         }
     823             :                 }
     824             :         }
     825             : 
     826             :         /* Delete all of the communities we flagged for deletion */
     827           0 :         for (i = delete_index - 1; i >= 0; i--) {
     828           0 :                 val = community_val_get(com, com_index_to_delete[i]);
     829           0 :                 val = htonl(val);
     830           0 :                 community_del_val(com, &val);
     831             :         }
     832             : 
     833           0 :         return com;
     834             : }
     835             : 
     836             : /* To avoid duplicated entry in the community-list, this function
     837             :    compares specified entry to existing entry.  */
     838           0 : static bool community_list_dup_check(struct community_list *list,
     839             :                                      struct community_entry *new)
     840             : {
     841           0 :         struct community_entry *entry;
     842             : 
     843           0 :         for (entry = list->head; entry; entry = entry->next) {
     844           0 :                 if (entry->style != new->style)
     845           0 :                         continue;
     846             : 
     847           0 :                 if (entry->direct != new->direct)
     848           0 :                         continue;
     849             : 
     850           0 :                 if (entry->any != new->any)
     851           0 :                         continue;
     852             : 
     853           0 :                 if (entry->any)
     854             :                         return true;
     855             : 
     856           0 :                 switch (entry->style) {
     857           0 :                 case COMMUNITY_LIST_STANDARD:
     858           0 :                         if (community_cmp(entry->u.com, new->u.com))
     859             :                                 return true;
     860             :                         break;
     861           0 :                 case LARGE_COMMUNITY_LIST_STANDARD:
     862           0 :                         if (lcommunity_cmp(entry->u.lcom, new->u.lcom))
     863             :                                 return true;
     864             :                         break;
     865           0 :                 case EXTCOMMUNITY_LIST_STANDARD:
     866           0 :                         if (ecommunity_cmp(entry->u.ecom, new->u.ecom))
     867             :                                 return true;
     868             :                         break;
     869           0 :                 case COMMUNITY_LIST_EXPANDED:
     870             :                 case EXTCOMMUNITY_LIST_EXPANDED:
     871             :                 case LARGE_COMMUNITY_LIST_EXPANDED:
     872           0 :                         if (strcmp(entry->config, new->config) == 0)
     873             :                                 return true;
     874             :                         break;
     875             :                 default:
     876             :                         break;
     877             :                 }
     878             :         }
     879             :         return false;
     880             : }
     881             : 
     882             : /* Set community-list.  */
     883           0 : int community_list_set(struct community_list_handler *ch, const char *name,
     884             :                        const char *str, const char *seq, int direct, int style)
     885             : {
     886           0 :         struct community_entry *entry = NULL;
     887           0 :         struct community_list *list;
     888           0 :         struct community *com = NULL;
     889           0 :         regex_t *regex = NULL;
     890           0 :         int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
     891             : 
     892           0 :         if (seq)
     893           0 :                 seqnum = (int64_t)atol(seq);
     894             : 
     895             :         /* Get community list. */
     896           0 :         list = community_list_get(ch, name, COMMUNITY_LIST_MASTER);
     897             : 
     898             :         /* When community-list already has entry, new entry should have same
     899             :            style.  If you want to have mixed style community-list, you can
     900             :            comment out this check.  */
     901           0 :         if (!community_list_empty_p(list)) {
     902           0 :                 struct community_entry *first;
     903             : 
     904           0 :                 first = list->head;
     905             : 
     906           0 :                 if (style != first->style) {
     907           0 :                         return (first->style == COMMUNITY_LIST_STANDARD
     908             :                                         ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
     909           0 :                                         : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
     910             :                 }
     911             :         }
     912             : 
     913           0 :         if (str) {
     914           0 :                 if (style == COMMUNITY_LIST_STANDARD)
     915           0 :                         com = community_str2com(str);
     916             :                 else
     917           0 :                         regex = bgp_regcomp(str);
     918             : 
     919           0 :                 if (!com && !regex)
     920             :                         return COMMUNITY_LIST_ERR_MALFORMED_VAL;
     921             :         }
     922             : 
     923           0 :         entry = community_entry_new();
     924           0 :         entry->direct = direct;
     925           0 :         entry->style = style;
     926           0 :         entry->any = (str ? false : true);
     927           0 :         entry->u.com = com;
     928           0 :         entry->reg = regex;
     929           0 :         entry->seq = seqnum;
     930           0 :         entry->config =
     931           0 :                 (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
     932             : 
     933             :         /* Do not put duplicated community entry.  */
     934           0 :         if (community_list_dup_check(list, entry))
     935           0 :                 community_entry_free(entry);
     936             :         else {
     937           0 :                 community_list_entry_add(list, entry, ch,
     938             :                                          COMMUNITY_LIST_MASTER);
     939           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED);
     940             :         }
     941             : 
     942             :         return 0;
     943             : }
     944             : 
     945             : /* Unset community-list */
     946           0 : int community_list_unset(struct community_list_handler *ch, const char *name,
     947             :                          const char *str, const char *seq, int direct,
     948             :                          int style)
     949             : {
     950           0 :         struct community_list_master *cm = NULL;
     951           0 :         struct community_entry *entry = NULL;
     952           0 :         struct community_list *list;
     953           0 :         struct community *com = NULL;
     954             : 
     955             :         /* Lookup community list.  */
     956           0 :         list = community_list_lookup(ch, name, 0, COMMUNITY_LIST_MASTER);
     957           0 :         if (list == NULL)
     958             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
     959             : 
     960           0 :         cm = community_list_master_lookup(ch, COMMUNITY_LIST_MASTER);
     961             :         /* Delete all of entry belongs to this community-list.  */
     962           0 :         if (!str) {
     963           0 :                 community_list_delete(cm, list);
     964           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
     965           0 :                 return 0;
     966             :         }
     967             : 
     968           0 :         if (style == COMMUNITY_LIST_STANDARD)
     969           0 :                 com = community_str2com(str);
     970             : 
     971           0 :         if (com) {
     972           0 :                 entry = community_list_entry_lookup(list, com, direct);
     973           0 :                 community_free(&com);
     974             :         } else
     975           0 :                 entry = community_list_entry_lookup(list, str, direct);
     976             : 
     977           0 :         if (!entry)
     978             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
     979             : 
     980           0 :         community_list_entry_delete(cm, list, entry);
     981           0 :         route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
     982             : 
     983           0 :         return 0;
     984             : }
     985             : 
     986             : /* Delete all permitted large communities in the list from com.  */
     987           0 : struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
     988             :                                                 struct community_list *list)
     989           0 : {
     990           0 :         struct community_entry *entry;
     991           0 :         uint32_t com_index_to_delete[lcom->size];
     992           0 :         uint8_t *ptr;
     993           0 :         int delete_index = 0;
     994           0 :         int i;
     995             : 
     996             :         /* Loop over each lcommunity value and evaluate each against the
     997             :          * community-list.  If we need to delete a community value add its index
     998             :          * to com_index_to_delete.
     999             :          */
    1000           0 :         for (i = 0; i < lcom->size; i++) {
    1001           0 :                 ptr = lcom->val + (i * LCOMMUNITY_SIZE);
    1002           0 :                 for (entry = list->head; entry; entry = entry->next) {
    1003           0 :                         if (entry->any) {
    1004           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
    1005           0 :                                         com_index_to_delete[delete_index] = i;
    1006           0 :                                         delete_index++;
    1007             :                                 }
    1008             :                                 break;
    1009             :                         }
    1010             : 
    1011           0 :                         else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
    1012           0 :                                  && lcommunity_include(entry->u.lcom, ptr)) {
    1013           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
    1014           0 :                                         com_index_to_delete[delete_index] = i;
    1015           0 :                                         delete_index++;
    1016             :                                 }
    1017             :                                 break;
    1018             :                         }
    1019             : 
    1020           0 :                         else if ((entry->style == LARGE_COMMUNITY_LIST_EXPANDED)
    1021           0 :                                  && lcommunity_regexp_include(entry->reg, lcom,
    1022             :                                                               i)) {
    1023           0 :                                 if (entry->direct == COMMUNITY_PERMIT) {
    1024           0 :                                         com_index_to_delete[delete_index] = i;
    1025           0 :                                         delete_index++;
    1026             :                                 }
    1027             :                                 break;
    1028             :                         }
    1029             :                 }
    1030             :         }
    1031             : 
    1032             :         /* Delete all of the communities we flagged for deletion */
    1033           0 :         for (i = delete_index - 1; i >= 0; i--) {
    1034           0 :                 ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE);
    1035           0 :                 lcommunity_del_val(lcom, ptr);
    1036             :         }
    1037             : 
    1038           0 :         return lcom;
    1039             : }
    1040             : 
    1041             : /* Helper to check if every octet do not exceed UINT_MAX */
    1042           0 : bool lcommunity_list_valid(const char *community, int style)
    1043             : {
    1044           0 :         int octets;
    1045           0 :         char **splits, **communities;
    1046           0 :         char *endptr;
    1047           0 :         int num, num_communities;
    1048           0 :         regex_t *regres;
    1049           0 :         int invalid = 0;
    1050             : 
    1051           0 :         frrstr_split(community, " ", &communities, &num_communities);
    1052             : 
    1053           0 :         for (int j = 0; j < num_communities; j++) {
    1054           0 :                 octets = 0;
    1055           0 :                 frrstr_split(communities[j], ":", &splits, &num);
    1056             : 
    1057           0 :                 for (int i = 0; i < num; i++) {
    1058           0 :                         if (strlen(splits[i]) == 0)
    1059             :                                 /* There is no digit to check */
    1060           0 :                                 invalid++;
    1061             : 
    1062           0 :                         if (style == LARGE_COMMUNITY_LIST_STANDARD) {
    1063           0 :                                 if (*splits[i] == '-')
    1064             :                                         /* Must not be negative */
    1065           0 :                                         invalid++;
    1066           0 :                                 else if (strtoul(splits[i], &endptr, 10)
    1067             :                                          > UINT_MAX)
    1068             :                                         /* Larger than 4 octets */
    1069           0 :                                         invalid++;
    1070           0 :                                 else if (*endptr)
    1071             :                                         /* Not all characters were digits */
    1072           0 :                                         invalid++;
    1073             :                         } else {
    1074           0 :                                 regres = bgp_regcomp(communities[j]);
    1075           0 :                                 if (!regres)
    1076             :                                         /* malformed regex */
    1077           0 :                                         invalid++;
    1078             :                                 else
    1079           0 :                                         bgp_regex_free(regres);
    1080             :                         }
    1081             : 
    1082           0 :                         octets++;
    1083           0 :                         XFREE(MTYPE_TMP, splits[i]);
    1084             :                 }
    1085           0 :                 XFREE(MTYPE_TMP, splits);
    1086             : 
    1087           0 :                 if (octets != 3)
    1088           0 :                         invalid++;
    1089             : 
    1090           0 :                 XFREE(MTYPE_TMP, communities[j]);
    1091             :         }
    1092           0 :         XFREE(MTYPE_TMP, communities);
    1093             : 
    1094           0 :         return (invalid > 0) ? false : true;
    1095             : }
    1096             : 
    1097             : /* Set lcommunity-list.  */
    1098           0 : int lcommunity_list_set(struct community_list_handler *ch, const char *name,
    1099             :                         const char *str, const char *seq, int direct, int style)
    1100             : {
    1101           0 :         struct community_entry *entry = NULL;
    1102           0 :         struct community_list *list;
    1103           0 :         struct lcommunity *lcom = NULL;
    1104           0 :         regex_t *regex = NULL;
    1105           0 :         int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
    1106             : 
    1107           0 :         if (seq)
    1108           0 :                 seqnum = (int64_t)atol(seq);
    1109             : 
    1110             :         /* Get community list. */
    1111           0 :         list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER);
    1112             : 
    1113             :         /* When community-list already has entry, new entry should have same
    1114             :            style.  If you want to have mixed style community-list, you can
    1115             :            comment out this check.  */
    1116           0 :         if (!community_list_empty_p(list)) {
    1117           0 :                 struct community_entry *first;
    1118             : 
    1119           0 :                 first = list->head;
    1120             : 
    1121           0 :                 if (style != first->style) {
    1122           0 :                         return (first->style == COMMUNITY_LIST_STANDARD
    1123             :                                         ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
    1124           0 :                                         : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
    1125             :                 }
    1126             :         }
    1127             : 
    1128           0 :         if (str) {
    1129           0 :                 if (style == LARGE_COMMUNITY_LIST_STANDARD)
    1130           0 :                         lcom = lcommunity_str2com(str);
    1131             :                 else
    1132           0 :                         regex = bgp_regcomp(str);
    1133             : 
    1134           0 :                 if (!lcom && !regex)
    1135             :                         return COMMUNITY_LIST_ERR_MALFORMED_VAL;
    1136             :         }
    1137             : 
    1138           0 :         entry = community_entry_new();
    1139           0 :         entry->direct = direct;
    1140           0 :         entry->style = style;
    1141           0 :         entry->any = (str ? false : true);
    1142           0 :         entry->u.lcom = lcom;
    1143           0 :         entry->reg = regex;
    1144           0 :         entry->seq = seqnum;
    1145           0 :         entry->config =
    1146           0 :                 (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
    1147             : 
    1148             :         /* Do not put duplicated community entry.  */
    1149           0 :         if (community_list_dup_check(list, entry))
    1150           0 :                 community_entry_free(entry);
    1151             :         else {
    1152           0 :                 community_list_entry_add(list, entry, ch,
    1153             :                                          LARGE_COMMUNITY_LIST_MASTER);
    1154           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
    1155             :         }
    1156             : 
    1157             :         return 0;
    1158             : }
    1159             : 
    1160             : /* Unset community-list.  When str is NULL, delete all of
    1161             :    community-list entry belongs to the specified name.  */
    1162           0 : int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
    1163             :                           const char *str, const char *seq, int direct,
    1164             :                           int style)
    1165             : {
    1166           0 :         struct community_list_master *cm = NULL;
    1167           0 :         struct community_entry *entry = NULL;
    1168           0 :         struct community_list *list;
    1169           0 :         struct lcommunity *lcom = NULL;
    1170           0 :         regex_t *regex = NULL;
    1171             : 
    1172             :         /* Lookup community list.  */
    1173           0 :         list = community_list_lookup(ch, name, 0, LARGE_COMMUNITY_LIST_MASTER);
    1174           0 :         if (list == NULL)
    1175             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
    1176             : 
    1177           0 :         cm = community_list_master_lookup(ch, LARGE_COMMUNITY_LIST_MASTER);
    1178             :         /* Delete all of entry belongs to this community-list.  */
    1179           0 :         if (!str) {
    1180           0 :                 community_list_delete(cm, list);
    1181           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
    1182           0 :                 return 0;
    1183             :         }
    1184             : 
    1185           0 :         if (style == LARGE_COMMUNITY_LIST_STANDARD)
    1186           0 :                 lcom = lcommunity_str2com(str);
    1187             :         else
    1188           0 :                 regex = bgp_regcomp(str);
    1189             : 
    1190           0 :         if (!lcom && !regex)
    1191             :                 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
    1192             : 
    1193           0 :         if (lcom)
    1194           0 :                 entry = community_list_entry_lookup(list, lcom, direct);
    1195             :         else
    1196           0 :                 entry = community_list_entry_lookup(list, str, direct);
    1197             : 
    1198           0 :         if (lcom)
    1199           0 :                 lcommunity_free(&lcom);
    1200           0 :         if (regex)
    1201           0 :                 bgp_regex_free(regex);
    1202             : 
    1203           0 :         if (!entry)
    1204             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
    1205             : 
    1206           0 :         community_list_entry_delete(cm, list, entry);
    1207           0 :         route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
    1208             : 
    1209           0 :         return 0;
    1210             : }
    1211             : 
    1212             : /* Set extcommunity-list.  */
    1213           0 : int extcommunity_list_set(struct community_list_handler *ch, const char *name,
    1214             :                           const char *str, const char *seq, int direct,
    1215             :                           int style)
    1216             : {
    1217           0 :         struct community_entry *entry = NULL;
    1218           0 :         struct community_list *list;
    1219           0 :         struct ecommunity *ecom = NULL;
    1220           0 :         regex_t *regex = NULL;
    1221           0 :         int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
    1222             : 
    1223           0 :         if (seq)
    1224           0 :                 seqnum = (int64_t)atol(seq);
    1225             : 
    1226           0 :         if (str == NULL)
    1227             :                 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
    1228             : 
    1229             :         /* Get community list. */
    1230           0 :         list = community_list_get(ch, name, EXTCOMMUNITY_LIST_MASTER);
    1231             : 
    1232             :         /* When community-list already has entry, new entry should have same
    1233             :            style.  If you want to have mixed style community-list, you can
    1234             :            comment out this check.  */
    1235           0 :         if (!community_list_empty_p(list)) {
    1236           0 :                 struct community_entry *first;
    1237             : 
    1238           0 :                 first = list->head;
    1239             : 
    1240           0 :                 if (style != first->style) {
    1241           0 :                         return (first->style == EXTCOMMUNITY_LIST_STANDARD
    1242             :                                         ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
    1243           0 :                                         : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
    1244             :                 }
    1245             :         }
    1246             : 
    1247           0 :         if (style == EXTCOMMUNITY_LIST_STANDARD)
    1248           0 :                 ecom = ecommunity_str2com(str, 0, 1);
    1249             :         else
    1250           0 :                 regex = bgp_regcomp(str);
    1251             : 
    1252           0 :         if (!ecom && !regex)
    1253             :                 return COMMUNITY_LIST_ERR_MALFORMED_VAL;
    1254             : 
    1255           0 :         if (ecom)
    1256           0 :                 ecom->str =
    1257           0 :                         ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
    1258             : 
    1259           0 :         entry = community_entry_new();
    1260           0 :         entry->direct = direct;
    1261           0 :         entry->style = style;
    1262           0 :         entry->any = false;
    1263           0 :         if (ecom)
    1264           0 :                 entry->config = ecommunity_ecom2str(
    1265             :                         ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
    1266           0 :         else if (regex)
    1267           0 :                 entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
    1268             : 
    1269           0 :         entry->u.ecom = ecom;
    1270           0 :         entry->reg = regex;
    1271           0 :         entry->seq = seqnum;
    1272             : 
    1273             :         /* Do not put duplicated community entry.  */
    1274           0 :         if (community_list_dup_check(list, entry))
    1275           0 :                 community_entry_free(entry);
    1276             :         else {
    1277           0 :                 community_list_entry_add(list, entry, ch,
    1278             :                                          EXTCOMMUNITY_LIST_MASTER);
    1279           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED);
    1280             :         }
    1281             : 
    1282             :         return 0;
    1283             : }
    1284             : 
    1285             : /* Unset extcommunity-list.
    1286             :  *
    1287             :  * When str is NULL, delete all extcommunity-list entries belonging to the
    1288             :  * specified name.
    1289             :  */
    1290           0 : int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
    1291             :                             const char *str, const char *seq, int direct,
    1292             :                             int style)
    1293             : {
    1294           0 :         struct community_list_master *cm = NULL;
    1295           0 :         struct community_entry *entry = NULL;
    1296           0 :         struct community_list *list;
    1297           0 :         struct ecommunity *ecom = NULL;
    1298             : 
    1299             :         /* Lookup extcommunity list.  */
    1300           0 :         list = community_list_lookup(ch, name, 0, EXTCOMMUNITY_LIST_MASTER);
    1301           0 :         if (list == NULL)
    1302             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
    1303             : 
    1304           0 :         cm = community_list_master_lookup(ch, EXTCOMMUNITY_LIST_MASTER);
    1305             :         /* Delete all of entry belongs to this extcommunity-list.  */
    1306           0 :         if (!str) {
    1307           0 :                 community_list_delete(cm, list);
    1308           0 :                 route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
    1309           0 :                 return 0;
    1310             :         }
    1311             : 
    1312           0 :         if (style == EXTCOMMUNITY_LIST_STANDARD)
    1313           0 :                 ecom = ecommunity_str2com(str, 0, 1);
    1314             : 
    1315           0 :         if (ecom) {
    1316           0 :                 entry = community_list_entry_lookup(list, ecom, direct);
    1317           0 :                 ecommunity_free(&ecom);
    1318             :         } else
    1319           0 :                 entry = community_list_entry_lookup(list, str, direct);
    1320             : 
    1321           0 :         if (!entry)
    1322             :                 return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
    1323             : 
    1324           0 :         community_list_entry_delete(cm, list, entry);
    1325           0 :         route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
    1326             : 
    1327           0 :         return 0;
    1328             : }
    1329             : 
    1330             : /* Initializa community-list.  Return community-list handler.  */
    1331           3 : struct community_list_handler *community_list_init(void)
    1332             : {
    1333           3 :         struct community_list_handler *ch;
    1334           3 :         ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
    1335             :                      sizeof(struct community_list_handler));
    1336             : 
    1337           6 :         ch->community_list.hash =
    1338           3 :                 hash_create_size(4, bgp_clist_hash_key_community_list,
    1339             :                                  bgp_clist_hash_cmp_community_list,
    1340             :                                  "Community List Number Quick Lookup");
    1341             : 
    1342           6 :         ch->extcommunity_list.hash =
    1343           3 :                 hash_create_size(4, bgp_clist_hash_key_community_list,
    1344             :                                  bgp_clist_hash_cmp_community_list,
    1345             :                                  "Extended Community List Quick Lookup");
    1346             : 
    1347           6 :         ch->lcommunity_list.hash =
    1348           3 :                 hash_create_size(4, bgp_clist_hash_key_community_list,
    1349             :                                  bgp_clist_hash_cmp_community_list,
    1350             :                                  "Large Community List Quick Lookup");
    1351             : 
    1352           3 :         return ch;
    1353             : }
    1354             : 
    1355             : /* Terminate community-list.  */
    1356           3 : void community_list_terminate(struct community_list_handler *ch)
    1357             : {
    1358           3 :         struct community_list_master *cm;
    1359           3 :         struct community_list *list;
    1360             : 
    1361           3 :         cm = &ch->community_list;
    1362           3 :         while ((list = cm->num.head) != NULL)
    1363           0 :                 community_list_delete(cm, list);
    1364           3 :         while ((list = cm->str.head) != NULL)
    1365           0 :                 community_list_delete(cm, list);
    1366           3 :         hash_free(cm->hash);
    1367             : 
    1368           3 :         cm = &ch->lcommunity_list;
    1369           3 :         while ((list = cm->num.head) != NULL)
    1370           0 :                 community_list_delete(cm, list);
    1371           3 :         while ((list = cm->str.head) != NULL)
    1372           0 :                 community_list_delete(cm, list);
    1373           3 :         hash_free(cm->hash);
    1374             : 
    1375           3 :         cm = &ch->extcommunity_list;
    1376           3 :         while ((list = cm->num.head) != NULL)
    1377           0 :                 community_list_delete(cm, list);
    1378           3 :         while ((list = cm->str.head) != NULL)
    1379           0 :                 community_list_delete(cm, list);
    1380           3 :         hash_free(cm->hash);
    1381             : 
    1382           3 :         XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
    1383           3 : }
    1384             : 
    1385           0 : static int bgp_community_list_vector_walker(struct hash_bucket *bucket,
    1386             :                                             void *data)
    1387             : {
    1388           0 :         vector *comps = data;
    1389           0 :         struct community_list *list = bucket->data;
    1390             : 
    1391           0 :         vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, list->name));
    1392             : 
    1393           0 :         return 1;
    1394             : }
    1395             : 
    1396           0 : static void bgp_community_list_cmd_completion(vector comps,
    1397             :                                               struct cmd_token *token)
    1398             : {
    1399           0 :         struct community_list_master *cm;
    1400             : 
    1401           0 :         cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER);
    1402             : 
    1403           0 :         hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
    1404           0 : }
    1405             : 
    1406           0 : static void bgp_lcommunity_list_cmd_completion(vector comps,
    1407             :                                                struct cmd_token *token)
    1408             : {
    1409           0 :         struct community_list_master *cm;
    1410             : 
    1411           0 :         cm = community_list_master_lookup(bgp_clist,
    1412             :                                           LARGE_COMMUNITY_LIST_MASTER);
    1413             : 
    1414           0 :         hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
    1415           0 : }
    1416             : 
    1417           0 : static void bgp_extcommunity_list_cmd_completion(vector comps,
    1418             :                                                  struct cmd_token *token)
    1419             : {
    1420           0 :         struct community_list_master *cm;
    1421             : 
    1422           0 :         cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER);
    1423             : 
    1424           0 :         hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
    1425           0 : }
    1426             : 
    1427             : static const struct cmd_variable_handler community_list_handlers[] = {
    1428             :         {.tokenname = "COMMUNITY_LIST_NAME",
    1429             :          .completions = bgp_community_list_cmd_completion},
    1430             :         {.completions = NULL}};
    1431             : 
    1432             : static const struct cmd_variable_handler lcommunity_list_handlers[] = {
    1433             :         {.tokenname = "LCOMMUNITY_LIST_NAME",
    1434             :          .completions = bgp_lcommunity_list_cmd_completion},
    1435             :         {.completions = NULL}};
    1436             : 
    1437             : static const struct cmd_variable_handler extcommunity_list_handlers[] = {
    1438             :         {.tokenname = "EXTCOMMUNITY_LIST_NAME",
    1439             :          .completions = bgp_extcommunity_list_cmd_completion},
    1440             :         {.completions = NULL}};
    1441             : 
    1442           3 : void bgp_community_list_command_completion_setup(void)
    1443             : {
    1444           3 :         cmd_variable_handler_register(community_list_handlers);
    1445           3 :         cmd_variable_handler_register(lcommunity_list_handlers);
    1446           3 :         cmd_variable_handler_register(extcommunity_list_handlers);
    1447           3 : }

Generated by: LCOV version v1.16-topotato