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