]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_community.c
Merge pull request #12642 from anlancs/fix/bgpd-debug-name
[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 bool translate_alias)
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 = BUFSIZ;
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 const char *com2alias =
452 translate_alias ? bgp_community2alias(buf)
453 : buf;
454
455 strlcat(str, com2alias, len);
456 if (make_json) {
457 json_string = json_object_new_string(com2alias);
458 json_object_array_add(json_community_list,
459 json_string);
460 }
461 break;
462 }
463 }
464
465 if (make_json) {
466 json_object_string_add(com->json, "string", str);
467 json_object_object_add(com->json, "list", json_community_list);
468 }
469 com->str = str;
470 }
471
472 /* Intern communities attribute. */
473 struct community *community_intern(struct community *com)
474 {
475 struct community *find;
476
477 /* Assert this community structure is not interned. */
478 assert(com->refcnt == 0);
479
480 /* Lookup community hash. */
481 find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
482
483 /* Arguemnt com is allocated temporary. So when it is not used in
484 hash, it should be freed. */
485 if (find != com)
486 community_free(&com);
487
488 /* Increment refrence counter. */
489 find->refcnt++;
490
491 /* Make string. */
492 if (!find->str)
493 set_community_string(find, false, true);
494
495 return find;
496 }
497
498 /* Free community attribute. */
499 void community_unintern(struct community **com)
500 {
501 struct community *ret;
502
503 if (!*com)
504 return;
505
506 if ((*com)->refcnt)
507 (*com)->refcnt--;
508
509 /* Pull off from hash. */
510 if ((*com)->refcnt == 0) {
511 /* Community value com must exist in hash. */
512 ret = (struct community *)hash_release(comhash, *com);
513 assert(ret != NULL);
514
515 community_free(com);
516 }
517 }
518
519 /* Create new community attribute. */
520 struct community *community_parse(uint32_t *pnt, unsigned short length)
521 {
522 struct community tmp;
523 struct community *new;
524
525 /* If length is malformed return NULL. */
526 if (length % COMMUNITY_SIZE)
527 return NULL;
528
529 /* Make temporary community for hash look up. */
530 tmp.size = length / COMMUNITY_SIZE;
531 tmp.val = pnt;
532
533 new = community_uniq_sort(&tmp);
534
535 return community_intern(new);
536 }
537
538 struct community *community_dup(struct community *com)
539 {
540 struct community *new;
541
542 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
543 new->size = com->size;
544 if (new->size) {
545 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
546 com->size * COMMUNITY_SIZE);
547 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
548 } else
549 new->val = NULL;
550 return new;
551 }
552
553 /* Return string representation of communities attribute. */
554 char *community_str(struct community *com, bool make_json, bool translate_alias)
555 {
556 if (!com)
557 return NULL;
558
559 if (make_json && !com->json && com->str)
560 XFREE(MTYPE_COMMUNITY_STR, com->str);
561
562 if (!com->str)
563 set_community_string(com, make_json, translate_alias);
564 return com->str;
565 }
566
567 /* Make hash value of community attribute. This function is used by
568 hash package.*/
569 unsigned int community_hash_make(const struct community *com)
570 {
571 uint32_t *pnt = com->val;
572
573 return jhash2(pnt, com->size, 0x43ea96c1);
574 }
575
576 bool community_match(const struct community *com1, const struct community *com2)
577 {
578 int i = 0;
579 int j = 0;
580
581 if (com1 == NULL && com2 == NULL)
582 return true;
583
584 if (com1 == NULL || com2 == NULL)
585 return false;
586
587 if (com1->size < com2->size)
588 return false;
589
590 /* Every community on com2 needs to be on com1 for this to match */
591 while (i < com1->size && j < com2->size) {
592 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
593 j++;
594 i++;
595 }
596
597 if (j == com2->size)
598 return true;
599 else
600 return false;
601 }
602
603 bool community_cmp(const struct community *com1, const struct community *com2)
604 {
605 if (com1 == NULL && com2 == NULL)
606 return true;
607 if (com1 == NULL || com2 == NULL)
608 return false;
609
610 if (com1->size == com2->size)
611 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
612 == 0)
613 return true;
614 return false;
615 }
616
617 /* Add com2 to the end of com1. */
618 struct community *community_merge(struct community *com1,
619 struct community *com2)
620 {
621 com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
622 (com1->size + com2->size) * COMMUNITY_SIZE);
623
624 memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
625 com1->size += com2->size;
626
627 return com1;
628 }
629
630 /* Community token enum. */
631 enum community_token {
632 community_token_val,
633 community_token_gshut,
634 community_token_accept_own,
635 community_token_route_filter_translated_v4,
636 community_token_route_filter_v4,
637 community_token_route_filter_translated_v6,
638 community_token_route_filter_v6,
639 community_token_llgr_stale,
640 community_token_no_llgr,
641 community_token_accept_own_nexthop,
642 community_token_blackhole,
643 community_token_no_export,
644 community_token_no_advertise,
645 community_token_local_as,
646 community_token_no_peer,
647 community_token_unknown
648 };
649
650 /* Helper to check if a given community is valid */
651 static bool community_valid(const char *community)
652 {
653 int octets = 0;
654 char **splits;
655 int num;
656 int invalid = 0;
657
658 frrstr_split(community, ":", &splits, &num);
659
660 for (int i = 0; i < num; i++) {
661 if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
662 invalid++;
663
664 if (strlen(splits[i]) == 0)
665 invalid++;
666
667 octets++;
668 XFREE(MTYPE_TMP, splits[i]);
669 }
670 XFREE(MTYPE_TMP, splits);
671
672 return (octets < 2 || invalid) ? false : true;
673 }
674
675 /* Get next community token from string. */
676 static const char *
677 community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
678 {
679 const char *p = buf;
680
681 /* Skip white space. */
682 while (isspace((unsigned char)*p))
683 p++;
684
685 /* Check the end of the line. */
686 if (*p == '\0')
687 return NULL;
688
689 /* Well known community string check. */
690 if (isalpha((unsigned char)*p)) {
691 if (strncmp(p, "internet", strlen("internet")) == 0) {
692 *val = COMMUNITY_INTERNET;
693 *token = community_token_no_export;
694 p += strlen("internet");
695 return p;
696 }
697 if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
698 == 0) {
699 *val = COMMUNITY_GSHUT;
700 *token = community_token_gshut;
701 p += strlen("graceful-shutdown");
702 return p;
703 }
704 if (strncmp(p, "accept-own-nexthop",
705 strlen("accept-own-nexthop"))
706 == 0) {
707 *val = COMMUNITY_ACCEPT_OWN_NEXTHOP;
708 *token = community_token_accept_own_nexthop;
709 p += strlen("accept-own-nexthop");
710 return p;
711 }
712 if (strncmp(p, "accept-own", strlen("accept-own"))
713 == 0) {
714 *val = COMMUNITY_ACCEPT_OWN;
715 *token = community_token_accept_own;
716 p += strlen("accept-own");
717 return p;
718 }
719 if (strncmp(p, "route-filter-translated-v4",
720 strlen("route-filter-translated-v4"))
721 == 0) {
722 *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v4;
723 *token = community_token_route_filter_translated_v4;
724 p += strlen("route-filter-translated-v4");
725 return p;
726 }
727 if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
728 == 0) {
729 *val = COMMUNITY_ROUTE_FILTER_v4;
730 *token = community_token_route_filter_v4;
731 p += strlen("route-filter-v4");
732 return p;
733 }
734 if (strncmp(p, "route-filter-translated-v6",
735 strlen("route-filter-translated-v6"))
736 == 0) {
737 *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v6;
738 *token = community_token_route_filter_translated_v6;
739 p += strlen("route-filter-translated-v6");
740 return p;
741 }
742 if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
743 == 0) {
744 *val = COMMUNITY_ROUTE_FILTER_v6;
745 *token = community_token_route_filter_v6;
746 p += strlen("route-filter-v6");
747 return p;
748 }
749 if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
750 == 0) {
751 *val = COMMUNITY_LLGR_STALE;
752 *token = community_token_llgr_stale;
753 p += strlen("llgr-stale");
754 return p;
755 }
756 if (strncmp(p, "no-llgr", strlen("no-llgr"))
757 == 0) {
758 *val = COMMUNITY_NO_LLGR;
759 *token = community_token_no_llgr;
760 p += strlen("no-llgr");
761 return p;
762 }
763 if (strncmp(p, "blackhole", strlen("blackhole"))
764 == 0) {
765 *val = COMMUNITY_BLACKHOLE;
766 *token = community_token_blackhole;
767 p += strlen("blackhole");
768 return p;
769 }
770 if (strncmp(p, "no-export", strlen("no-export")) == 0) {
771 *val = COMMUNITY_NO_EXPORT;
772 *token = community_token_no_export;
773 p += strlen("no-export");
774 return p;
775 }
776 if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
777 *val = COMMUNITY_NO_ADVERTISE;
778 *token = community_token_no_advertise;
779 p += strlen("no-advertise");
780 return p;
781 }
782 if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
783 *val = COMMUNITY_LOCAL_AS;
784 *token = community_token_local_as;
785 p += strlen("local-AS");
786 return p;
787 }
788 if (strncmp(p, "no-peer", strlen("no-peer")) == 0) {
789 *val = COMMUNITY_NO_PEER;
790 *token = community_token_no_peer;
791 p += strlen("no-peer");
792 return p;
793 }
794
795 /* Unknown string. */
796 *token = community_token_unknown;
797 return NULL;
798 }
799
800 /* Community value. */
801 if (isdigit((unsigned char)*p)) {
802 int separator = 0;
803 int digit = 0;
804 uint32_t community_low = 0;
805 uint32_t community_high = 0;
806
807 if (!community_valid(p)) {
808 *token = community_token_unknown;
809 return NULL;
810 }
811
812 while (isdigit((unsigned char)*p) || *p == ':') {
813 if (*p == ':') {
814 if (separator) {
815 *token = community_token_unknown;
816 return NULL;
817 } else {
818 separator = 1;
819 digit = 0;
820
821 if (community_low > UINT16_MAX) {
822 *token =
823 community_token_unknown;
824 return NULL;
825 }
826
827 community_high = community_low << 16;
828 community_low = 0;
829 }
830 } else {
831 digit = 1;
832 community_low *= 10;
833 community_low += (*p - '0');
834 }
835 p++;
836 }
837 if (!digit) {
838 *token = community_token_unknown;
839 return NULL;
840 }
841
842 *val = community_high + community_low;
843 *token = community_token_val;
844 return p;
845 }
846 *token = community_token_unknown;
847 return NULL;
848 }
849
850 /* convert string to community structure */
851 struct community *community_str2com(const char *str)
852 {
853 struct community *com = NULL;
854 struct community *com_sort = NULL;
855 uint32_t val = 0;
856 enum community_token token = community_token_unknown;
857
858 do {
859 str = community_gettoken(str, &token, &val);
860
861 switch (token) {
862 case community_token_val:
863 case community_token_gshut:
864 case community_token_accept_own:
865 case community_token_route_filter_translated_v4:
866 case community_token_route_filter_v4:
867 case community_token_route_filter_translated_v6:
868 case community_token_route_filter_v6:
869 case community_token_llgr_stale:
870 case community_token_no_llgr:
871 case community_token_accept_own_nexthop:
872 case community_token_blackhole:
873 case community_token_no_export:
874 case community_token_no_advertise:
875 case community_token_local_as:
876 case community_token_no_peer:
877 if (com == NULL) {
878 com = community_new();
879 com->json = NULL;
880 }
881 community_add_val(com, val);
882 break;
883 case community_token_unknown:
884 if (com)
885 community_free(&com);
886 return NULL;
887 }
888 } while (str);
889
890 com_sort = community_uniq_sort(com);
891 community_free(&com);
892
893 return com_sort;
894 }
895
896 /* Return communities hash entry count. */
897 unsigned long community_count(void)
898 {
899 return comhash->count;
900 }
901
902 /* Return communities hash. */
903 struct hash *community_hash(void)
904 {
905 return comhash;
906 }
907
908 /* Initialize comminity related hash. */
909 void community_init(void)
910 {
911 comhash =
912 hash_create((unsigned int (*)(const void *))community_hash_make,
913 (bool (*)(const void *, const void *))community_cmp,
914 "BGP Community Hash");
915 }
916
917 static void community_hash_free(void *data)
918 {
919 struct community *com = data;
920
921 community_free(&com);
922 }
923
924 void community_finish(void)
925 {
926 hash_clean(comhash, community_hash_free);
927 hash_free(comhash);
928 comhash = NULL;
929 }
930
931 static struct community *bgp_aggr_community_lookup(
932 struct bgp_aggregate *aggregate,
933 struct community *community)
934 {
935 return hash_lookup(aggregate->community_hash, community);
936 }
937
938 static void *bgp_aggr_community_hash_alloc(void *p)
939 {
940 struct community *ref = (struct community *)p;
941 struct community *community = NULL;
942
943 community = community_dup(ref);
944 return community;
945 }
946
947 static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
948 {
949 struct community *hb_community = hb->data;
950 struct community **aggr_community = arg;
951
952 if (*aggr_community)
953 *aggr_community = community_merge(*aggr_community,
954 hb_community);
955 else
956 *aggr_community = community_dup(hb_community);
957 }
958
959 void bgp_aggr_community_remove(void *arg)
960 {
961 struct community *community = arg;
962
963 community_free(&community);
964 }
965
966 void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
967 struct community *community)
968 {
969 bgp_compute_aggregate_community_hash(aggregate, community);
970 bgp_compute_aggregate_community_val(aggregate);
971 }
972
973
974 void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
975 struct community *community)
976 {
977 struct community *aggr_community = NULL;
978
979 if ((aggregate == NULL) || (community == NULL))
980 return;
981
982 /* Create hash if not already created.
983 */
984 if (aggregate->community_hash == NULL)
985 aggregate->community_hash = hash_create(
986 (unsigned int (*)(const void *))community_hash_make,
987 (bool (*)(const void *, const void *))community_cmp,
988 "BGP Aggregator community hash");
989
990 aggr_community = bgp_aggr_community_lookup(aggregate, community);
991 if (aggr_community == NULL) {
992 /* Insert community into hash.
993 */
994 aggr_community = hash_get(aggregate->community_hash, community,
995 bgp_aggr_community_hash_alloc);
996 }
997
998 /* Increment reference counter.
999 */
1000 aggr_community->refcnt++;
1001 }
1002
1003 void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
1004 {
1005 struct community *commerge = NULL;
1006
1007 if (aggregate == NULL)
1008 return;
1009
1010 /* Re-compute aggregate's community.
1011 */
1012 if (aggregate->community)
1013 community_free(&aggregate->community);
1014 if (aggregate->community_hash &&
1015 aggregate->community_hash->count) {
1016 hash_iterate(aggregate->community_hash,
1017 bgp_aggr_community_prepare,
1018 &aggregate->community);
1019 commerge = aggregate->community;
1020 aggregate->community = community_uniq_sort(commerge);
1021 if (commerge)
1022 community_free(&commerge);
1023 }
1024 }
1025
1026
1027
1028 void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
1029 struct community *community)
1030 {
1031 struct community *aggr_community = NULL;
1032 struct community *ret_comm = NULL;
1033
1034 if ((!aggregate)
1035 || (!aggregate->community_hash)
1036 || (!community))
1037 return;
1038
1039 /* Look-up the community in the hash.
1040 */
1041 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1042 if (aggr_community) {
1043 aggr_community->refcnt--;
1044
1045 if (aggr_community->refcnt == 0) {
1046 ret_comm = hash_release(aggregate->community_hash,
1047 aggr_community);
1048 community_free(&ret_comm);
1049
1050 bgp_compute_aggregate_community_val(aggregate);
1051 }
1052 }
1053 }
1054
1055 void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1056 struct community *community)
1057 {
1058
1059 struct community *aggr_community = NULL;
1060 struct community *ret_comm = NULL;
1061
1062 if ((!aggregate)
1063 || (!aggregate->community_hash)
1064 || (!community))
1065 return;
1066
1067 /* Look-up the community in the hash.
1068 */
1069 aggr_community = bgp_aggr_community_lookup(aggregate, community);
1070 if (aggr_community) {
1071 aggr_community->refcnt--;
1072
1073 if (aggr_community->refcnt == 0) {
1074 ret_comm = hash_release(aggregate->community_hash,
1075 aggr_community);
1076 community_free(&ret_comm);
1077 }
1078 }
1079 }