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