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