Coverage Report

Created: 2025-11-09 06:11

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