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