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