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