]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_community.c
Merge pull request #9056 from askorichenko/test-dont-capability
[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 #include "frrstr.h"
28
29 #include "bgpd/bgp_memory.h"
30 #include "bgpd/bgp_community.h"
31 #include "bgpd/bgp_community_alias.h"
32
33 /* Hash of community attribute. */
34 static struct hash *comhash;
35
36 /* Allocate a new communities value. */
37 static struct community *community_new(void)
38 {
39 return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
40 }
41
42 /* Free communities value. */
43 void community_free(struct community **com)
44 {
45 if (!(*com))
46 return;
47
48 XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
49 XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
50
51 if ((*com)->json) {
52 json_object_free((*com)->json);
53 (*com)->json = NULL;
54 }
55
56 XFREE(MTYPE_COMMUNITY, (*com));
57 }
58
59 /* Add one community value to the community. */
60 void community_add_val(struct community *com, uint32_t val)
61 {
62 com->size++;
63 com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
64
65 val = htonl(val);
66 memcpy(com_lastval(com), &val, sizeof(uint32_t));
67 }
68
69 /* Delete one community. */
70 void community_del_val(struct community *com, uint32_t *val)
71 {
72 int i = 0;
73 int c = 0;
74
75 if (!com->val)
76 return;
77
78 while (i < com->size) {
79 if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
80 c = com->size - i - 1;
81
82 if (c > 0)
83 memmove(com->val + i, com->val + (i + 1),
84 c * sizeof(*val));
85
86 com->size--;
87
88 if (com->size > 0)
89 com->val = XREALLOC(MTYPE_COMMUNITY_VAL,
90 com->val, com_length(com));
91 else {
92 XFREE(MTYPE_COMMUNITY_VAL, com->val);
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 bool 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 true;
141 return false;
142 }
143
144 uint32_t community_val_get(struct community *com, int i)
145 {
146 uint8_t *p;
147 uint32_t val;
148
149 p = (uint8_t *)com->val;
150 p += (i * COMMUNITY_SIZE);
151
152 memcpy(&val, p, sizeof(uint32_t));
153
154 return ntohl(val);
155 }
156
157 /* Sort and uniq given community. */
158 struct community *community_uniq_sort(struct community *com)
159 {
160 int i;
161 struct community *new;
162 uint32_t val;
163
164 if (!com)
165 return NULL;
166
167 new = community_new();
168 new->json = NULL;
169
170 for (i = 0; i < com->size; i++) {
171 val = community_val_get(com, i);
172
173 if (!community_include(new, val))
174 community_add_val(new, val);
175 }
176
177 qsort(new->val, new->size, sizeof(uint32_t), community_compare);
178
179 return new;
180 }
181
182 /* Convert communities attribute to string.
183
184 For Well-known communities value, below keyword is used.
185
186 0x0 "internet"
187 0xFFFF0000 "graceful-shutdown"
188 0xFFFF0001 "accept-own"
189 0xFFFF0002 "route-filter-translated-v4"
190 0xFFFF0003 "route-filter-v4"
191 0xFFFF0004 "route-filter-translated-v6"
192 0xFFFF0005 "route-filter-v6"
193 0xFFFF0006 "llgr-stale"
194 0xFFFF0007 "no-llgr"
195 0xFFFF0008 "accept-own-nexthop"
196 0xFFFF029A "blackhole"
197 0xFFFFFF01 "no-export"
198 0xFFFFFF02 "no-advertise"
199 0xFFFFFF03 "local-AS"
200 0xFFFFFF04 "no-peer"
201
202 For other values, "AS:VAL" format is used. */
203 static void set_community_string(struct community *com, bool make_json)
204 {
205 int i;
206 char *str;
207 int len;
208 int first;
209 uint32_t comval;
210 uint16_t as;
211 uint16_t val;
212 json_object *json_community_list = NULL;
213 json_object *json_string = NULL;
214
215 if (!com)
216 return;
217
218 if (make_json) {
219 com->json = json_object_new_object();
220 json_community_list = json_object_new_array();
221 }
222
223 /* When communities attribute is empty. */
224 if (com->size == 0) {
225 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
226 str[0] = '\0';
227
228 if (make_json) {
229 json_object_string_add(com->json, "string", "");
230 json_object_object_add(com->json, "list",
231 json_community_list);
232 }
233 com->str = str;
234 return;
235 }
236
237 /* Memory allocation is time consuming work. So we calculate
238 required string length first. */
239 len = 0;
240
241 for (i = 0; i < com->size; i++) {
242 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
243 comval = ntohl(comval);
244
245 switch (comval) {
246 case COMMUNITY_INTERNET:
247 len += strlen(" internet");
248 break;
249 case COMMUNITY_GSHUT:
250 len += strlen(" graceful-shutdown");
251 break;
252 case COMMUNITY_ACCEPT_OWN:
253 len += strlen(" accept-own");
254 break;
255 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
256 len += strlen(" route-filter-translated-v4");
257 break;
258 case COMMUNITY_ROUTE_FILTER_v4:
259 len += strlen(" route-filter-v4");
260 break;
261 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
262 len += strlen(" route-filter-translated-v6");
263 break;
264 case COMMUNITY_ROUTE_FILTER_v6:
265 len += strlen(" route-filter-v6");
266 break;
267 case COMMUNITY_LLGR_STALE:
268 len += strlen(" llgr-stale");
269 break;
270 case COMMUNITY_NO_LLGR:
271 len += strlen(" no-llgr");
272 break;
273 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
274 len += strlen(" accept-own-nexthop");
275 break;
276 case COMMUNITY_BLACKHOLE:
277 len += strlen(" blackhole");
278 break;
279 case COMMUNITY_NO_EXPORT:
280 len += strlen(" no-export");
281 break;
282 case COMMUNITY_NO_ADVERTISE:
283 len += strlen(" no-advertise");
284 break;
285 case COMMUNITY_LOCAL_AS:
286 len += strlen(" local-AS");
287 break;
288 case COMMUNITY_NO_PEER:
289 len += strlen(" no-peer");
290 break;
291 default:
292 len = BUFSIZ;
293 break;
294 }
295 }
296
297 /* Allocate memory. */
298 str = XCALLOC(MTYPE_COMMUNITY_STR, len);
299 first = 1;
300
301 /* Fill in string. */
302 for (i = 0; i < com->size; i++) {
303 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
304 comval = ntohl(comval);
305
306 if (first)
307 first = 0;
308 else
309 strlcat(str, " ", len);
310
311 switch (comval) {
312 case COMMUNITY_INTERNET:
313 strlcat(str, "internet", len);
314 if (make_json) {
315 json_string =
316 json_object_new_string("internet");
317 json_object_array_add(json_community_list,
318 json_string);
319 }
320 break;
321 case COMMUNITY_GSHUT:
322 strlcat(str, "graceful-shutdown", len);
323 if (make_json) {
324 json_string = json_object_new_string(
325 "gracefulShutdown");
326 json_object_array_add(json_community_list,
327 json_string);
328 }
329 break;
330 case COMMUNITY_ACCEPT_OWN:
331 strlcat(str, "accept-own", len);
332 if (make_json) {
333 json_string = json_object_new_string(
334 "acceptown");
335 json_object_array_add(json_community_list,
336 json_string);
337 }
338 break;
339 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
340 strlcat(str, "route-filter-translated-v4", len);
341 if (make_json) {
342 json_string = json_object_new_string(
343 "routeFilterTranslatedV4");
344 json_object_array_add(json_community_list,
345 json_string);
346 }
347 break;
348 case COMMUNITY_ROUTE_FILTER_v4:
349 strlcat(str, "route-filter-v4", len);
350 if (make_json) {
351 json_string = json_object_new_string(
352 "routeFilterV4");
353 json_object_array_add(json_community_list,
354 json_string);
355 }
356 break;
357 case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
358 strlcat(str, "route-filter-translated-v6", len);
359 if (make_json) {
360 json_string = json_object_new_string(
361 "routeFilterTranslatedV6");
362 json_object_array_add(json_community_list,
363 json_string);
364 }
365 break;
366 case COMMUNITY_ROUTE_FILTER_v6:
367 strlcat(str, "route-filter-v6", len);
368 if (make_json) {
369 json_string = json_object_new_string(
370 "routeFilterV6");
371 json_object_array_add(json_community_list,
372 json_string);
373 }
374 break;
375 case COMMUNITY_LLGR_STALE:
376 strlcat(str, "llgr-stale", len);
377 if (make_json) {
378 json_string = json_object_new_string(
379 "llgrStale");
380 json_object_array_add(json_community_list,
381 json_string);
382 }
383 break;
384 case COMMUNITY_NO_LLGR:
385 strlcat(str, "no-llgr", len);
386 if (make_json) {
387 json_string = json_object_new_string(
388 "noLlgr");
389 json_object_array_add(json_community_list,
390 json_string);
391 }
392 break;
393 case COMMUNITY_ACCEPT_OWN_NEXTHOP:
394 strlcat(str, "accept-own-nexthop", len);
395 if (make_json) {
396 json_string = json_object_new_string(
397 "acceptownnexthop");
398 json_object_array_add(json_community_list,
399 json_string);
400 }
401 break;
402 case COMMUNITY_BLACKHOLE:
403 strlcat(str, "blackhole", len);
404 if (make_json) {
405 json_string = json_object_new_string(
406 "blackhole");
407 json_object_array_add(json_community_list,
408 json_string);
409 }
410 break;
411 case COMMUNITY_NO_EXPORT:
412 strlcat(str, "no-export", len);
413 if (make_json) {
414 json_string =
415 json_object_new_string("noExport");
416 json_object_array_add(json_community_list,
417 json_string);
418 }
419 break;
420 case COMMUNITY_NO_ADVERTISE:
421 strlcat(str, "no-advertise", len);
422 if (make_json) {
423 json_string =
424 json_object_new_string("noAdvertise");
425 json_object_array_add(json_community_list,
426 json_string);
427 }
428 break;
429 case COMMUNITY_LOCAL_AS:
430 strlcat(str, "local-AS", len);
431 if (make_json) {
432 json_string = json_object_new_string("localAs");
433 json_object_array_add(json_community_list,
434 json_string);
435 }
436 break;
437 case COMMUNITY_NO_PEER:
438 strlcat(str, "no-peer", len);
439 if (make_json) {
440 json_string = json_object_new_string("noPeer");
441 json_object_array_add(json_community_list,
442 json_string);
443 }
444 break;
445 default:
446 as = (comval >> 16) & 0xFFFF;
447 val = comval & 0xFFFF;
448 char buf[32];
449 snprintf(buf, sizeof(buf), "%u:%d", as, val);
450 const char *com2alias = bgp_community2alias(buf);
451
452 strlcat(str, com2alias, len);
453 if (make_json) {
454 json_string = json_object_new_string(com2alias);
455 json_object_array_add(json_community_list,
456 json_string);
457 }
458 break;
459 }
460 }
461
462 if (make_json) {
463 json_object_string_add(com->json, "string", str);
464 json_object_object_add(com->json, "list", json_community_list);
465 }
466 com->str = str;
467 }
468
469 /* Intern communities attribute. */
470 struct community *community_intern(struct community *com)
471 {
472 struct community *find;
473
474 /* Assert this community structure is not interned. */
475 assert(com->refcnt == 0);
476
477 /* Lookup community hash. */
478 find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
479
480 /* Arguemnt com is allocated temporary. So when it is not used in
481 hash, it should be freed. */
482 if (find != com)
483 community_free(&com);
484
485 /* Increment refrence counter. */
486 find->refcnt++;
487
488 /* Make string. */
489 if (!find->str)
490 set_community_string(find, false);
491
492 return find;
493 }
494
495 /* Free community attribute. */
496 void community_unintern(struct community **com)
497 {
498 struct community *ret;
499
500 if ((*com)->refcnt)
501 (*com)->refcnt--;
502
503 /* Pull off from hash. */
504 if ((*com)->refcnt == 0) {
505 /* Community value com must exist in hash. */
506 ret = (struct community *)hash_release(comhash, *com);
507 assert(ret != NULL);
508
509 community_free(com);
510 }
511 }
512
513 /* Create new community attribute. */
514 struct community *community_parse(uint32_t *pnt, unsigned short length)
515 {
516 struct community tmp;
517 struct community *new;
518
519 /* If length is malformed return NULL. */
520 if (length % COMMUNITY_SIZE)
521 return NULL;
522
523 /* Make temporary community for hash look up. */
524 tmp.size = length / COMMUNITY_SIZE;
525 tmp.val = pnt;
526
527 new = community_uniq_sort(&tmp);
528
529 return community_intern(new);
530 }
531
532 struct community *community_dup(struct community *com)
533 {
534 struct community *new;
535
536 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
537 new->size = com->size;
538 if (new->size) {
539 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
540 com->size * COMMUNITY_SIZE);
541 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
542 } else
543 new->val = NULL;
544 return new;
545 }
546
547 /* Retrun string representation of communities attribute. */
548 char *community_str(struct community *com, bool make_json)
549 {
550 if (!com)
551 return NULL;
552
553 if (make_json && !com->json && com->str)
554 XFREE(MTYPE_COMMUNITY_STR, com->str);
555
556 if (!com->str)
557 set_community_string(com, make_json);
558 return com->str;
559 }
560
561 /* Make hash value of community attribute. This function is used by
562 hash package.*/
563 unsigned int community_hash_make(const struct community *com)
564 {
565 uint32_t *pnt = com->val;
566
567 return jhash2(pnt, com->size, 0x43ea96c1);
568 }
569
570 bool community_match(const struct community *com1, const struct community *com2)
571 {
572 int i = 0;
573 int j = 0;
574
575 if (com1 == NULL && com2 == NULL)
576 return true;
577
578 if (com1 == NULL || com2 == NULL)
579 return false;
580
581 if (com1->size < com2->size)
582 return false;
583
584 /* Every community on com2 needs to be on com1 for this to match */
585 while (i < com1->size && j < com2->size) {
586 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
587 j++;
588 i++;
589 }
590
591 if (j == com2->size)
592 return true;
593 else
594 return false;
595 }
596
597 /* If two aspath have same value then return 1 else return 0. This
598 function is used by hash package. */
599 bool community_cmp(const struct community *com1, const struct community *com2)
600 {
601 if (com1 == NULL && com2 == NULL)
602 return true;
603 if (com1 == NULL || com2 == NULL)
604 return false;
605
606 if (com1->size == com2->size)
607 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
608 == 0)
609 return true;
610 return false;
611 }
612
613 /* Add com2 to the end of com1. */
614 struct community *community_merge(struct community *com1,
615 struct community *com2)
616 {
617 com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
618 (com1->size + com2->size) * COMMUNITY_SIZE);
619
620 memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
621 com1->size += com2->size;
622
623 return com1;
624 }
625
626 /* Community token enum. */
627 enum community_token {
628 community_token_val,
629 community_token_gshut,
630 community_token_accept_own,
631 community_token_route_filter_translated_v4,
632 community_token_route_filter_v4,
633 community_token_route_filter_translated_v6,
634 community_token_route_filter_v6,
635 community_token_llgr_stale,
636 community_token_no_llgr,
637 community_token_accept_own_nexthop,
638 community_token_blackhole,
639 community_token_no_export,
640 community_token_no_advertise,
641 community_token_local_as,
642 community_token_no_peer,
643 community_token_unknown
644 };
645
646 /* Helper to check if a given community is valid */
647 static bool community_valid(const char *community)
648 {
649 int octets = 0;
650 char **splits;
651 int num;
652 int invalid = 0;
653
654 frrstr_split(community, ":", &splits, &num);
655
656 for (int i = 0; i < num; i++) {
657 if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
658 invalid++;
659
660 if (strlen(splits[i]) == 0)
661 invalid++;
662
663 octets++;
664 XFREE(MTYPE_TMP, splits[i]);
665 }
666 XFREE(MTYPE_TMP, splits);
667
668 return (octets < 2 || invalid) ? false : true;
669 }
670
671 /* Get next community token from string. */
672 static const char *
673 community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
674 {
675 const char *p = buf;
676
677 /* Skip white space. */
678 while (isspace((unsigned char)*p))
679 p++;
680
681 /* Check the end of the line. */
682 if (*p == '\0')
683 return NULL;
684
685 /* Well known community string check. */
686 if (isalpha((unsigned char)*p)) {
687 if (strncmp(p, "internet", strlen("internet")) == 0) {
688 *val = COMMUNITY_INTERNET;
689 *token = community_token_no_export;
690 p += strlen("internet");
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 void community_finish(void)
914 {
915 hash_free(comhash);
916 comhash = NULL;
917 }
918
919 static struct community *bgp_aggr_community_lookup(
920 struct bgp_aggregate *aggregate,
921 struct community *community)
922 {
923 return hash_lookup(aggregate->community_hash, community);
924 }
925
926 static void *bgp_aggr_communty_hash_alloc(void *p)
927 {
928 struct community *ref = (struct community *)p;
929 struct community *community = NULL;
930
931 community = community_dup(ref);
932 return community;
933 }
934
935 static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
936 {
937 struct community *hb_community = hb->data;
938 struct community **aggr_community = arg;
939
940 if (*aggr_community)
941 *aggr_community = community_merge(*aggr_community,
942 hb_community);
943 else
944 *aggr_community = community_dup(hb_community);
945 }
946
947 void bgp_aggr_community_remove(void *arg)
948 {
949 struct community *community = arg;
950
951 community_free(&community);
952 }
953
954 void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
955 struct community *community)
956 {
957 bgp_compute_aggregate_community_hash(aggregate, community);
958 bgp_compute_aggregate_community_val(aggregate);
959 }
960
961
962 void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
963 struct community *community)
964 {
965 struct community *aggr_community = NULL;
966
967 if ((aggregate == NULL) || (community == NULL))
968 return;
969
970 /* Create hash if not already created.
971 */
972 if (aggregate->community_hash == NULL)
973 aggregate->community_hash = hash_create(
974 (unsigned int (*)(const void *))community_hash_make,
975 (bool (*)(const void *, const void *))community_cmp,
976 "BGP Aggregator community hash");
977
978 aggr_community = bgp_aggr_community_lookup(aggregate, community);
979 if (aggr_community == NULL) {
980 /* Insert community into hash.
981 */
982 aggr_community = hash_get(aggregate->community_hash, community,
983 bgp_aggr_communty_hash_alloc);
984 }
985
986 /* Increment reference counter.
987 */
988 aggr_community->refcnt++;
989 }
990
991 void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
992 {
993 struct community *commerge = NULL;
994
995 if (aggregate == NULL)
996 return;
997
998 /* Re-compute aggregate's community.
999 */
1000 if (aggregate->community)
1001 community_free(&aggregate->community);
1002 if (aggregate->community_hash &&
1003 aggregate->community_hash->count) {
1004 hash_iterate(aggregate->community_hash,
1005 bgp_aggr_community_prepare,
1006 &aggregate->community);
1007 commerge = aggregate->community;
1008 aggregate->community = community_uniq_sort(commerge);
1009 if (commerge)
1010 community_free(&commerge);
1011 }
1012 }
1013
1014
1015
1016 void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
1017 struct community *community)
1018 {
1019 struct community *aggr_community = NULL;
1020 struct community *ret_comm = NULL;
1021
1022 if ((!aggregate)
1023 || (!aggregate->community_hash)
1024 || (!community))
1025 return;
1026
1027 /* Look-up the community in the hash.
1028 */
1029 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1030 if (aggr_community) {
1031 aggr_community->refcnt--;
1032
1033 if (aggr_community->refcnt == 0) {
1034 ret_comm = hash_release(aggregate->community_hash,
1035 aggr_community);
1036 community_free(&ret_comm);
1037
1038 bgp_compute_aggregate_community_val(aggregate);
1039 }
1040 }
1041 }
1042
1043 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1044 struct community *community)
1045 {
1046
1047 struct community *aggr_community = NULL;
1048 struct community *ret_comm = NULL;
1049
1050 if ((!aggregate)
1051 || (!aggregate->community_hash)
1052 || (!community))
1053 return;
1054
1055 /* Look-up the community in the hash.
1056 */
1057 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1058 if (aggr_community) {
1059 aggr_community->refcnt--;
1060
1061 if (aggr_community->refcnt == 0) {
1062 ret_comm = hash_release(aggregate->community_hash,
1063 aggr_community);
1064 community_free(&ret_comm);
1065 }
1066 }
1067 }