]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_community.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[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 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 {
190 int i;
191 char *str;
192 int len;
193 int first;
194 uint32_t comval;
195 uint16_t as;
196 uint16_t val;
197 json_object *json_community_list = NULL;
198 json_object *json_string = NULL;
199
200 if (!com)
201 return;
202
203 if (make_json) {
204 com->json = json_object_new_object();
205 json_community_list = json_object_new_array();
206 }
207
208 /* When communities attribute is empty. */
209 if (com->size == 0) {
210 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
211 str[0] = '\0';
212
213 if (make_json) {
214 json_object_string_add(com->json, "string", "");
215 json_object_object_add(com->json, "list",
216 json_community_list);
217 }
218 com->str = str;
219 return;
220 }
221
222 /* Memory allocation is time consuming work. So we calculate
223 required string length first. */
224 len = 0;
225
226 for (i = 0; i < com->size; i++) {
227 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
228 comval = ntohl(comval);
229
230 switch (comval) {
231 #if CONFDATE > 20230801
232 CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
233 #endif
234 case COMMUNITY_INTERNET:
235 len += strlen(" internet");
236 zlog_warn("`internet` community is deprecated");
237 break;
238 case COMMUNITY_GSHUT:
239 len += strlen(" graceful-shutdown");
240 break;
241 case COMMUNITY_ACCEPT_OWN:
242 len += strlen(" accept-own");
243 break;
244 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
245 len += strlen(" route-filter-translated-v4");
246 break;
247 case COMMUNITY_ROUTE_FILTER_v4:
248 len += strlen(" route-filter-v4");
249 break;
250 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
251 len += strlen(" route-filter-translated-v6");
252 break;
253 case COMMUNITY_ROUTE_FILTER_v6:
254 len += strlen(" route-filter-v6");
255 break;
256 case COMMUNITY_LLGR_STALE:
257 len += strlen(" llgr-stale");
258 break;
259 case COMMUNITY_NO_LLGR:
260 len += strlen(" no-llgr");
261 break;
262 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
263 len += strlen(" accept-own-nexthop");
264 break;
265 case COMMUNITY_BLACKHOLE:
266 len += strlen(" blackhole");
267 break;
268 case COMMUNITY_NO_EXPORT:
269 len += strlen(" no-export");
270 break;
271 case COMMUNITY_NO_ADVERTISE:
272 len += strlen(" no-advertise");
273 break;
274 case COMMUNITY_LOCAL_AS:
275 len += strlen(" local-AS");
276 break;
277 case COMMUNITY_NO_PEER:
278 len += strlen(" no-peer");
279 break;
280 default:
281 len = BUFSIZ;
282 break;
283 }
284 }
285
286 /* Allocate memory. */
287 str = XCALLOC(MTYPE_COMMUNITY_STR, len);
288 first = 1;
289
290 /* Fill in string. */
291 for (i = 0; i < com->size; i++) {
292 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
293 comval = ntohl(comval);
294
295 if (first)
296 first = 0;
297 else
298 strlcat(str, " ", len);
299
300 switch (comval) {
301 #if CONFDATE > 20230801
302 CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
303 #endif
304 case COMMUNITY_INTERNET:
305 strlcat(str, "internet", len);
306 if (make_json) {
307 json_string =
308 json_object_new_string("internet");
309 json_object_array_add(json_community_list,
310 json_string);
311 }
312 zlog_warn("`internet` community is deprecated");
313 break;
314 case COMMUNITY_GSHUT:
315 strlcat(str, "graceful-shutdown", len);
316 if (make_json) {
317 json_string = json_object_new_string(
318 "gracefulShutdown");
319 json_object_array_add(json_community_list,
320 json_string);
321 }
322 break;
323 case COMMUNITY_ACCEPT_OWN:
324 strlcat(str, "accept-own", len);
325 if (make_json) {
326 json_string = json_object_new_string(
327 "acceptown");
328 json_object_array_add(json_community_list,
329 json_string);
330 }
331 break;
332 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
333 strlcat(str, "route-filter-translated-v4", len);
334 if (make_json) {
335 json_string = json_object_new_string(
336 "routeFilterTranslatedV4");
337 json_object_array_add(json_community_list,
338 json_string);
339 }
340 break;
341 case COMMUNITY_ROUTE_FILTER_v4:
342 strlcat(str, "route-filter-v4", len);
343 if (make_json) {
344 json_string = json_object_new_string(
345 "routeFilterV4");
346 json_object_array_add(json_community_list,
347 json_string);
348 }
349 break;
350 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
351 strlcat(str, "route-filter-translated-v6", len);
352 if (make_json) {
353 json_string = json_object_new_string(
354 "routeFilterTranslatedV6");
355 json_object_array_add(json_community_list,
356 json_string);
357 }
358 break;
359 case COMMUNITY_ROUTE_FILTER_v6:
360 strlcat(str, "route-filter-v6", len);
361 if (make_json) {
362 json_string = json_object_new_string(
363 "routeFilterV6");
364 json_object_array_add(json_community_list,
365 json_string);
366 }
367 break;
368 case COMMUNITY_LLGR_STALE:
369 strlcat(str, "llgr-stale", len);
370 if (make_json) {
371 json_string = json_object_new_string(
372 "llgrStale");
373 json_object_array_add(json_community_list,
374 json_string);
375 }
376 break;
377 case COMMUNITY_NO_LLGR:
378 strlcat(str, "no-llgr", len);
379 if (make_json) {
380 json_string = json_object_new_string(
381 "noLlgr");
382 json_object_array_add(json_community_list,
383 json_string);
384 }
385 break;
386 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
387 strlcat(str, "accept-own-nexthop", len);
388 if (make_json) {
389 json_string = json_object_new_string(
390 "acceptownnexthop");
391 json_object_array_add(json_community_list,
392 json_string);
393 }
394 break;
395 case COMMUNITY_BLACKHOLE:
396 strlcat(str, "blackhole", len);
397 if (make_json) {
398 json_string = json_object_new_string(
399 "blackhole");
400 json_object_array_add(json_community_list,
401 json_string);
402 }
403 break;
404 case COMMUNITY_NO_EXPORT:
405 strlcat(str, "no-export", len);
406 if (make_json) {
407 json_string =
408 json_object_new_string("noExport");
409 json_object_array_add(json_community_list,
410 json_string);
411 }
412 break;
413 case COMMUNITY_NO_ADVERTISE:
414 strlcat(str, "no-advertise", len);
415 if (make_json) {
416 json_string =
417 json_object_new_string("noAdvertise");
418 json_object_array_add(json_community_list,
419 json_string);
420 }
421 break;
422 case COMMUNITY_LOCAL_AS:
423 strlcat(str, "local-AS", len);
424 if (make_json) {
425 json_string = json_object_new_string("localAs");
426 json_object_array_add(json_community_list,
427 json_string);
428 }
429 break;
430 case COMMUNITY_NO_PEER:
431 strlcat(str, "no-peer", len);
432 if (make_json) {
433 json_string = json_object_new_string("noPeer");
434 json_object_array_add(json_community_list,
435 json_string);
436 }
437 break;
438 default:
439 as = (comval >> 16) & 0xFFFF;
440 val = comval & 0xFFFF;
441 char buf[32];
442 snprintf(buf, sizeof(buf), "%u:%d", as, val);
443 const char *com2alias =
444 translate_alias ? bgp_community2alias(buf)
445 : buf;
446
447 strlcat(str, com2alias, len);
448 if (make_json) {
449 json_string = json_object_new_string(com2alias);
450 json_object_array_add(json_community_list,
451 json_string);
452 }
453 break;
454 }
455 }
456
457 if (make_json) {
458 json_object_string_add(com->json, "string", str);
459 json_object_object_add(com->json, "list", json_community_list);
460 }
461 com->str = str;
462 }
463
464 /* Intern communities attribute. */
465 struct community *community_intern(struct community *com)
466 {
467 struct community *find;
468
469 /* Assert this community structure is not interned. */
470 assert(com->refcnt == 0);
471
472 /* Lookup community hash. */
473 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 if (find != com)
478 community_free(&com);
479
480 /* Increment refrence counter. */
481 find->refcnt++;
482
483 /* Make string. */
484 if (!find->str)
485 set_community_string(find, false, true);
486
487 return find;
488 }
489
490 /* Free community attribute. */
491 void community_unintern(struct community **com)
492 {
493 struct community *ret;
494
495 if (!*com)
496 return;
497
498 if ((*com)->refcnt)
499 (*com)->refcnt--;
500
501 /* Pull off from hash. */
502 if ((*com)->refcnt == 0) {
503 /* Community value com must exist in hash. */
504 ret = (struct community *)hash_release(comhash, *com);
505 assert(ret != NULL);
506
507 community_free(com);
508 }
509 }
510
511 /* Create new community attribute. */
512 struct community *community_parse(uint32_t *pnt, unsigned short length)
513 {
514 struct community tmp;
515 struct community *new;
516
517 /* If length is malformed return NULL. */
518 if (length % COMMUNITY_SIZE)
519 return NULL;
520
521 /* Make temporary community for hash look up. */
522 tmp.size = length / COMMUNITY_SIZE;
523 tmp.val = pnt;
524
525 new = community_uniq_sort(&tmp);
526
527 return community_intern(new);
528 }
529
530 struct community *community_dup(struct community *com)
531 {
532 struct community *new;
533
534 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
535 new->size = com->size;
536 if (new->size) {
537 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
538 com->size * COMMUNITY_SIZE);
539 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
540 } else
541 new->val = NULL;
542 return new;
543 }
544
545 /* Return string representation of communities attribute. */
546 char *community_str(struct community *com, bool make_json, bool translate_alias)
547 {
548 if (!com)
549 return NULL;
550
551 if (make_json && !com->json && com->str)
552 XFREE(MTYPE_COMMUNITY_STR, com->str);
553
554 if (!com->str)
555 set_community_string(com, make_json, translate_alias);
556 return com->str;
557 }
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 {
563 uint32_t *pnt = com->val;
564
565 return jhash2(pnt, com->size, 0x43ea96c1);
566 }
567
568 bool community_match(const struct community *com1, const struct community *com2)
569 {
570 int i = 0;
571 int j = 0;
572
573 if (com1 == NULL && com2 == NULL)
574 return true;
575
576 if (com1 == NULL || com2 == NULL)
577 return false;
578
579 if (com1->size < com2->size)
580 return false;
581
582 /* Every community on com2 needs to be on com1 for this to match */
583 while (i < com1->size && j < com2->size) {
584 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
585 j++;
586 i++;
587 }
588
589 if (j == com2->size)
590 return true;
591 else
592 return false;
593 }
594
595 bool community_cmp(const struct community *com1, const struct community *com2)
596 {
597 if (com1 == NULL && com2 == NULL)
598 return true;
599 if (com1 == NULL || com2 == NULL)
600 return false;
601
602 if (com1->size == com2->size)
603 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
604 == 0)
605 return true;
606 return false;
607 }
608
609 /* Add com2 to the end of com1. */
610 struct community *community_merge(struct community *com1,
611 struct community *com2)
612 {
613 com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
614 (com1->size + com2->size) * COMMUNITY_SIZE);
615
616 memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
617 com1->size += com2->size;
618
619 return com1;
620 }
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 {
645 int octets = 0;
646 char **splits;
647 int num;
648 int invalid = 0;
649
650 frrstr_split(community, ":", &splits, &num);
651
652 for (int i = 0; i < num; i++) {
653 if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
654 invalid++;
655
656 if (strlen(splits[i]) == 0)
657 invalid++;
658
659 octets++;
660 XFREE(MTYPE_TMP, splits[i]);
661 }
662 XFREE(MTYPE_TMP, splits);
663
664 return (octets < 2 || invalid) ? false : true;
665 }
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 {
671 const char *p = buf;
672
673 /* Skip white space. */
674 while (isspace((unsigned char)*p))
675 p++;
676
677 /* Check the end of the line. */
678 if (*p == '\0')
679 return NULL;
680
681 /* Well known community string check. */
682 if (isalpha((unsigned char)*p)) {
683 #if CONFDATE > 20230801
684 CPP_NOTICE("Deprecate COMMUNITY_INTERNET BGP community")
685 #endif
686 if (strncmp(p, "internet", strlen("internet")) == 0) {
687 *val = COMMUNITY_INTERNET;
688 *token = community_token_no_export;
689 p += strlen("internet");
690 zlog_warn("`internet` community is deprecated");
691 return p;
692 }
693 if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
694 == 0) {
695 *val = COMMUNITY_GSHUT;
696 *token = community_token_gshut;
697 p += strlen("graceful-shutdown");
698 return p;
699 }
700 if (strncmp(p, "accept-own-nexthop",
701 strlen("accept-own-nexthop"))
702 == 0) {
703 *val = COMMUNITY_ACCEPT_OWN_NEXTHOP;
704 *token = community_token_accept_own_nexthop;
705 p += strlen("accept-own-nexthop");
706 return p;
707 }
708 if (strncmp(p, "accept-own", strlen("accept-own"))
709 == 0) {
710 *val = COMMUNITY_ACCEPT_OWN;
711 *token = community_token_accept_own;
712 p += strlen("accept-own");
713 return p;
714 }
715 if (strncmp(p, "route-filter-translated-v4",
716 strlen("route-filter-translated-v4"))
717 == 0) {
718 *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v4;
719 *token = community_token_route_filter_translated_v4;
720 p += strlen("route-filter-translated-v4");
721 return p;
722 }
723 if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
724 == 0) {
725 *val = COMMUNITY_ROUTE_FILTER_v4;
726 *token = community_token_route_filter_v4;
727 p += strlen("route-filter-v4");
728 return p;
729 }
730 if (strncmp(p, "route-filter-translated-v6",
731 strlen("route-filter-translated-v6"))
732 == 0) {
733 *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v6;
734 *token = community_token_route_filter_translated_v6;
735 p += strlen("route-filter-translated-v6");
736 return p;
737 }
738 if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
739 == 0) {
740 *val = COMMUNITY_ROUTE_FILTER_v6;
741 *token = community_token_route_filter_v6;
742 p += strlen("route-filter-v6");
743 return p;
744 }
745 if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
746 == 0) {
747 *val = COMMUNITY_LLGR_STALE;
748 *token = community_token_llgr_stale;
749 p += strlen("llgr-stale");
750 return p;
751 }
752 if (strncmp(p, "no-llgr", strlen("no-llgr"))
753 == 0) {
754 *val = COMMUNITY_NO_LLGR;
755 *token = community_token_no_llgr;
756 p += strlen("no-llgr");
757 return p;
758 }
759 if (strncmp(p, "blackhole", strlen("blackhole"))
760 == 0) {
761 *val = COMMUNITY_BLACKHOLE;
762 *token = community_token_blackhole;
763 p += strlen("blackhole");
764 return p;
765 }
766 if (strncmp(p, "no-export", strlen("no-export")) == 0) {
767 *val = COMMUNITY_NO_EXPORT;
768 *token = community_token_no_export;
769 p += strlen("no-export");
770 return p;
771 }
772 if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
773 *val = COMMUNITY_NO_ADVERTISE;
774 *token = community_token_no_advertise;
775 p += strlen("no-advertise");
776 return p;
777 }
778 if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
779 *val = COMMUNITY_LOCAL_AS;
780 *token = community_token_local_as;
781 p += strlen("local-AS");
782 return p;
783 }
784 if (strncmp(p, "no-peer", strlen("no-peer")) == 0) {
785 *val = COMMUNITY_NO_PEER;
786 *token = community_token_no_peer;
787 p += strlen("no-peer");
788 return p;
789 }
790
791 /* Unknown string. */
792 *token = community_token_unknown;
793 return NULL;
794 }
795
796 /* Community value. */
797 if (isdigit((unsigned char)*p)) {
798 int separator = 0;
799 int digit = 0;
800 uint32_t community_low = 0;
801 uint32_t community_high = 0;
802
803 if (!community_valid(p)) {
804 *token = community_token_unknown;
805 return NULL;
806 }
807
808 while (isdigit((unsigned char)*p) || *p == ':') {
809 if (*p == ':') {
810 if (separator) {
811 *token = community_token_unknown;
812 return NULL;
813 } else {
814 separator = 1;
815 digit = 0;
816
817 if (community_low > UINT16_MAX) {
818 *token =
819 community_token_unknown;
820 return NULL;
821 }
822
823 community_high = community_low << 16;
824 community_low = 0;
825 }
826 } else {
827 digit = 1;
828 community_low *= 10;
829 community_low += (*p - '0');
830 }
831 p++;
832 }
833 if (!digit) {
834 *token = community_token_unknown;
835 return NULL;
836 }
837
838 *val = community_high + community_low;
839 *token = community_token_val;
840 return p;
841 }
842 *token = community_token_unknown;
843 return NULL;
844 }
845
846 /* convert string to community structure */
847 struct community *community_str2com(const char *str)
848 {
849 struct community *com = NULL;
850 struct community *com_sort = NULL;
851 uint32_t val = 0;
852 enum community_token token = community_token_unknown;
853
854 do {
855 str = community_gettoken(str, &token, &val);
856
857 switch (token) {
858 case community_token_val:
859 case community_token_gshut:
860 case community_token_accept_own:
861 case community_token_route_filter_translated_v4:
862 case community_token_route_filter_v4:
863 case community_token_route_filter_translated_v6:
864 case community_token_route_filter_v6:
865 case community_token_llgr_stale:
866 case community_token_no_llgr:
867 case community_token_accept_own_nexthop:
868 case community_token_blackhole:
869 case community_token_no_export:
870 case community_token_no_advertise:
871 case community_token_local_as:
872 case community_token_no_peer:
873 if (com == NULL) {
874 com = community_new();
875 com->json = NULL;
876 }
877 community_add_val(com, val);
878 break;
879 case community_token_unknown:
880 if (com)
881 community_free(&com);
882 return NULL;
883 }
884 } while (str);
885
886 com_sort = community_uniq_sort(com);
887 community_free(&com);
888
889 return com_sort;
890 }
891
892 /* Return communities hash entry count. */
893 unsigned long community_count(void)
894 {
895 return comhash->count;
896 }
897
898 /* Return communities hash. */
899 struct hash *community_hash(void)
900 {
901 return comhash;
902 }
903
904 /* Initialize comminity related hash. */
905 void community_init(void)
906 {
907 comhash =
908 hash_create((unsigned int (*)(const void *))community_hash_make,
909 (bool (*)(const void *, const void *))community_cmp,
910 "BGP Community Hash");
911 }
912
913 static void community_hash_free(void *data)
914 {
915 struct community *com = data;
916
917 community_free(&com);
918 }
919
920 void community_finish(void)
921 {
922 hash_clean(comhash, community_hash_free);
923 hash_free(comhash);
924 comhash = NULL;
925 }
926
927 static struct community *bgp_aggr_community_lookup(
928 struct bgp_aggregate *aggregate,
929 struct community *community)
930 {
931 return hash_lookup(aggregate->community_hash, community);
932 }
933
934 static void *bgp_aggr_community_hash_alloc(void *p)
935 {
936 struct community *ref = (struct community *)p;
937 struct community *community = NULL;
938
939 community = community_dup(ref);
940 return community;
941 }
942
943 static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
944 {
945 struct community *hb_community = hb->data;
946 struct community **aggr_community = arg;
947
948 if (*aggr_community)
949 *aggr_community = community_merge(*aggr_community,
950 hb_community);
951 else
952 *aggr_community = community_dup(hb_community);
953 }
954
955 void bgp_aggr_community_remove(void *arg)
956 {
957 struct community *community = arg;
958
959 community_free(&community);
960 }
961
962 void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
963 struct community *community)
964 {
965 bgp_compute_aggregate_community_hash(aggregate, community);
966 bgp_compute_aggregate_community_val(aggregate);
967 }
968
969
970 void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
971 struct community *community)
972 {
973 struct community *aggr_community = NULL;
974
975 if ((aggregate == NULL) || (community == NULL))
976 return;
977
978 /* Create hash if not already created.
979 */
980 if (aggregate->community_hash == NULL)
981 aggregate->community_hash = hash_create(
982 (unsigned int (*)(const void *))community_hash_make,
983 (bool (*)(const void *, const void *))community_cmp,
984 "BGP Aggregator community hash");
985
986 aggr_community = bgp_aggr_community_lookup(aggregate, community);
987 if (aggr_community == NULL) {
988 /* Insert community into hash.
989 */
990 aggr_community = hash_get(aggregate->community_hash, community,
991 bgp_aggr_community_hash_alloc);
992 }
993
994 /* Increment reference counter.
995 */
996 aggr_community->refcnt++;
997 }
998
999 void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
1000 {
1001 struct community *commerge = NULL;
1002
1003 if (aggregate == NULL)
1004 return;
1005
1006 /* Re-compute aggregate's community.
1007 */
1008 if (aggregate->community)
1009 community_free(&aggregate->community);
1010 if (aggregate->community_hash &&
1011 aggregate->community_hash->count) {
1012 hash_iterate(aggregate->community_hash,
1013 bgp_aggr_community_prepare,
1014 &aggregate->community);
1015 commerge = aggregate->community;
1016 aggregate->community = community_uniq_sort(commerge);
1017 if (commerge)
1018 community_free(&commerge);
1019 }
1020 }
1021
1022
1023
1024 void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
1025 struct community *community)
1026 {
1027 struct community *aggr_community = NULL;
1028 struct community *ret_comm = NULL;
1029
1030 if ((!aggregate)
1031 || (!aggregate->community_hash)
1032 || (!community))
1033 return;
1034
1035 /* Look-up the community in the hash.
1036 */
1037 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1038 if (aggr_community) {
1039 aggr_community->refcnt--;
1040
1041 if (aggr_community->refcnt == 0) {
1042 ret_comm = hash_release(aggregate->community_hash,
1043 aggr_community);
1044 community_free(&ret_comm);
1045
1046 bgp_compute_aggregate_community_val(aggregate);
1047 }
1048 }
1049 }
1050
1051 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1052 struct community *community)
1053 {
1054
1055 struct community *aggr_community = NULL;
1056 struct community *ret_comm = NULL;
1057
1058 if ((!aggregate)
1059 || (!aggregate->community_hash)
1060 || (!community))
1061 return;
1062
1063 /* Look-up the community in the hash.
1064 */
1065 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1066 if (aggr_community) {
1067 aggr_community->refcnt--;
1068
1069 if (aggr_community->refcnt == 0) {
1070 ret_comm = hash_release(aggregate->community_hash,
1071 aggr_community);
1072 community_free(&ret_comm);
1073 }
1074 }
1075 }