Coverage Report

Created: 2026-05-25 06:55

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.24k
{
24
1.24k
  return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
25
1.24k
}
26
27
/* Free communities value.  */
28
void community_free(struct community **com)
29
1.65k
{
30
1.65k
  if (!(*com))
31
0
    return;
32
33
1.65k
  XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
34
1.65k
  XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
35
36
1.65k
  if ((*com)->json) {
37
0
    json_object_free((*com)->json);
38
0
    (*com)->json = NULL;
39
0
  }
40
41
1.65k
  XFREE(MTYPE_COMMUNITY, (*com));
42
1.65k
}
43
44
/* Add one community value to the community. */
45
void community_add_val(struct community *com, uint32_t val)
46
6.27k
{
47
6.27k
  com->size++;
48
6.27k
  com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
49
50
6.27k
  val = htonl(val);
51
6.27k
  memcpy(com_lastval(com), &val, sizeof(uint32_t));
52
6.27k
}
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
18.3k
{
102
18.3k
  uint32_t v1;
103
18.3k
  uint32_t v2;
104
105
18.3k
  memcpy(&v1, a1, sizeof(uint32_t));
106
18.3k
  memcpy(&v2, a2, sizeof(uint32_t));
107
18.3k
  v1 = ntohl(v1);
108
18.3k
  v2 = ntohl(v2);
109
110
18.3k
  if (v1 < v2)
111
10.5k
    return -1;
112
7.81k
  if (v1 > v2)
113
7.81k
    return 1;
114
0
  return 0;
115
7.81k
}
116
117
bool community_include(struct community *com, uint32_t val)
118
9.03k
{
119
9.03k
  int i;
120
121
9.03k
  val = htonl(val);
122
123
366k
  for (i = 0; i < com->size; i++)
124
359k
    if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
125
1.86k
      return true;
126
7.16k
  return false;
127
9.03k
}
128
129
uint32_t community_val_get(struct community *com, int i)
130
6.73k
{
131
6.73k
  uint8_t *p;
132
6.73k
  uint32_t val;
133
134
6.73k
  p = (uint8_t *)com->val;
135
6.73k
  p += (i * COMMUNITY_SIZE);
136
137
6.73k
  memcpy(&val, p, sizeof(uint32_t));
138
139
6.73k
  return ntohl(val);
140
6.73k
}
141
142
/* Sort and uniq given community. */
143
struct community *community_uniq_sort(struct community *com)
144
842
{
145
842
  int i;
146
842
  struct community *new;
147
842
  uint32_t val;
148
149
842
  if (!com)
150
0
    return NULL;
151
152
842
  new = community_new();
153
842
  new->json = NULL;
154
155
7.57k
  for (i = 0; i < com->size; i++) {
156
6.73k
    val = community_val_get(com, i);
157
158
6.73k
    if (!community_include(new, val))
159
5.46k
      community_add_val(new, val);
160
6.73k
  }
161
162
842
  qsort(new->val, new->size, sizeof(uint32_t), community_compare);
163
164
842
  return new;
165
842
}
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
34
{
190
34
  int i;
191
34
  char *str;
192
34
  int len;
193
34
  int first;
194
34
  uint32_t comval;
195
34
  uint16_t as;
196
34
  uint16_t val;
197
34
  json_object *json_community_list = NULL;
198
34
  json_object *json_string = NULL;
199
200
34
  if (!com)
201
0
    return;
202
203
34
  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
34
  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
34
  len = 0;
225
226
2.45k
  for (i = 0; i < com->size; i++) {
227
2.42k
    memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
228
2.42k
    comval = ntohl(comval);
229
230
2.42k
    switch (comval) {
231
#if CONFDATE > 20230801
232
CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
233
#endif
234
17
    case COMMUNITY_INTERNET:
235
17
      len += strlen(" internet");
236
17
      zlog_warn("`internet` community is deprecated");
237
17
      break;
238
4
    case COMMUNITY_GSHUT:
239
4
      len += strlen(" graceful-shutdown");
240
4
      break;
241
3
    case COMMUNITY_ACCEPT_OWN:
242
3
      len += strlen(" accept-own");
243
3
      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
0
    case COMMUNITY_ROUTE_FILTER_v6:
254
0
      len += strlen(" route-filter-v6");
255
0
      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
2
    case COMMUNITY_ACCEPT_OWN_NEXTHOP:
263
2
      len += strlen(" accept-own-nexthop");
264
2
      break;
265
5
    case COMMUNITY_BLACKHOLE:
266
5
      len += strlen(" blackhole");
267
5
      break;
268
3
    case COMMUNITY_NO_EXPORT:
269
3
      len += strlen(" no-export");
270
3
      break;
271
1
    case COMMUNITY_NO_ADVERTISE:
272
1
      len += strlen(" no-advertise");
273
1
      break;
274
0
    case COMMUNITY_LOCAL_AS:
275
0
      len += strlen(" local-AS");
276
0
      break;
277
3
    case COMMUNITY_NO_PEER:
278
3
      len += strlen(" no-peer");
279
3
      break;
280
2.38k
    default:
281
2.38k
      len = BUFSIZ;
282
2.38k
      break;
283
2.42k
    }
284
2.42k
  }
285
286
  /* Allocate memory.  */
287
34
  str = XCALLOC(MTYPE_COMMUNITY_STR, len);
288
34
  first = 1;
289
290
  /* Fill in string.  */
291
2.45k
  for (i = 0; i < com->size; i++) {
292
2.42k
    memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
293
2.42k
    comval = ntohl(comval);
294
295
2.42k
    if (first)
296
34
      first = 0;
297
2.39k
    else
298
2.39k
      strlcat(str, " ", len);
299
300
2.42k
    switch (comval) {
301
#if CONFDATE > 20230801
302
CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
303
#endif
304
17
    case COMMUNITY_INTERNET:
305
17
      strlcat(str, "internet", len);
306
17
      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
17
      zlog_warn("`internet` community is deprecated");
313
17
      break;
314
4
    case COMMUNITY_GSHUT:
315
4
      strlcat(str, "graceful-shutdown", len);
316
4
      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
4
      break;
323
3
    case COMMUNITY_ACCEPT_OWN:
324
3
      strlcat(str, "accept-own", len);
325
3
      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
3
      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
0
    case COMMUNITY_ROUTE_FILTER_v6:
360
0
      strlcat(str, "route-filter-v6", len);
361
0
      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
0
      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
2
    case COMMUNITY_ACCEPT_OWN_NEXTHOP:
387
2
      strlcat(str, "accept-own-nexthop", len);
388
2
      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
2
      break;
395
5
    case COMMUNITY_BLACKHOLE:
396
5
      strlcat(str, "blackhole", len);
397
5
      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
5
      break;
404
3
    case COMMUNITY_NO_EXPORT:
405
3
      strlcat(str, "no-export", len);
406
3
      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
3
      break;
413
1
    case COMMUNITY_NO_ADVERTISE:
414
1
      strlcat(str, "no-advertise", len);
415
1
      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
1
      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
3
    case COMMUNITY_NO_PEER:
431
3
      strlcat(str, "no-peer", len);
432
3
      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
3
      break;
438
2.38k
    default:
439
2.38k
      as = (comval >> 16) & 0xFFFF;
440
2.38k
      val = comval & 0xFFFF;
441
2.38k
      char buf[32];
442
2.38k
      snprintf(buf, sizeof(buf), "%u:%d", as, val);
443
2.38k
      const char *com2alias =
444
2.38k
        translate_alias ? bgp_community2alias(buf)
445
2.38k
            : buf;
446
447
2.38k
      strlcat(str, com2alias, len);
448
2.38k
      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
2.38k
      break;
454
2.42k
    }
455
2.42k
  }
456
457
34
  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
34
  com->str = str;
462
34
}
463
464
/* Intern communities attribute.  */
465
struct community *community_intern(struct community *com)
466
34
{
467
34
  struct community *find;
468
469
  /* Assert this community structure is not interned. */
470
34
  assert(com->refcnt == 0);
471
472
  /* Lookup community hash. */
473
34
  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
34
  if (find != com)
478
0
    community_free(&com);
479
480
  /* Increment refrence counter.  */
481
34
  find->refcnt++;
482
483
  /* Make string.  */
484
34
  if (!find->str)
485
34
    set_community_string(find, false, true);
486
487
34
  return find;
488
34
}
489
490
/* Free community attribute. */
491
void community_unintern(struct community **com)
492
497
{
493
497
  struct community *ret;
494
495
497
  if (!*com)
496
463
    return;
497
498
34
  if ((*com)->refcnt)
499
34
    (*com)->refcnt--;
500
501
  /* Pull off from hash.  */
502
34
  if ((*com)->refcnt == 0) {
503
    /* Community value com must exist in hash. */
504
34
    ret = (struct community *)hash_release(comhash, *com);
505
34
    assert(ret != NULL);
506
507
34
    community_free(com);
508
34
  }
509
34
}
510
511
/* Create new community attribute. */
512
struct community *community_parse(uint32_t *pnt, unsigned short length)
513
34
{
514
34
  struct community tmp;
515
34
  struct community *new;
516
517
  /* If length is malformed return NULL. */
518
34
  if (length % COMMUNITY_SIZE)
519
0
    return NULL;
520
521
  /* Make temporary community for hash look up. */
522
34
  tmp.size = length / COMMUNITY_SIZE;
523
34
  tmp.val = pnt;
524
525
34
  new = community_uniq_sort(&tmp);
526
527
34
  return community_intern(new);
528
34
}
529
530
struct community *community_dup(struct community *com)
531
404
{
532
404
  struct community *new;
533
534
404
  new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
535
404
  new->size = com->size;
536
404
  if (new->size) {
537
404
    new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
538
404
           com->size * COMMUNITY_SIZE);
539
404
    memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
540
404
  } else
