]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_community.c
Merge pull request #8063 from ton31337/fix/typo_enum_bestpath
[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"
0e8916e0 27#include "frrstr.h"
718e3744 28
4a1ab8e4 29#include "bgpd/bgp_memory.h"
718e3744 30#include "bgpd/bgp_community.h"
31
32/* Hash of community attribute. */
730394d9 33static struct hash *comhash;
718e3744 34
35/* Allocate a new communities value. */
d62a17ae 36static struct community *community_new(void)
718e3744 37{
9f5dc319 38 return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
718e3744 39}
40
41/* Free communities value. */
3c1f53de 42void community_free(struct community **com)
718e3744 43{
2c15754e
JC
44 if (!(*com))
45 return;
46
0a22ddfb
QY
47 XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
48 XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
3c1f53de
SMS
49
50 if ((*com)->json) {
51 json_object_free((*com)->json);
52 (*com)->json = NULL;
d62a17ae 53 }
54
3c1f53de 55 XFREE(MTYPE_COMMUNITY, (*com));
718e3744 56}
57
58/* Add one community value to the community. */
2721dd61 59void community_add_val(struct community *com, uint32_t val)
718e3744 60{
d62a17ae 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);
d7c0a89a 69 memcpy(com_lastval(com), &val, sizeof(uint32_t));
718e3744 70}
71
72/* Delete one community. */
d7c0a89a 73void community_del_val(struct community *com, uint32_t *val)
718e3744 74{
d62a17ae 75 int i = 0;
76 int c = 0;
77
78 if (!com->val)
79 return;
80
81 while (i < com->size) {
d7c0a89a 82 if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
d62a17ae 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);
d62a17ae 96 }
97 return;
98 }
99 i++;
718e3744 100 }
718e3744 101}
102
103/* Delete all communities listed in com2 from com1 */
d62a17ae 104struct community *community_delete(struct community *com1,
105 struct community *com2)
718e3744 106{
d62a17ae 107 int i = 0;
718e3744 108
d62a17ae 109 while (i < com2->size) {
110 community_del_val(com1, com2->val + i);
111 i++;
112 }
718e3744 113
d62a17ae 114 return com1;
718e3744 115}
116
117/* Callback function from qsort(). */
d62a17ae 118static int community_compare(const void *a1, const void *a2)
718e3744 119{
d7c0a89a
QY
120 uint32_t v1;
121 uint32_t v2;
d62a17ae 122
d7c0a89a
QY
123 memcpy(&v1, a1, sizeof(uint32_t));
124 memcpy(&v2, a2, sizeof(uint32_t));
d62a17ae 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;
718e3744 133}
134
3dc339cd 135bool community_include(struct community *com, uint32_t val)
718e3744 136{
d62a17ae 137 int i;
718e3744 138
d62a17ae 139 val = htonl(val);
718e3744 140
d62a17ae 141 for (i = 0; i < com->size; i++)
d7c0a89a 142 if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
3dc339cd
DA
143 return true;
144 return false;
718e3744 145}
146
d7c0a89a 147uint32_t community_val_get(struct community *com, int i)
718e3744 148{
d7c0a89a
QY
149 uint8_t *p;
150 uint32_t val;
718e3744 151
d7c0a89a 152 p = (uint8_t *)com->val;
11400e73 153 p += (i * COMMUNITY_SIZE);
718e3744 154
d7c0a89a 155 memcpy(&val, p, sizeof(uint32_t));
718e3744 156
d62a17ae 157 return ntohl(val);
718e3744 158}
159
160/* Sort and uniq given community. */
d62a17ae 161struct community *community_uniq_sort(struct community *com)
718e3744 162{
d62a17ae 163 int i;
164 struct community *new;
d7c0a89a 165 uint32_t val;
d62a17ae 166
167 if (!com)
168 return NULL;
169
170 new = community_new();
d62a17ae 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
d7c0a89a 180 qsort(new->val, new->size, sizeof(uint32_t), community_compare);
d62a17ae 181
182 return new;
718e3744 183}
184
185/* Convert communities attribute to string.
186
187 For Well-known communities value, below keyword is used.
188
d62a17ae 189 0x0 "internet"
aa861c10
C
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"
718e3744 200 0xFFFFFF01 "no-export"
201 0xFFFFFF02 "no-advertise"
202 0xFFFFFF03 "local-AS"
aa861c10 203 0xFFFFFF04 "no-peer"
718e3744 204
205 For other values, "AS:VAL" format is used. */
a69ea8ae 206static void set_community_string(struct community *com, bool make_json)
718e3744 207{
d62a17ae 208 int i;
209 char *str;
d62a17ae 210 int len;
211 int first;
d7c0a89a
QY
212 uint32_t comval;
213 uint16_t as;
214 uint16_t val;
d62a17ae 215 json_object *json_community_list = NULL;
216 json_object *json_string = NULL;
217
218 if (!com)
219 return;
220
a69ea8ae
DS
221 if (make_json) {
222 com->json = json_object_new_object();
223 json_community_list = json_object_new_array();
224 }
d62a17ae 225
226 /* When communities attribute is empty. */
227 if (com->size == 0) {
228 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
229 str[0] = '\0';
230
a69ea8ae
DS
231 if (make_json) {
232 json_object_string_add(com->json, "string", "");
996c9314
LB
233 json_object_object_add(com->json, "list",
234 json_community_list);
a69ea8ae 235 }
d62a17ae 236 com->str = str;
237 return;
718e3744 238 }
d62a17ae 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++) {
d7c0a89a 245 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
d62a17ae 246 comval = ntohl(comval);
247
248 switch (comval) {
249 case COMMUNITY_INTERNET:
250 len += strlen(" internet");
251 break;
aa861c10
C
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;
d62a17ae 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;
aa861c10
C
291 case COMMUNITY_NO_PEER:
292 len += strlen(" no-peer");
7f323236 293 break;
d62a17ae 294 default:
295 len += strlen(" 65536:65535");
296 break;
297 }
298 }
299
300 /* Allocate memory. */
f9bff3be 301 str = XCALLOC(MTYPE_COMMUNITY_STR, len);
d62a17ae 302 first = 1;
303
304 /* Fill in string. */
305 for (i = 0; i < com->size; i++) {
d7c0a89a 306 memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
d62a17ae 307 comval = ntohl(comval);
308
309 if (first)
310 first = 0;
311 else
552d6491 312 strlcat(str, " ", len);
d62a17ae 313
314 switch (comval) {
315 case COMMUNITY_INTERNET:
552d6491 316 strlcat(str, "internet", len);
a69ea8ae 317 if (make_json) {
996c9314
LB
318 json_string =
319 json_object_new_string("internet");
320 json_object_array_add(json_community_list,
321 json_string);
a69ea8ae 322 }
d62a17ae 323 break;
aa861c10 324 case COMMUNITY_GSHUT:
6e0b62b4 325 strlcat(str, "graceful-shutdown", len);
aa861c10
C
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:
6e0b62b4 334 strlcat(str, "accept-own", len);
aa861c10
C
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:
6e0b62b4 343 strlcat(str, "route-filter-translated-v4", len);
aa861c10
C
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:
6e0b62b4 352 strlcat(str, "route-filter-v4", len);
aa861c10
C
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:
6e0b62b4 361 strlcat(str, "route-filter-translated-v6", len);
aa861c10
C
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:
6e0b62b4 370 strlcat(str, "route-filter-v6", len);
aa861c10
C
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:
6e0b62b4 379 strlcat(str, "llgr-stale", len);
aa861c10
C
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:
6e0b62b4 388 strlcat(str, "no-llgr", len);
aa861c10
C
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:
6e0b62b4 397 strlcat(str, "accept-own-nexthop", len);
aa861c10
C
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:
6e0b62b4 406 strlcat(str, "blackhole", len);
aa861c10
C
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;
d62a17ae 414 case COMMUNITY_NO_EXPORT:
6e0b62b4 415 strlcat(str, "no-export", len);
a69ea8ae 416 if (make_json) {
996c9314
LB
417 json_string =
418 json_object_new_string("noExport");
419 json_object_array_add(json_community_list,
420 json_string);
a69ea8ae 421 }
d62a17ae 422 break;
423 case COMMUNITY_NO_ADVERTISE:
6e0b62b4 424 strlcat(str, "no-advertise", len);
a69ea8ae 425 if (make_json) {
996c9314
LB
426 json_string =
427 json_object_new_string("noAdvertise");
428 json_object_array_add(json_community_list,
429 json_string);
a69ea8ae 430 }
d62a17ae 431 break;
432 case COMMUNITY_LOCAL_AS:
6e0b62b4 433 strlcat(str, "local-AS", len);
a69ea8ae
DS
434 if (make_json) {
435 json_string = json_object_new_string("localAs");
996c9314
LB
436 json_object_array_add(json_community_list,
437 json_string);
a69ea8ae 438 }
d62a17ae 439 break;
aa861c10 440 case COMMUNITY_NO_PEER:
6e0b62b4 441 strlcat(str, "no-peer", len);
a69ea8ae 442 if (make_json) {
aa861c10 443 json_string = json_object_new_string("noPeer");
996c9314
LB
444 json_object_array_add(json_community_list,
445 json_string);
a69ea8ae 446 }
7f323236 447 break;
d62a17ae 448 default:
449 as = (comval >> 16) & 0xFFFF;
450 val = comval & 0xFFFF;
6e0b62b4
QY
451 char buf[32];
452 snprintf(buf, sizeof(buf), "%u:%d", as, val);
453 strlcat(str, buf, len);
a69ea8ae 454 if (make_json) {
6e0b62b4 455 json_string = json_object_new_string(buf);
996c9314
LB
456 json_object_array_add(json_community_list,
457 json_string);
a69ea8ae 458 }
d62a17ae 459 break;
460 }
718e3744 461 }
718e3744 462
a69ea8ae
DS
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 }
d62a17ae 467 com->str = str;
718e3744 468}
469
470/* Intern communities attribute. */
d62a17ae 471struct community *community_intern(struct community *com)
718e3744 472{
d62a17ae 473 struct community *find;
718e3744 474
d62a17ae 475 /* Assert this community structure is not interned. */
476 assert(com->refcnt == 0);
718e3744 477
d62a17ae 478 /* Lookup community hash. */
479 find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
718e3744 480
d62a17ae 481 /* Arguemnt com is allocated temporary. So when it is not used in
482 hash, it should be freed. */
483 if (find != com)
3c1f53de 484 community_free(&com);
718e3744 485
d62a17ae 486 /* Increment refrence counter. */
487 find->refcnt++;
718e3744 488
d62a17ae 489 /* Make string. */
490 if (!find->str)
a69ea8ae 491 set_community_string(find, false);
718e3744 492
d62a17ae 493 return find;
718e3744 494}
495
496/* Free community attribute. */
d62a17ae 497void community_unintern(struct community **com)
718e3744 498{
d62a17ae 499 struct community *ret;
718e3744 500
d62a17ae 501 if ((*com)->refcnt)
502 (*com)->refcnt--;
718e3744 503
d62a17ae 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);
718e3744 509
3c1f53de 510 community_free(com);
d62a17ae 511 }
718e3744 512}
513
514/* Create new community attribute. */
d7c0a89a 515struct community *community_parse(uint32_t *pnt, unsigned short length)
718e3744 516{
d62a17ae 517 struct community tmp;
518 struct community *new;
718e3744 519
d62a17ae 520 /* If length is malformed return NULL. */
11400e73 521 if (length % COMMUNITY_SIZE)
d62a17ae 522 return NULL;
718e3744 523
d62a17ae 524 /* Make temporary community for hash look up. */
11400e73 525 tmp.size = length / COMMUNITY_SIZE;
d62a17ae 526 tmp.val = pnt;
718e3744 527
d62a17ae 528 new = community_uniq_sort(&tmp);
718e3744 529
d62a17ae 530 return community_intern(new);
718e3744 531}
532
d62a17ae 533struct community *community_dup(struct community *com)
718e3744 534{
d62a17ae 535 struct community *new;
536
537 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
538 new->size = com->size;
539 if (new->size) {
11400e73
DA
540 new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
541 com->size * COMMUNITY_SIZE);
542 memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
d62a17ae 543 } else
544 new->val = NULL;
545 return new;
718e3744 546}
547
548/* Retrun string representation of communities attribute. */
a69ea8ae 549char *community_str(struct community *com, bool make_json)
718e3744 550{
d62a17ae 551 if (!com)
552 return NULL;
f1aa5d8a 553
a69ea8ae
DS
554 if (make_json && !com->json && com->str)
555 XFREE(MTYPE_COMMUNITY_STR, com->str);
556
d62a17ae 557 if (!com->str)
a69ea8ae 558 set_community_string(com, make_json);
d62a17ae 559 return com->str;
718e3744 560}
561
562/* Make hash value of community attribute. This function is used by
563 hash package.*/
d8b87afe 564unsigned int community_hash_make(const struct community *com)
718e3744 565{
c4efd0f4 566 uint32_t *pnt = com->val;
d62a17ae 567
3f65c5b1 568 return jhash2(pnt, com->size, 0x43ea96c1);
718e3744 569}
570
3dc339cd 571bool community_match(const struct community *com1, const struct community *com2)
718e3744 572{
d62a17ae 573 int i = 0;
574 int j = 0;
575
576 if (com1 == NULL && com2 == NULL)
3dc339cd 577 return true;
d62a17ae 578
579 if (com1 == NULL || com2 == NULL)
3dc339cd 580 return false;
d62a17ae 581
582 if (com1->size < com2->size)
3dc339cd 583 return false;
d62a17ae 584
585 /* Every community on com2 needs to be on com1 for this to match */
586 while (i < com1->size && j < com2->size) {
d7c0a89a 587 if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
d62a17ae 588 j++;
589 i++;
590 }
591
592 if (j == com2->size)
3dc339cd 593 return true;
d62a17ae 594 else
3dc339cd 595 return false;
718e3744 596}
597
598/* If two aspath have same value then return 1 else return 0. This
599 function is used by hash package. */
74df8d6d 600bool community_cmp(const struct community *com1, const struct community *com2)
718e3744 601{
d62a17ae 602 if (com1 == NULL && com2 == NULL)
74df8d6d 603 return true;
d62a17ae 604 if (com1 == NULL || com2 == NULL)
74df8d6d 605 return false;
d62a17ae 606
607 if (com1->size == com2->size)
11400e73
DA
608 if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
609 == 0)
74df8d6d
DS
610 return true;
611 return false;
718e3744 612}
613
614/* Add com2 to the end of com1. */
d62a17ae 615struct community *community_merge(struct community *com1,
616 struct community *com2)
718e3744 617{
d62a17ae 618 if (com1->val)
11400e73
DA
619 com1->val =
620 XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
621 (com1->size + com2->size) * COMMUNITY_SIZE);
d62a17ae 622 else
623 com1->val = XMALLOC(MTYPE_COMMUNITY_VAL,
11400e73 624 (com1->size + com2->size) * COMMUNITY_SIZE);
718e3744 625
11400e73 626 memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
d62a17ae 627 com1->size += com2->size;
718e3744 628
d62a17ae 629 return com1;
718e3744 630}
631
632/* Community token enum. */
d62a17ae 633enum community_token {
634 community_token_val,
aa861c10
C
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,
d62a17ae 645 community_token_no_export,
646 community_token_no_advertise,
647 community_token_local_as,
aa861c10 648 community_token_no_peer,
d62a17ae 649 community_token_unknown
718e3744 650};
651
0e8916e0
DA
652/* Helper to check if a given community is valid */
653static 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
718e3744 677/* Get next community token from string. */
94f2b392 678static const char *
d7c0a89a 679community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
718e3744 680{
d62a17ae 681 const char *p = buf;
682
683 /* Skip white space. */
fefa5e0f 684 while (isspace((unsigned char)*p))
d62a17ae 685 p++;
686
687 /* Check the end of the line. */
688 if (*p == '\0')
689 return NULL;
690
691 /* Well known community string check. */
fefa5e0f 692 if (isalpha((unsigned char)*p)) {
d62a17ae 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 }
aa861c10
C
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 }
68f36a94
AJ
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 }
aa861c10
C
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 }
aa861c10
C
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 }
d62a17ae 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 }
aa861c10
C
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");
7f323236
DW
794 return p;
795 }
d62a17ae 796
797 /* Unknown string. */
798 *token = community_token_unknown;
799 return NULL;
718e3744 800 }
801
d62a17ae 802 /* Community value. */
fefa5e0f 803 if (isdigit((unsigned char)*p)) {
d62a17ae 804 int separator = 0;
805 int digit = 0;
d7c0a89a
QY
806 uint32_t community_low = 0;
807 uint32_t community_high = 0;
d62a17ae 808
0e8916e0
DA
809 if (!community_valid(p)) {
810 *token = community_token_unknown;
811 return NULL;
812 }
813
fefa5e0f 814 while (isdigit((unsigned char)*p) || *p == ':') {
d62a17ae 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++;
718e3744 838 }
d62a17ae 839 if (!digit) {
840 *token = community_token_unknown;
841 return NULL;
842 }
843
d62a17ae 844 *val = community_high + community_low;
845 *token = community_token_val;
846 return p;
847 }
848 *token = community_token_unknown;
849 return NULL;
718e3744 850}
851
852/* convert string to community structure */
d62a17ae 853struct community *community_str2com(const char *str)
718e3744 854{
d62a17ae 855 struct community *com = NULL;
856 struct community *com_sort = NULL;
d7c0a89a 857 uint32_t val = 0;
d62a17ae 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:
aa861c10
C
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:
d62a17ae 875 case community_token_no_export:
876 case community_token_no_advertise:
877 case community_token_local_as:
aa861c10 878 case community_token_no_peer:
d62a17ae 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:
d62a17ae 886 if (com)
3c1f53de 887 community_free(&com);
d62a17ae 888 return NULL;
889 }
890 } while (str);
891
d62a17ae 892 com_sort = community_uniq_sort(com);
3c1f53de 893 community_free(&com);
718e3744 894
d62a17ae 895 return com_sort;
718e3744 896}
897
898/* Return communities hash entry count. */
d62a17ae 899unsigned long community_count(void)
718e3744 900{
d62a17ae 901 return comhash->count;
718e3744 902}
903
904/* Return communities hash. */
d62a17ae 905struct hash *community_hash(void)
718e3744 906{
d62a17ae 907 return comhash;
718e3744 908}
909
910/* Initialize comminity related hash. */
d62a17ae 911void community_init(void)
718e3744 912{
996c9314 913 comhash =
d8b87afe 914 hash_create((unsigned int (*)(const void *))community_hash_make,
74df8d6d 915 (bool (*)(const void *, const void *))community_cmp,
996c9314 916 "BGP Community Hash");
718e3744 917}
228da428 918
d62a17ae 919void community_finish(void)
228da428 920{
d62a17ae 921 hash_free(comhash);
922 comhash = NULL;
228da428 923}
c0d7a6cc
NT
924
925static 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
932static 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
7f5818fb 941static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
c0d7a6cc 942{
c0d7a6cc
NT
943 struct community *hb_community = hb->data;
944 struct community **aggr_community = arg;
945
21fec674 946 if (*aggr_community)
947 *aggr_community = community_merge(*aggr_community,
948 hb_community);
949 else
c0d7a6cc
NT
950 *aggr_community = community_dup(hb_community);
951}
952
953void bgp_aggr_community_remove(void *arg)
954{
955 struct community *community = arg;
956
957 community_free(&community);
958}
959
960void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
961 struct community *community)
21fec674 962{
963 bgp_compute_aggregate_community_hash(aggregate, community);
964 bgp_compute_aggregate_community_val(aggregate);
965}
966
967
968void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
969 struct community *community)
c0d7a6cc
NT
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(
d8b87afe 980 (unsigned int (*)(const void *))community_hash_make,
c0d7a6cc
NT
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);
21fec674 990 }
c0d7a6cc 991
21fec674 992 /* Increment reference counter.
993 */
994 aggr_community->refcnt++;
995}
c0d7a6cc 996
21fec674 997void 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) {
c0d7a6cc
NT
1010 hash_iterate(aggregate->community_hash,
1011 bgp_aggr_community_prepare,
1012 &aggregate->community);
21fec674 1013 commerge = aggregate->community;
1014 aggregate->community = community_uniq_sort(commerge);
1015 if (commerge)
1016 community_free(&commerge);
c0d7a6cc 1017 }
c0d7a6cc
NT
1018}
1019
21fec674 1020
1021
c0d7a6cc
NT
1022void 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
21fec674 1028 if ((!aggregate)
1029 || (!aggregate->community_hash)
1030 || (!community))
c0d7a6cc
NT
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
21fec674 1044 bgp_compute_aggregate_community_val(aggregate);
1045 }
1046 }
1047}
1048
1049void 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;
c0d7a6cc 1055
21fec674 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);
c0d7a6cc
NT
1071 }
1072 }
1073}