back to topotato report
topotato coverage report
Current view: top level - bgpd - bgp_community.c (source / functions) Hit Total Coverage
Test: test_exabgp_demo.py::ExaBGPDemo Lines: 12 554 2.2 %
Date: 2023-02-24 18:37:55 Functions: 3 34 8.8 %

          Line data    Source code
       1             : /* Community attribute related functions.
       2             :  * Copyright (C) 1998, 2001 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 "hash.h"
      25             : #include "memory.h"
      26             : #include "jhash.h"
      27             : #include "frrstr.h"
      28             : 
      29             : #include "bgpd/bgp_memory.h"
      30             : #include "bgpd/bgp_community.h"
      31             : #include "bgpd/bgp_community_alias.h"
      32             : 
      33             : /* Hash of community attribute. */
      34             : static struct hash *comhash;
      35             : 
      36             : /* Allocate a new communities value.  */
      37           0 : static struct community *community_new(void)
      38             : {
      39           0 :         return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
      40             : }
      41             : 
      42             : /* Free communities value.  */
      43           0 : void community_free(struct community **com)
      44             : {
      45           0 :         if (!(*com))
      46             :                 return;
      47             : 
      48           0 :         XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
      49           0 :         XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
      50             : 
      51           0 :         if ((*com)->json) {
      52           0 :                 json_object_free((*com)->json);
      53           0 :                 (*com)->json = NULL;
      54             :         }
      55             : 
      56           0 :         XFREE(MTYPE_COMMUNITY, (*com));
      57             : }
      58             : 
      59             : /* Add one community value to the community. */
      60           0 : void community_add_val(struct community *com, uint32_t val)
      61             : {
      62           0 :         com->size++;
      63           0 :         com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
      64             : 
      65           0 :         val = htonl(val);
      66           0 :         memcpy(com_lastval(com), &val, sizeof(uint32_t));
      67           0 : }
      68             : 
      69             : /* Delete one community. */
      70           0 : void community_del_val(struct community *com, uint32_t *val)
      71             : {
      72           0 :         int i = 0;
      73           0 :         int c = 0;
      74             : 
      75           0 :         if (!com->val)
      76             :                 return;
      77             : 
      78           0 :         while (i < com->size) {
      79           0 :                 if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
      80           0 :                         c = com->size - i - 1;
      81             : 
      82           0 :                         if (c > 0)
      83           0 :                                 memmove(com->val + i, com->val + (i + 1),
      84             :                                         c * sizeof(*val));
      85             : 
      86           0 :                         com->size--;
      87             : 
      88           0 :                         if (com->size > 0)
      89           0 :                                 com->val = XREALLOC(MTYPE_COMMUNITY_VAL,
      90             :                                                     com->val, com_length(com));
      91             :                         else {
      92           0 :                                 XFREE(MTYPE_COMMUNITY_VAL, com->val);
      93             :                         }
      94           0 :                         return;
      95             :                 }
      96           0 :                 i++;
      97             :         }
      98             : }
      99             : 
     100             : /* Delete all communities listed in com2 from com1 */
     101           0 : struct community *community_delete(struct community *com1,
     102             :                                    struct community *com2)
     103             : {
     104           0 :         int i = 0;
     105             : 
     106           0 :         while (i < com2->size) {
     107           0 :                 community_del_val(com1, com2->val + i);
     108           0 :                 i++;
     109             :         }
     110             : 
     111           0 :         return com1;
     112             : }
     113             : 
     114             : /* Callback function from qsort(). */
     115           0 : static int community_compare(const void *a1, const void *a2)
     116             : {
     117           0 :         uint32_t v1;
     118           0 :         uint32_t v2;
     119             : 
     120           0 :         memcpy(&v1, a1, sizeof(uint32_t));
     121           0 :         memcpy(&v2, a2, sizeof(uint32_t));
     122           0 :         v1 = ntohl(v1);
     123           0 :         v2 = ntohl(v2);
     124             : 
     125           0 :         if (v1 < v2)
     126             :                 return -1;
     127           0 :         if (v1 > v2)
     128           0 :                 return 1;
     129             :         return 0;
     130             : }
     131             : 
     132           0 : bool community_include(struct community *com, uint32_t val)
     133             : {
     134           0 :         int i;
     135             : 
     136           0 :         val = htonl(val);
     137             : 
     138           0 :         for (i = 0; i < com->size; i++)
     139           0 :                 if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
     140             :                         return true;
     141             :         return false;
     142             : }
     143             : 
     144           0 : uint32_t community_val_get(struct community *com, int i)
     145             : {
     146           0 :         uint8_t *p;
     147           0 :         uint32_t val;
     148             : 
     149           0 :         p = (uint8_t *)com->val;
     150           0 :         p += (i * COMMUNITY_SIZE);
     151             : 
     152           0 :         memcpy(&val, p, sizeof(uint32_t));
     153             : 
     154           0 :         return ntohl(val);
     155             : }
     156             : 
     157             : /* Sort and uniq given community. */
     158           0 : struct community *community_uniq_sort(struct community *com)
     159             : {
     160           0 :         int i;
     161           0 :         struct community *new;
     162           0 :         uint32_t val;
     163             : 
     164           0 :         if (!com)
     165             :                 return NULL;
     166             : 
     167           0 :         new = community_new();
     168           0 :         new->json = NULL;
     169             : 
     170           0 :         for (i = 0; i < com->size; i++) {
     171           0 :                 val = community_val_get(com, i);
     172             : 
     173           0 :                 if (!community_include(new, val))
     174           0 :                         community_add_val(new, val);
     175             :         }
     176             : 
     177           0 :         qsort(new->val, new->size, sizeof(uint32_t), community_compare);
     178             : 
     179           0 :         return new;
     180             : }
     181             : 
     182             : /* Convert communities attribute to string.
     183             : 
     184             :    For Well-known communities value, below keyword is used.
     185             : 
     186             :    0x0             "internet"
     187             :    0xFFFF0000      "graceful-shutdown"
     188             :    0xFFFF0001      "accept-own"
     189             :    0xFFFF0002      "route-filter-translated-v4"
     190             :    0xFFFF0003      "route-filter-v4"
     191             :    0xFFFF0004      "route-filter-translated-v6"
     192             :    0xFFFF0005      "route-filter-v6"
     193             :    0xFFFF0006      "llgr-stale"
     194             :    0xFFFF0007      "no-llgr"
     195             :    0xFFFF0008      "accept-own-nexthop"
     196             :    0xFFFF029A      "blackhole"
     197             :    0xFFFFFF01      "no-export"
     198             :    0xFFFFFF02      "no-advertise"
     199             :    0xFFFFFF03      "local-AS"
     200             :    0xFFFFFF04      "no-peer"
     201             : 
     202             :    For other values, "AS:VAL" format is used.  */
     203           0 : static void set_community_string(struct community *com, bool make_json,
     204             :                                  bool translate_alias)
     205             : {
     206           0 :         int i;
     207           0 :         char *str;
     208           0 :         int len;
     209           0 :         int first;
     210           0 :         uint32_t comval;
     211           0 :         uint16_t as;
     212           0 :         uint16_t val;
     213           0 :         json_object *json_community_list = NULL;
     214           0 :         json_object *json_string = NULL;
     215             : 
     216           0 :         if (!com)
     217             :                 return;
     218             : 
     219           0 :         if (make_json) {
     220           0 :                 com->json = json_object_new_object();
     221           0 :                 json_community_list = json_object_new_array();
     222             :         }
     223             : 
     224             :         /* When communities attribute is empty.  */
     225           0 :         if (com->size == 0) {
     226           0 :                 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
     227           0 :                 str[0] = '\0';
     228             : 
     229           0 :                 if (make_json) {
     230           0 :                         json_object_string_add(com->json, "string", "");
     231           0 :                         json_object_object_add(com->json, "list",
     232             :                                                json_community_list);
     233             :                 }
     234           0 :                 com->str = str;
     235           0 :                 return;
     236             :         }
     237             : 
     238             :         /* Memory allocation is time consuming work.  So we calculate
     239             :            required string length first.  */
     240             :         len = 0;
     241             : 
     242           0 :         for (i = 0; i < com->size; i++) {
     243           0 :                 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
     244           0 :                 comval = ntohl(comval);
     245             : 
     246           0 :                 switch (comval) {
     247           0 :                 case COMMUNITY_INTERNET:
     248           0 :                         len += strlen(" internet");
     249           0 :                         break;
     250           0 :                 case COMMUNITY_GSHUT:
     251           0 :                         len += strlen(" graceful-shutdown");
     252           0 :                         break;
     253           0 :                 case COMMUNITY_ACCEPT_OWN:
     254           0 :                         len += strlen(" accept-own");
     255           0 :                         break;
     256           0 :                 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
     257           0 :                         len += strlen(" route-filter-translated-v4");
     258           0 :                         break;
     259           0 :                 case COMMUNITY_ROUTE_FILTER_v4:
     260           0 :                         len += strlen(" route-filter-v4");
     261           0 :                         break;
     262           0 :                 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
     263           0 :                         len += strlen(" route-filter-translated-v6");
     264           0 :                         break;
     265           0 :                 case COMMUNITY_ROUTE_FILTER_v6:
     266           0 :                         len += strlen(" route-filter-v6");
     267           0 :                         break;
     268           0 :                 case COMMUNITY_LLGR_STALE:
     269           0 :                         len += strlen(" llgr-stale");
     270           0 :                         break;
     271           0 :                 case COMMUNITY_NO_LLGR:
     272           0 :                         len += strlen(" no-llgr");
     273           0 :                         break;
     274           0 :                 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
     275           0 :                         len += strlen(" accept-own-nexthop");
     276           0 :                         break;
     277           0 :                 case COMMUNITY_BLACKHOLE:
     278           0 :                         len += strlen(" blackhole");
     279           0 :                         break;
     280           0 :                 case COMMUNITY_NO_EXPORT:
     281           0 :                         len += strlen(" no-export");
     282           0 :                         break;
     283           0 :                 case COMMUNITY_NO_ADVERTISE:
     284           0 :                         len += strlen(" no-advertise");
     285           0 :                         break;
     286           0 :                 case COMMUNITY_LOCAL_AS:
     287           0 :                         len += strlen(" local-AS");
     288           0 :                         break;
     289           0 :                 case COMMUNITY_NO_PEER:
     290           0 :                         len += strlen(" no-peer");
     291           0 :                         break;
     292             :                 default:
     293             :                         len = BUFSIZ;
     294             :                         break;
     295             :                 }
     296             :         }
     297             : 
     298             :         /* Allocate memory.  */
     299           0 :         str = XCALLOC(MTYPE_COMMUNITY_STR, len);
     300           0 :         first = 1;
     301             : 
     302             :         /* Fill in string.  */
     303           0 :         for (i = 0; i < com->size; i++) {
     304           0 :                 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
     305           0 :                 comval = ntohl(comval);
     306             : 
     307           0 :                 if (first)
     308             :                         first = 0;
     309             :                 else
     310           0 :                         strlcat(str, " ", len);
     311             : 
     312           0 :                 switch (comval) {
     313           0 :                 case COMMUNITY_INTERNET:
     314           0 :                         strlcat(str, "internet", len);
     315           0 :                         if (make_json) {
     316           0 :                                 json_string =
     317           0 :                                         json_object_new_string("internet");
     318           0 :                                 json_object_array_add(json_community_list,
     319             :                                                       json_string);
     320             :                         }
     321             :                         break;
     322           0 :                 case COMMUNITY_GSHUT:
     323           0 :                         strlcat(str, "graceful-shutdown", len);
     324           0 :                         if (make_json) {
     325           0 :                                 json_string = json_object_new_string(
     326             :                                         "gracefulShutdown");
     327           0 :                                 json_object_array_add(json_community_list,
     328             :                                                       json_string);
     329             :                         }
     330             :                         break;
     331           0 :                 case COMMUNITY_ACCEPT_OWN:
     332           0 :                         strlcat(str, "accept-own", len);
     333           0 :                         if (make_json) {
     334           0 :                                 json_string = json_object_new_string(
     335             :                                         "acceptown");
     336           0 :                                 json_object_array_add(json_community_list,
     337             :                                                       json_string);
     338             :                         }
     339             :                         break;
     340           0 :                 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
     341           0 :                         strlcat(str, "route-filter-translated-v4", len);
     342           0 :                         if (make_json) {
     343           0 :                                 json_string = json_object_new_string(
     344             :                                         "routeFilterTranslatedV4");
     345           0 :                                 json_object_array_add(json_community_list,
     346             :                                                       json_string);
     347             :                         }
     348             :                         break;
     349           0 :                 case COMMUNITY_ROUTE_FILTER_v4:
     350           0 :                         strlcat(str, "route-filter-v4", len);
     351           0 :                         if (make_json) {
     352           0 :                                 json_string = json_object_new_string(
     353             :                                         "routeFilterV4");
     354           0 :                                 json_object_array_add(json_community_list,
     355             :                                                       json_string);
     356             :                         }
     357             :                         break;
     358           0 :                 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
     359           0 :                         strlcat(str, "route-filter-translated-v6", len);
     360           0 :                         if (make_json) {
     361           0 :                                 json_string = json_object_new_string(
     362             :                                         "routeFilterTranslatedV6");
     363           0 :                                 json_object_array_add(json_community_list,
     364             :                                                       json_string);
     365             :                         }
     366             :                         break;
     367           0 :                 case COMMUNITY_ROUTE_FILTER_v6:
     368           0 :                         strlcat(str, "route-filter-v6", len);
     369           0 :                         if (make_json) {
     370           0 :                                 json_string = json_object_new_string(
     371             :                                         "routeFilterV6");
     372           0 :                                 json_object_array_add(json_community_list,
     373             :                                                       json_string);
     374             :                         }
     375             :                         break;
     376           0 :                 case COMMUNITY_LLGR_STALE:
     377           0 :                         strlcat(str, "llgr-stale", len);
     378           0 :                         if (make_json) {
     379           0 :                                 json_string = json_object_new_string(
     380             :                                         "llgrStale");
     381           0 :                                 json_object_array_add(json_community_list,
     382             :                                                       json_string);
     383             :                         }
     384             :                         break;
     385           0 :                 case COMMUNITY_NO_LLGR:
     386           0 :                         strlcat(str, "no-llgr", len);
     387           0 :                         if (make_json) {
     388           0 :                                 json_string = json_object_new_string(
     389             :                                         "noLlgr");
     390           0 :                                 json_object_array_add(json_community_list,
     391             :                                                       json_string);
     392             :                         }
     393             :                         break;
     394           0 :                 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
     395           0 :                         strlcat(str, "accept-own-nexthop", len);
     396           0 :                         if (make_json) {
     397           0 :                                 json_string = json_object_new_string(
     398             :                                         "acceptownnexthop");
     399           0 :                                 json_object_array_add(json_community_list,
     400             :                                                       json_string);
     401             :                         }
     402             :                         break;
     403           0 :                 case COMMUNITY_BLACKHOLE:
     404           0 :                         strlcat(str, "blackhole", len);
     405           0 :                         if (make_json) {
     406           0 :                                 json_string = json_object_new_string(
     407             :                                         "blackhole");
     408           0 :                                 json_object_array_add(json_community_list,
     409             :                                                       json_string);
     410             :                         }
     411             :                         break;
     412           0 :                 case COMMUNITY_NO_EXPORT:
     413           0 :                         strlcat(str, "no-export", len);
     414           0 :                         if (make_json) {
     415           0 :                                 json_string =
     416           0 :                                         json_object_new_string("noExport");
     417           0 :                                 json_object_array_add(json_community_list,
     418             :                                                       json_string);
     419             :                         }
     420             :                         break;
     421           0 :                 case COMMUNITY_NO_ADVERTISE:
     422           0 :                         strlcat(str, "no-advertise", len);
     423           0 :                         if (make_json) {
     424           0 :                                 json_string =
     425           0 :                                         json_object_new_string("noAdvertise");
     426           0 :                                 json_object_array_add(json_community_list,
     427             :                                                       json_string);
     428             :                         }
     429             :                         break;
     430           0 :                 case COMMUNITY_LOCAL_AS:
     431           0 :                         strlcat(str, "local-AS", len);
     432           0 :                         if (make_json) {
     433           0 :                                 json_string = json_object_new_string("localAs");
     434           0 :                                 json_object_array_add(json_community_list,
     435             :                                                       json_string);
     436             :                         }
     437             :                         break;
     438           0 :                 case COMMUNITY_NO_PEER:
     439           0 :                         strlcat(str, "no-peer", len);
     440           0 :                         if (make_json) {
     441           0 :                                 json_string = json_object_new_string("noPeer");
     442           0 :                                 json_object_array_add(json_community_list,
     443             :                                                       json_string);
     444             :                         }
     445             :                         break;
     446           0 :                 default:
     447           0 :                         as = (comval >> 16) & 0xFFFF;
     448           0 :                         val = comval & 0xFFFF;
     449           0 :                         char buf[32];
     450           0 :                         snprintf(buf, sizeof(buf), "%u:%d", as, val);
     451           0 :                         const char *com2alias =
     452           0 :                                 translate_alias ? bgp_community2alias(buf)
     453           0 :                                                 : buf;
     454             : 
     455           0 :                         strlcat(str, com2alias, len);
     456           0 :                         if (make_json) {
     457           0 :                                 json_string = json_object_new_string(com2alias);
     458           0 :                                 json_object_array_add(json_community_list,
     459             :                                                       json_string);
     460             :                         }
     461             :                         break;
     462             :                 }
     463             :         }
     464             : 
     465           0 :         if (make_json) {
     466           0 :                 json_object_string_add(com->json, "string", str);
     467           0 :                 json_object_object_add(com->json, "list", json_community_list);
     468             :         }
     469           0 :         com->str = str;
     470             : }
     471             : 
     472             : /* Intern communities attribute.  */
     473           0 : struct community *community_intern(struct community *com)
     474             : {
     475           0 :         struct community *find;
     476             : 
     477             :         /* Assert this community structure is not interned. */
     478           0 :         assert(com->refcnt == 0);
     479             : 
     480             :         /* Lookup community hash. */
     481           0 :         find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
     482             : 
     483             :         /* Arguemnt com is allocated temporary.  So when it is not used in
     484             :            hash, it should be freed.  */
     485           0 :         if (find != com)
     486           0 :                 community_free(&com);
     487             : 
     488             :         /* Increment refrence counter.  */
     489           0 :         find->refcnt++;
     490             : 
     491             :         /* Make string.  */
     492           0 :         if (!find->str)
     493           0 :                 set_community_string(find, false, true);
     494             : 
     495           0 :         return find;
     496             : }
     497             : 
     498             : /* Free community attribute. */
     499           4 : void community_unintern(struct community **com)
     500             : {
     501           4 :         struct community *ret;
     502             : 
     503           4 :         if (!*com)
     504             :                 return;
     505             : 
     506           0 :         if ((*com)->refcnt)
     507           0 :                 (*com)->refcnt--;
     508             : 
     509             :         /* Pull off from hash.  */
     510           0 :         if ((*com)->refcnt == 0) {
     511             :                 /* Community value com must exist in hash. */
     512           0 :                 ret = (struct community *)hash_release(comhash, *com);
     513           0 :                 assert(ret != NULL);
     514             : 
     515           0 :                 community_free(com);
     516             :         }
     517             : }
     518             : 
     519             : /* Create new community attribute. */
     520           0 : struct community *community_parse(uint32_t *pnt, unsigned short length)
     521             : {
     522           0 :         struct community tmp;
     523           0 :         struct community *new;
     524             : 
     525             :         /* If length is malformed return NULL. */
     526           0 :         if (length % COMMUNITY_SIZE)
     527             :                 return NULL;
     528             : 
     529             :         /* Make temporary community for hash look up. */
     530           0 :         tmp.size = length / COMMUNITY_SIZE;
     531           0 :         tmp.val = pnt;
     532             : 
     533           0 :         new = community_uniq_sort(&tmp);
     534             : 
     535           0 :         return community_intern(new);
     536             : }
     537             : 
     538           0 : struct community *community_dup(struct community *com)
     539             : {
     540           0 :         struct community *new;
     541             : 
     542           0 :         new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
     543           0 :         new->size = com->size;
     544           0 :         if (new->size) {
     545           0 :                 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
     546             :                                    com->size * COMMUNITY_SIZE);
     547           0 :                 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
     548             :         } else
     549           0 :                 new->val = NULL;
     550           0 :         return new;
     551             : }
     552             : 
     553             : /* Return string representation of communities attribute. */
     554           0 : char *community_str(struct community *com, bool make_json, bool translate_alias)
     555             : {
     556           0 :         if (!com)
     557             :                 return NULL;
     558             : 
     559           0 :         if (make_json && !com->json && com->str)
     560           0 :                 XFREE(MTYPE_COMMUNITY_STR, com->str);
     561             : 
     562           0 :         if (!com->str)
     563           0 :                 set_community_string(com, make_json, translate_alias);
     564           0 :         return com->str;
     565             : }
     566             : 
     567             : /* Make hash value of community attribute. This function is used by
     568             :    hash package.*/
     569           0 : unsigned int community_hash_make(const struct community *com)
     570             : {
     571           0 :         uint32_t *pnt = com->val;
     572             : 
     573           0 :         return jhash2(pnt, com->size, 0x43ea96c1);
     574             : }
     575             : 
     576           0 : bool community_match(const struct community *com1, const struct community *com2)
     577             : {
     578           0 :         int i = 0;
     579           0 :         int j = 0;
     580             : 
     581           0 :         if (com1 == NULL && com2 == NULL)
     582             :                 return true;
     583             : 
     584           0 :         if (com1 == NULL || com2 == NULL)
     585             :                 return false;
     586             : 
     587           0 :         if (com1->size < com2->size)
     588             :                 return false;
     589             : 
     590             :         /* Every community on com2 needs to be on com1 for this to match */
     591           0 :         while (i < com1->size && j < com2->size) {
     592           0 :                 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
     593           0 :                         j++;
     594           0 :                 i++;
     595             :         }
     596             : 
     597           0 :         if (j == com2->size)
     598             :                 return true;
     599             :         else
     600           0 :                 return false;
     601             : }
     602             : 
     603           0 : bool community_cmp(const struct community *com1, const struct community *com2)
     604             : {
     605           0 :         if (com1 == NULL && com2 == NULL)
     606             :                 return true;
     607           0 :         if (com1 == NULL || com2 == NULL)
     608             :                 return false;
     609             : 
     610           0 :         if (com1->size == com2->size)
     611           0 :                 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
     612             :                     == 0)
     613           0 :                         return true;
     614             :         return false;
     615             : }
     616             : 
     617             : /* Add com2 to the end of com1. */
     618           0 : struct community *community_merge(struct community *com1,
     619             :                                   struct community *com2)
     620             : {
     621           0 :         com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
     622             :                              (com1->size + com2->size) * COMMUNITY_SIZE);
     623             : 
     624           0 :         memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
     625           0 :         com1->size += com2->size;
     626             : 
     627           0 :         return com1;
     628             : }
     629             : 
     630             : /* Community token enum. */
     631             : enum community_token {
     632             :         community_token_val,
     633             :         community_token_gshut,
     634             :         community_token_accept_own,
     635             :         community_token_route_filter_translated_v4,
     636             :         community_token_route_filter_v4,
     637             :         community_token_route_filter_translated_v6,
     638             :         community_token_route_filter_v6,
     639             :         community_token_llgr_stale,
     640             :         community_token_no_llgr,
     641             :         community_token_accept_own_nexthop,
     642             :         community_token_blackhole,
     643             :         community_token_no_export,
     644             :         community_token_no_advertise,
     645             :         community_token_local_as,
     646             :         community_token_no_peer,
     647             :         community_token_unknown
     648             : };
     649             : 
     650             : /* Helper to check if a given community is valid */
     651           0 : static bool community_valid(const char *community)
     652             : {
     653           0 :         int octets = 0;
     654           0 :         char **splits;
     655           0 :         int num;
     656           0 :         int invalid = 0;
     657             : 
     658           0 :         frrstr_split(community, ":", &splits, &num);
     659             : 
     660           0 :         for (int i = 0; i < num; i++) {
     661           0 :                 if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
     662           0 :                         invalid++;
     663             : 
     664           0 :                 if (strlen(splits[i]) == 0)
     665           0 :                         invalid++;
     666             : 
     667           0 :                 octets++;
     668           0 :                 XFREE(MTYPE_TMP, splits[i]);
     669             :         }
     670           0 :         XFREE(MTYPE_TMP, splits);
     671             : 
     672           0 :         return (octets < 2 || invalid) ? false : true;
     673             : }
     674             : 
     675             : /* Get next community token from string. */
     676             : static const char *
     677           0 : community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
     678             : {
     679           0 :         const char *p = buf;
     680             : 
     681             :         /* Skip white space. */
     682           0 :         while (isspace((unsigned char)*p))
     683           0 :                 p++;
     684             : 
     685             :         /* Check the end of the line. */
     686           0 :         if (*p == '\0')
     687             :                 return NULL;
     688             : 
     689             :         /* Well known community string check. */
     690           0 :         if (isalpha((unsigned char)*p)) {
     691           0 :                 if (strncmp(p, "internet", strlen("internet")) == 0) {
     692           0 :                         *val = COMMUNITY_INTERNET;
     693           0 :                         *token = community_token_no_export;
     694           0 :                         p += strlen("internet");
     695           0 :                         return p;
     696             :                 }
     697           0 :                 if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
     698             :                     == 0) {
     699           0 :                         *val = COMMUNITY_GSHUT;
     700           0 :                         *token = community_token_gshut;
     701           0 :                         p += strlen("graceful-shutdown");
     702           0 :                         return p;
     703             :                 }
     704           0 :                 if (strncmp(p, "accept-own-nexthop",
     705             :                             strlen("accept-own-nexthop"))
     706             :                     == 0) {
     707           0 :                         *val = COMMUNITY_ACCEPT_OWN_NEXTHOP;
     708           0 :                         *token = community_token_accept_own_nexthop;
     709           0 :                         p += strlen("accept-own-nexthop");
     710           0 :                         return p;
     711             :                 }
     712           0 :                 if (strncmp(p, "accept-own", strlen("accept-own"))
     713             :                     == 0) {
     714           0 :                         *val = COMMUNITY_ACCEPT_OWN;
     715           0 :                         *token = community_token_accept_own;
     716           0 :                         p += strlen("accept-own");
     717           0 :                         return p;
     718             :                 }
     719           0 :                 if (strncmp(p, "route-filter-translated-v4",
     720             :                         strlen("route-filter-translated-v4"))
     721             :                     == 0) {
     722           0 :                         *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v4;
     723           0 :                         *token = community_token_route_filter_translated_v4;
     724           0 :                         p += strlen("route-filter-translated-v4");
     725           0 :                         return p;
     726             :                 }
     727           0 :                 if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
     728             :                     == 0) {
     729           0 :                         *val = COMMUNITY_ROUTE_FILTER_v4;
     730           0 :                         *token = community_token_route_filter_v4;
     731           0 :                         p += strlen("route-filter-v4");
     732           0 :                         return p;
     733             :                 }
     734           0 :                 if (strncmp(p, "route-filter-translated-v6",
     735             :                         strlen("route-filter-translated-v6"))
     736             :                     == 0) {
     737           0 :                         *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v6;
     738           0 :                         *token = community_token_route_filter_translated_v6;
     739           0 :                         p += strlen("route-filter-translated-v6");
     740           0 :                         return p;
     741             :                 }
     742           0 :                 if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
     743             :                     == 0) {
     744           0 :                         *val = COMMUNITY_ROUTE_FILTER_v6;
     745           0 :                         *token = community_token_route_filter_v6;
     746           0 :                         p += strlen("route-filter-v6");
     747           0 :                         return p;
     748             :                 }
     749           0 :                 if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
     750             :                     == 0) {
     751           0 :                         *val = COMMUNITY_LLGR_STALE;
     752           0 :                         *token = community_token_llgr_stale;
     753           0 :                         p += strlen("llgr-stale");
     754           0 :                         return p;
     755             :                 }
     756           0 :                 if (strncmp(p, "no-llgr", strlen("no-llgr"))
     757             :                     == 0) {
     758           0 :                         *val = COMMUNITY_NO_LLGR;
     759           0 :                         *token = community_token_no_llgr;
     760           0 :                         p += strlen("no-llgr");
     761           0 :                         return p;
     762             :                 }
     763           0 :                 if (strncmp(p, "blackhole", strlen("blackhole"))
     764             :                     == 0) {
     765           0 :                         *val = COMMUNITY_BLACKHOLE;
     766           0 :                         *token = community_token_blackhole;
     767           0 :                         p += strlen("blackhole");
     768           0 :                         return p;
     769             :                 }
     770           0 :                 if (strncmp(p, "no-export", strlen("no-export")) == 0) {
     771           0 :                         *val = COMMUNITY_NO_EXPORT;
     772           0 :                         *token = community_token_no_export;
     773           0 :                         p += strlen("no-export");
     774           0 :                         return p;
     775             :                 }
     776           0 :                 if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
     777           0 :                         *val = COMMUNITY_NO_ADVERTISE;
     778           0 :                         *token = community_token_no_advertise;
     779           0 :                         p += strlen("no-advertise");
     780           0 :                         return p;
     781             :                 }
     782           0 :                 if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
     783           0 :                         *val = COMMUNITY_LOCAL_AS;
     784           0 :                         *token = community_token_local_as;
     785           0 :                         p += strlen("local-AS");
     786           0 :                         return p;
     787             :                 }
     788           0 :                 if (strncmp(p, "no-peer", strlen("no-peer")) == 0) {
     789           0 :                         *val = COMMUNITY_NO_PEER;
     790           0 :                         *token = community_token_no_peer;
     791           0 :                         p += strlen("no-peer");
     792           0 :                         return p;
     793             :                 }
     794             : 
     795             :                 /* Unknown string. */
     796           0 :                 *token = community_token_unknown;
     797           0 :                 return NULL;
     798             :         }
     799             : 
     800             :         /* Community value. */
     801           0 :         if (isdigit((unsigned char)*p)) {
     802           0 :                 int separator = 0;
     803           0 :                 int digit = 0;
     804           0 :                 uint32_t community_low = 0;
     805           0 :                 uint32_t community_high = 0;
     806             : 
     807           0 :                 if (!community_valid(p)) {
     808           0 :                         *token = community_token_unknown;
     809           0 :                         return NULL;
     810             :                 }
     811             : 
     812           0 :                 while (isdigit((unsigned char)*p) || *p == ':') {
     813           0 :                         if (*p == ':') {
     814           0 :                                 if (separator) {
     815           0 :                                         *token = community_token_unknown;
     816           0 :                                         return NULL;
     817             :                                 } else {
     818           0 :                                         separator = 1;
     819           0 :                                         digit = 0;
     820             : 
     821           0 :                                         if (community_low > UINT16_MAX) {
     822           0 :                                                 *token =
     823             :                                                         community_token_unknown;
     824           0 :                                                 return NULL;
     825             :                                         }
     826             : 
     827           0 :                                         community_high = community_low << 16;
     828           0 :                                         community_low = 0;
     829             :                                 }
     830             :                         } else {
     831           0 :                                 digit = 1;
     832           0 :                                 community_low *= 10;
     833           0 :                                 community_low += (*p - '0');
     834             :                         }
     835           0 :                         p++;
     836             :                 }
     837           0 :                 if (!digit) {
     838           0 :                         *token = community_token_unknown;
     839           0 :                         return NULL;
     840             :                 }
     841             : 
     842           0 :                 *val = community_high + community_low;
     843           0 :                 *token = community_token_val;
     844           0 :                 return p;
     845             :         }
     846           0 :         *token = community_token_unknown;
     847           0 :         return NULL;
     848             : }
     849             : 
     850             : /* convert string to community structure */
     851           0 : struct community *community_str2com(const char *str)
     852             : {
     853           0 :         struct community *com = NULL;
     854           0 :         struct community *com_sort = NULL;
     855           0 :         uint32_t val = 0;
     856           0 :         enum community_token token = community_token_unknown;
     857             : 
     858           0 :         do {
     859           0 :                 str = community_gettoken(str, &token, &val);
     860             : 
     861           0 :                 switch (token) {
     862           0 :                 case community_token_val:
     863             :                 case community_token_gshut:
     864             :                 case community_token_accept_own:
     865             :                 case community_token_route_filter_translated_v4:
     866             :                 case community_token_route_filter_v4:
     867             :                 case community_token_route_filter_translated_v6:
     868             :                 case community_token_route_filter_v6:
     869             :                 case community_token_llgr_stale:
     870             :                 case community_token_no_llgr:
     871             :                 case community_token_accept_own_nexthop:
     872             :                 case community_token_blackhole:
     873             :                 case community_token_no_export:
     874             :                 case community_token_no_advertise:
     875             :                 case community_token_local_as:
     876             :                 case community_token_no_peer:
     877           0 :                         if (com == NULL) {
     878           0 :                                 com = community_new();
     879           0 :                                 com->json = NULL;
     880             :                         }
     881           0 :                         community_add_val(com, val);
     882           0 :                         break;
     883           0 :                 case community_token_unknown:
     884           0 :                         if (com)
     885           0 :                                 community_free(&com);
     886             :                         return NULL;
     887             :                 }
     888           0 :         } while (str);
     889             : 
     890           0 :         com_sort = community_uniq_sort(com);
     891           0 :         community_free(&com);
     892             : 
     893           0 :         return com_sort;
     894             : }
     895             : 
     896             : /* Return communities hash entry count.  */
     897           0 : unsigned long community_count(void)
     898             : {
     899           0 :         return comhash->count;
     900             : }
     901             : 
     902             : /* Return communities hash.  */
     903           0 : struct hash *community_hash(void)
     904             : {
     905           0 :         return comhash;
     906             : }
     907             : 
     908             : /* Initialize comminity related hash. */
     909           3 : void community_init(void)
     910             : {
     911           6 :         comhash =
     912           3 :                 hash_create((unsigned int (*)(const void *))community_hash_make,
     913             :                             (bool (*)(const void *, const void *))community_cmp,
     914             :                             "BGP Community Hash");
     915           3 : }
     916             : 
     917           0 : static void community_hash_free(void *data)
     918             : {
     919           0 :         struct community *com = data;
     920             : 
     921           0 :         community_free(&com);
     922           0 : }
     923             : 
     924           3 : void community_finish(void)
     925             : {
     926           3 :         hash_clean(comhash, community_hash_free);
     927           3 :         hash_free(comhash);
     928           3 :         comhash = NULL;
     929           3 : }
     930             : 
     931           0 : static struct community *bgp_aggr_community_lookup(
     932             :                                                 struct bgp_aggregate *aggregate,
     933             :                                                 struct community *community)
     934             : {
     935           0 :         return hash_lookup(aggregate->community_hash, community);
     936             : }
     937             : 
     938           0 : static void *bgp_aggr_community_hash_alloc(void *p)
     939             : {
     940           0 :         struct community *ref = (struct community *)p;
     941           0 :         struct community *community = NULL;
     942             : 
     943           0 :         community = community_dup(ref);
     944           0 :         return community;
     945             : }
     946             : 
     947           0 : static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
     948             : {
     949           0 :         struct community *hb_community = hb->data;
     950           0 :         struct community **aggr_community = arg;
     951             : 
     952           0 :         if (*aggr_community)
     953           0 :                 *aggr_community = community_merge(*aggr_community,
     954             :                                                   hb_community);
     955             :         else
     956           0 :                 *aggr_community = community_dup(hb_community);
     957           0 : }
     958             : 
     959           0 : void bgp_aggr_community_remove(void *arg)
     960             : {
     961           0 :         struct community *community = arg;
     962             : 
     963           0 :         community_free(&community);
     964           0 : }
     965             : 
     966           0 : void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
     967             :                                      struct community *community)
     968             : {
     969           0 :         bgp_compute_aggregate_community_hash(aggregate, community);
     970           0 :         bgp_compute_aggregate_community_val(aggregate);
     971           0 : }
     972             : 
     973             : 
     974           0 : void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
     975             :                                           struct community *community)
     976             : {
     977           0 :         struct community *aggr_community = NULL;
     978             : 
     979           0 :         if ((aggregate == NULL) || (community == NULL))
     980             :                 return;
     981             : 
     982             :         /* Create hash if not already created.
     983             :          */
     984           0 :         if (aggregate->community_hash == NULL)
     985           0 :                 aggregate->community_hash = hash_create(
     986             :                         (unsigned int (*)(const void *))community_hash_make,
     987             :                         (bool (*)(const void *, const void *))community_cmp,
     988             :                         "BGP Aggregator community hash");
     989             : 
     990           0 :         aggr_community = bgp_aggr_community_lookup(aggregate, community);
     991           0 :         if (aggr_community == NULL) {
     992             :                 /* Insert community into hash.
     993             :                  */
     994           0 :                 aggr_community = hash_get(aggregate->community_hash, community,
     995             :                                           bgp_aggr_community_hash_alloc);
     996             :         }
     997             : 
     998             :         /* Increment reference counter.
     999             :          */
    1000           0 :         aggr_community->refcnt++;
    1001             : }
    1002             : 
    1003           0 : void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
    1004             : {
    1005           0 :         struct community *commerge = NULL;
    1006             : 
    1007           0 :         if (aggregate == NULL)
    1008           0 :                 return;
    1009             : 
    1010             :         /* Re-compute aggregate's community.
    1011             :          */
    1012           0 :         if (aggregate->community)
    1013           0 :                 community_free(&aggregate->community);
    1014           0 :         if (aggregate->community_hash &&
    1015           0 :             aggregate->community_hash->count) {
    1016           0 :                 hash_iterate(aggregate->community_hash,
    1017             :                              bgp_aggr_community_prepare,
    1018           0 :                              &aggregate->community);
    1019           0 :                 commerge = aggregate->community;
    1020           0 :                 aggregate->community = community_uniq_sort(commerge);
    1021           0 :                 if (commerge)
    1022           0 :                         community_free(&commerge);
    1023             :         }
    1024             : }
    1025             : 
    1026             : 
    1027             : 
    1028           0 : void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
    1029             :                                          struct community *community)
    1030             : {
    1031           0 :         struct community *aggr_community = NULL;
    1032           0 :         struct community *ret_comm = NULL;
    1033             : 
    1034           0 :         if ((!aggregate)
    1035           0 :             || (!aggregate->community_hash)
    1036           0 :             || (!community))
    1037           0 :                 return;
    1038             : 
    1039             :         /* Look-up the community in the hash.
    1040             :          */
    1041           0 :         aggr_community = bgp_aggr_community_lookup(aggregate, community);
    1042           0 :         if (aggr_community) {
    1043           0 :                 aggr_community->refcnt--;
    1044             : 
    1045           0 :                 if (aggr_community->refcnt == 0) {
    1046           0 :                         ret_comm = hash_release(aggregate->community_hash,
    1047             :                                                 aggr_community);
    1048           0 :                         community_free(&ret_comm);
    1049             : 
    1050           0 :                         bgp_compute_aggregate_community_val(aggregate);
    1051             :                 }
    1052             :         }
    1053             : }
    1054             : 
    1055           0 : void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
    1056             :                 struct community *community)
    1057             : {
    1058             : 
    1059           0 :         struct community *aggr_community = NULL;
    1060           0 :         struct community *ret_comm = NULL;
    1061             : 
    1062           0 :         if ((!aggregate)
    1063           0 :             || (!aggregate->community_hash)
    1064           0 :             || (!community))
    1065           0 :                 return;
    1066             : 
    1067             :         /* Look-up the community in the hash.
    1068             :          */
    1069           0 :         aggr_community = bgp_aggr_community_lookup(aggregate, community);
    1070           0 :         if (aggr_community) {
    1071           0 :                 aggr_community->refcnt--;
    1072             : 
    1073           0 :                 if (aggr_community->refcnt == 0) {
    1074           0 :                         ret_comm = hash_release(aggregate->community_hash,
    1075             :                                                 aggr_community);
    1076           0 :                         community_free(&ret_comm);
    1077             :                 }
    1078             :         }
    1079             : }

Generated by: LCOV version v1.16-topotato