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