541
0
    new->val = NULL;
542
404
  return new;
543
404
}
544
545
/* Return string representation of communities attribute. */
546
char *community_str(struct community *com, bool make_json, bool translate_alias)
547
8
{
548
8
  if (!com)
549
0
    return NULL;
550
551
8
  if (make_json && !com->json && com->str)
552
0
    XFREE(MTYPE_COMMUNITY_STR, com->str);
553
554
8
  if (!com->str)
555
0
    set_community_string(com, make_json, translate_alias);
556
8
  return com->str;
557
8
}
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
68
{
563
68
  uint32_t *pnt = com->val;
564
565
68
  return jhash2(pnt, com->size, 0x43ea96c1);
566
68
}
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
34
{
597
34
  if (com1 == NULL && com2 == NULL)
598
0
    return true;
599
34
  if (com1 == NULL || com2 == NULL)
600
0
    return false;
601
602
34
  if (com1->size == com2->size)
603
34
    if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
604
34
        == 0)
605
34
      return true;
606
0
  return false;
607
34
}
608
609
/* Add com2 to the end of com1. */
610
struct community *community_merge(struct community *com1,
611
          struct community *com2)
612
404
{
613
404
  com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
614
404
           (com1->size + com2->size) * COMMUNITY_SIZE);
615
616
404
  memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
