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