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