617
404
  com1->size += com2->size;
618
619
404
  return com1;
620
404
}
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
808
{
671
808
  const char *p = buf;
672
673
  /* Skip white space. */
674
808
  while (isspace((unsigned char)*p))
675
0
    p++;
676
677
  /* Check the end of the line. */
678
808
  if (*p == '\0')
679
404
    return NULL;
680
681
  /* Well known community string check. */
682
404
  if (isalpha((unsigned char)*p)) {
683
#if CONFDATE > 20230801
684
CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
685
#endif
686
404
    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
404
    if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
694
404
        == 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
404
    if (strncmp(p, "accept-own-nexthop",
701
404
          strlen("accept-own-nexthop"))
702
404
        == 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
404
    if (strncmp(p, "accept-own", strlen("accept-own"))
709
404
        == 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
404
    if (strncmp(p, "route-filter-translated-v4",
716
404
      strlen("route-filter-translated-v4"))
717
404
        == 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
404
    if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
724
404
        == 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
404
    if (strncmp(p, "route-filter-translated-v6",
731
404
      strlen("route-filter-translated-v6"))
732
404
        == 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
404
    if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
739
404
        == 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
404
    if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
746
404
        == 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
404
    if (strncmp(p, "no-llgr", strlen("no-llgr"))
753
404
        == 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
404
    if (strncmp(p, "blackhole", strlen("blackhole"))
760
404
        == 0) {
761
0
      *val = COMMUNITY_BLACKHOLE;
762
0
      *token = community_token_blackhole;
763
0
      p += strlen("blackhole");
764
0
      return p;
765
0
    }
766
404
    if (strncmp(p, "no-export", strlen("no-export")) == 0) {
767
404
      *val = COMMUNITY_NO_EXPORT;
768
404
      *token = community_token_no_export;
769
404
      p += strlen("no-export");
770
404
      return p;
771
404
    }
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
404
{
849
404
  struct community *com = NULL;
850
404
  struct community *com_sort = NULL;
851
404
  uint32_t val = 0;
852
404
  enum community_token token = community_token_unknown;
853
854
808
  do {
855
808
    str = community_gettoken(str, &token, &val);
856
857
808
    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
808
    case community_token_no_export:
870
808
    case community_token_no_advertise:
871
808
    case community_token_local_as:
872
808
    case community_token_no_peer:
873
808
      if (com == NULL) {
874
404
        com = community_new();
875
404
        com->json = NULL;
876
404
      }
877
808
      community_add_val(com, val);
878
808
      break;
879
0
    case community_token_unknown:
880
0
      if (com)
881
0
        community_free(&com);
882
0
      return NULL;
883
808
    }
884
808
  } while (str);
885
886
404
  com_sort = community_uniq_sort(com);
887
404
  community_free(&com);
888
889
404
  return com_sort;
890
404
}
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
}