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