]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_community.c
Merge pull request #12780 from opensourcerouting/spdx-license-id
[mirror_frr.git] / bgpd / bgp_community.c
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 {
24 return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
25 }
26
27 /* Free communities value. */
28 void community_free(struct community **com)
29 {
30 if (!(*com))
31 return;
32
33 XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
34 XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
35
36 if ((*com)->json) {
37 json_object_free((*com)->json);
38 (*com)->json = NULL;
39 }
40
41 XFREE(MTYPE_COMMUNITY, (*com));
42 }
43
44 /* Add one community value to the community. */
45 void community_add_val(struct community *com, uint32_t val)
46 {
47 com->size++;
48 com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
49
50 val = htonl(val);
51 memcpy(com_lastval(com), &val, sizeof(uint32_t));
52 }
53
54 /* Delete one community. */
55 void community_del_val(struct community *com, uint32_t *val)
56 {
57 int i = 0;
58 int c = 0;
59
60 if (!com->val)
61 return;
62
63 while (i < com->size) {
64 if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
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);
78 }
79 return;
80 }
81 i++;
82 }
83 }
84
85 /* Delete all communities listed in com2 from com1 */
86 struct community *community_delete(struct community *com1,
87 struct community *com2)
88 {
89 int i = 0;
90
91 while (i < com2->size) {
92 community_del_val(com1, com2->val + i);
93 i++;
94 }
95
96 return com1;
97 }
98
99 /* Callback function from qsort(). */
100 static int community_compare(const void *a1, const void *a2)
101 {
102 uint32_t v1;
103 uint32_t v2;
104
105 memcpy(&v1, a1, sizeof(uint32_t));
106 memcpy(&v2, a2, sizeof(uint32_t));
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;
115 }
116
117 bool community_include(struct community *com, uint32_t val)
118 {
119 int i;
120
121 val = htonl(val);
122
123 for (i = 0; i < com->size; i++)
124 if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
125 return true;
126 return false;
127 }
128
129 uint32_t community_val_get(struct community *com, int i)
130 {
131 uint8_t *p;
132 uint32_t val;
133
134 p = (uint8_t *)com->val;
135 p += (i * COMMUNITY_SIZE);
136
137 memcpy(&val, p, sizeof(uint32_t));
138
139 return ntohl(val);
140 }
141
142 /* Sort and uniq given community. */
143 struct community *community_uniq_sort(struct community *com)
144 {
145 int i;
146 struct community *new;
147 uint32_t val;
148
149 if (!com)
150 return NULL;
151
152 new = community_new();
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
162 qsort(new->val, new->size, sizeof(uint32_t), community_compare);
163
164 return new;
165 }
166
167 /* Convert communities attribute to string.
168
169 For Well-known communities value, below keyword is used.
170
171 0x0 "internet"
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"
182 0xFFFFFF01 "no-export"
183 0xFFFFFF02 "no-advertise"
184 0xFFFFFF03 "local-AS"
185 0xFFFFFF04 "no-peer"
186
187 For other values, "AS:VAL" format is used. */
188 static void set_community_string(struct community *com, bool make_json,
189 bool translate_alias)
190 {
191 int i;
192 char *str;
193 int len;
194 int first;
195 uint32_t comval;
196 uint16_t as;
197 uint16_t val;
198 json_object *json_community_list = NULL;
199 json_object *json_string = NULL;
200
201 if (!com)
202 return;
203
204 if (make_json) {
205 com->json = json_object_new_object();
206 json_community_list = json_object_new_array();
207 }
208
209 /* When communities attribute is empty. */
210 if (com->size == 0) {
211 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
212 str[0] = '\0';
213
214 if (make_json) {
215 json_object_string_add(com->json, "string", "");
216 json_object_object_add(com->json, "list",
217 json_community_list);
218 }
219 com->str = str;
220 return;
221 }
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++) {
228 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
229 comval = ntohl(comval);
230
231 switch (comval) {
232 case COMMUNITY_INTERNET:
233 len += strlen(" internet");
234 break;
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;
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;
274 case COMMUNITY_NO_PEER:
275 len += strlen(" no-peer");
276 break;
277 default:
278 len = BUFSIZ;
279 break;
280 }
281 }
282
283 /* Allocate memory. */
284 str = XCALLOC(MTYPE_COMMUNITY_STR, len);
285 first = 1;
286
287 /* Fill in string. */
288 for (i = 0; i < com->size; i++) {
289 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
290 comval = ntohl(comval);
291
292 if (first)
293 first = 0;
294 else
295 strlcat(str, " ", len);
296
297 switch (comval) {
298 case COMMUNITY_INTERNET:
299 strlcat(str, "internet", len);
300 if (make_json) {
301 json_string =
302 json_object_new_string("internet");
303 json_object_array_add(json_community_list,
304 json_string);
305 }
306 break;
307 case COMMUNITY_GSHUT:
308 strlcat(str, "graceful-shutdown", len);
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:
317 strlcat(str, "accept-own", len);
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:
326 strlcat(str, "route-filter-translated-v4", len);
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:
335 strlcat(str, "route-filter-v4", len);
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:
344 strlcat(str, "route-filter-translated-v6", len);
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:
353 strlcat(str, "route-filter-v6", len);
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:
362 strlcat(str, "llgr-stale", len);
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:
371 strlcat(str, "no-llgr", len);
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:
380 strlcat(str, "accept-own-nexthop", len);
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:
389 strlcat(str, "blackhole", len);
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;
397 case COMMUNITY_NO_EXPORT:
398 strlcat(str, "no-export", len);
399 if (make_json) {
400 json_string =
401 json_object_new_string("noExport");
402 json_object_array_add(json_community_list,
403 json_string);
404 }
405 break;
406 case COMMUNITY_NO_ADVERTISE:
407 strlcat(str, "no-advertise", len);
408 if (make_json) {
409 json_string =
410 json_object_new_string("noAdvertise");
411 json_object_array_add(json_community_list,
412 json_string);
413 }
414 break;
415 case COMMUNITY_LOCAL_AS:
416 strlcat(str, "local-AS", len);
417 if (make_json) {
418 json_string = json_object_new_string("localAs");
419 json_object_array_add(json_community_list,
420 json_string);
421 }
422 break;
423 case COMMUNITY_NO_PEER:
424 strlcat(str, "no-peer", len);
425 if (make_json) {
426 json_string = json_object_new_string("noPeer");
427 json_object_array_add(json_community_list,
428 json_string);
429 }
430 break;
431 default:
432 as = (comval >> 16) & 0xFFFF;
433 val = comval & 0xFFFF;
434 char buf[32];
435 snprintf(buf, sizeof(buf), "%u:%d", as, val);
436 const char *com2alias =
437 translate_alias ? bgp_community2alias(buf)
438 : buf;
439
440 strlcat(str, com2alias, len);
441 if (make_json) {
442 json_string = json_object_new_string(com2alias);
443 json_object_array_add(json_community_list,
444 json_string);
445 }
446 break;
447 }
448 }
449
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 }
454 com->str = str;
455 }
456
457 /* Intern communities attribute. */
458 struct community *community_intern(struct community *com)
459 {
460 struct community *find;
461
462 /* Assert this community structure is not interned. */
463 assert(com->refcnt == 0);
464
465 /* Lookup community hash. */
466 find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
467
468 /* Arguemnt com is allocated temporary. So when it is not used in
469 hash, it should be freed. */
470 if (find != com)
471 community_free(&com);
472
473 /* Increment refrence counter. */
474 find->refcnt++;
475
476 /* Make string. */
477 if (!find->str)
478 set_community_string(find, false, true);
479
480 return find;
481 }
482
483 /* Free community attribute. */
484 void community_unintern(struct community **com)
485 {
486 struct community *ret;
487
488 if (!*com)
489 return;
490
491 if ((*com)->refcnt)
492 (*com)->refcnt--;
493
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);
499
500 community_free(com);
501 }
502 }
503
504 /* Create new community attribute. */
505 struct community *community_parse(uint32_t *pnt, unsigned short length)
506 {
507 struct community tmp;
508 struct community *new;
509
510 /* If length is malformed return NULL. */
511 if (length % COMMUNITY_SIZE)
512 return NULL;
513
514 /* Make temporary community for hash look up. */
515 tmp.size = length / COMMUNITY_SIZE;
516 tmp.val = pnt;
517
518 new = community_uniq_sort(&tmp);
519
520 return community_intern(new);
521 }
522
523 struct community *community_dup(struct community *com)
524 {
525 struct community *new;
526
527 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
528 new->size = com->size;
529 if (new->size) {
530 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
531 com->size * COMMUNITY_SIZE);
532 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
533 } else
534 new->val = NULL;
535 return new;
536 }
537
538 /* Return string representation of communities attribute. */
539 char *community_str(struct community *com, bool make_json, bool translate_alias)
540 {
541 if (!com)
542 return NULL;
543
544 if (make_json && !com->json && com->str)
545 XFREE(MTYPE_COMMUNITY_STR, com->str);
546
547 if (!com->str)
548 set_community_string(com, make_json, translate_alias);
549 return com->str;
550 }
551
552 /* Make hash value of community attribute. This function is used by
553 hash package.*/
554 unsigned int community_hash_make(const struct community *com)
555 {
556 uint32_t *pnt = com->val;
557
558 return jhash2(pnt, com->size, 0x43ea96c1);
559 }
560
561 bool community_match(const struct community *com1, const struct community *com2)
562 {
563 int i = 0;
564 int j = 0;
565
566 if (com1 == NULL && com2 == NULL)
567 return true;
568
569 if (com1 == NULL || com2 == NULL)
570 return false;
571
572 if (com1->size < com2->size)
573 return false;
574
575 /* Every community on com2 needs to be on com1 for this to match */
576 while (i < com1->size && j < com2->size) {
577 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
578 j++;
579 i++;
580 }
581
582 if (j == com2->size)
583 return true;
584 else
585 return false;
586 }
587
588 bool community_cmp(const struct community *com1, const struct community *com2)
589 {
590 if (com1 == NULL && com2 == NULL)
591 return true;
592 if (com1 == NULL || com2 == NULL)
593 return false;
594
595 if (com1->size == com2->size)
596 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
597 == 0)
598 return true;
599 return false;
600 }
601
602 /* Add com2 to the end of com1. */
603 struct community *community_merge(struct community *com1,
604 struct community *com2)
605 {
606 com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
607 (com1->size + com2->size) * COMMUNITY_SIZE);
608
609 memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
610 com1->size += com2->size;
611
612 return com1;
613 }
614
615 /* Community token enum. */
616 enum community_token {
617 community_token_val,
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,
628 community_token_no_export,
629 community_token_no_advertise,
630 community_token_local_as,
631 community_token_no_peer,
632 community_token_unknown
633 };
634
635 /* Helper to check if a given community is valid */
636 static 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
660 /* Get next community token from string. */
661 static const char *
662 community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
663 {
664 const char *p = buf;
665
666 /* Skip white space. */
667 while (isspace((unsigned char)*p))
668 p++;
669
670 /* Check the end of the line. */
671 if (*p == '\0')
672 return NULL;
673
674 /* Well known community string check. */
675 if (isalpha((unsigned char)*p)) {
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 }
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 }
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 }
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 }
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 }
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 }
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");
777 return p;
778 }
779
780 /* Unknown string. */
781 *token = community_token_unknown;
782 return NULL;
783 }
784
785 /* Community value. */
786 if (isdigit((unsigned char)*p)) {
787 int separator = 0;
788 int digit = 0;
789 uint32_t community_low = 0;
790 uint32_t community_high = 0;
791
792 if (!community_valid(p)) {
793 *token = community_token_unknown;
794 return NULL;
795 }
796
797 while (isdigit((unsigned char)*p) || *p == ':') {
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++;
821 }
822 if (!digit) {
823 *token = community_token_unknown;
824 return NULL;
825 }
826
827 *val = community_high + community_low;
828 *token = community_token_val;
829 return p;
830 }
831 *token = community_token_unknown;
832 return NULL;
833 }
834
835 /* convert string to community structure */
836 struct community *community_str2com(const char *str)
837 {
838 struct community *com = NULL;
839 struct community *com_sort = NULL;
840 uint32_t val = 0;
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:
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:
858 case community_token_no_export:
859 case community_token_no_advertise:
860 case community_token_local_as:
861 case community_token_no_peer:
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:
869 if (com)
870 community_free(&com);
871 return NULL;
872 }
873 } while (str);
874
875 com_sort = community_uniq_sort(com);
876 community_free(&com);
877
878 return com_sort;
879 }
880
881 /* Return communities hash entry count. */
882 unsigned long community_count(void)
883 {
884 return comhash->count;
885 }
886
887 /* Return communities hash. */
888 struct hash *community_hash(void)
889 {
890 return comhash;
891 }
892
893 /* Initialize comminity related hash. */
894 void community_init(void)
895 {
896 comhash =
897 hash_create((unsigned int (*)(const void *))community_hash_make,
898 (bool (*)(const void *, const void *))community_cmp,
899 "BGP Community Hash");
900 }
901
902 static void community_hash_free(void *data)
903 {
904 struct community *com = data;
905
906 community_free(&com);
907 }
908
909 void community_finish(void)
910 {
911 hash_clean(comhash, community_hash_free);
912 hash_free(comhash);
913 comhash = NULL;
914 }
915
916 static 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
923 static void *bgp_aggr_community_hash_alloc(void *p)
924 {
925 struct community *ref = (struct community *)p;
926 struct community *community = NULL;
927
928 community = community_dup(ref);
929 return community;
930 }
931
932 static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
933 {
934 struct community *hb_community = hb->data;
935 struct community **aggr_community = arg;
936
937 if (*aggr_community)
938 *aggr_community = community_merge(*aggr_community,
939 hb_community);
940 else
941 *aggr_community = community_dup(hb_community);
942 }
943
944 void bgp_aggr_community_remove(void *arg)
945 {
946 struct community *community = arg;
947
948 community_free(&community);
949 }
950
951 void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
952 struct community *community)
953 {
954 bgp_compute_aggregate_community_hash(aggregate, community);
955 bgp_compute_aggregate_community_val(aggregate);
956 }
957
958
959 void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
960 struct community *community)
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(
971 (unsigned int (*)(const void *))community_hash_make,
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,
980 bgp_aggr_community_hash_alloc);
981 }
982
983 /* Increment reference counter.
984 */
985 aggr_community->refcnt++;
986 }
987
988 void 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) {
1001 hash_iterate(aggregate->community_hash,
1002 bgp_aggr_community_prepare,
1003 &aggregate->community);
1004 commerge = aggregate->community;
1005 aggregate->community = community_uniq_sort(commerge);
1006 if (commerge)
1007 community_free(&commerge);
1008 }
1009 }
1010
1011
1012
1013 void 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
1019 if ((!aggregate)
1020 || (!aggregate->community_hash)
1021 || (!community))
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
1035 bgp_compute_aggregate_community_val(aggregate);
1036 }
1037 }
1038 }
1039
1040 void 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;
1046
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);
1062 }
1063 }
1064 }