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