]> git.proxmox.com Git - mirror_frr.git/blame - bgpd/bgp_community.c
bgpd: implement draft-ietf-grow-bgp-gshut-10
[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"
26
4a1ab8e4 27#include "bgpd/bgp_memory.h"
718e3744 28#include "bgpd/bgp_community.h"
29
30/* Hash of community attribute. */
730394d9 31static struct hash *comhash;
718e3744 32
33/* Allocate a new communities value. */
d62a17ae 34static struct community *community_new(void)
718e3744 35{
d62a17ae 36 return (struct community *)XCALLOC(MTYPE_COMMUNITY,
37 sizeof(struct community));
718e3744 38}
39
40/* Free communities value. */
d62a17ae 41void community_free(struct community *com)
718e3744 42{
d62a17ae 43 if (com->val)
44 XFREE(MTYPE_COMMUNITY_VAL, com->val);
45 if (com->str)
46 XFREE(MTYPE_COMMUNITY_STR, com->str);
47
48 if (com->json) {
49 json_object_free(com->json);
50 com->json = NULL;
51 }
52
53 XFREE(MTYPE_COMMUNITY, com);
718e3744 54}
55
56/* Add one community value to the community. */
d62a17ae 57static void community_add_val(struct community *com, u_int32_t val)
718e3744 58{
d62a17ae 59 com->size++;
60 if (com->val)
61 com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val,
62 com_length(com));
63 else
64 com->val = XMALLOC(MTYPE_COMMUNITY_VAL, com_length(com));
65
66 val = htonl(val);
67 memcpy(com_lastval(com), &val, sizeof(u_int32_t));
718e3744 68}
69
70/* Delete one community. */
d62a17ae 71void community_del_val(struct community *com, u_int32_t *val)
718e3744 72{
d62a17ae 73 int i = 0;
74 int c = 0;
75
76 if (!com->val)
77 return;
78
79 while (i < com->size) {
80 if (memcmp(com->val + i, val, sizeof(u_int32_t)) == 0) {
81 c = com->size - i - 1;
82
83 if (c > 0)
84 memmove(com->val + i, com->val + (i + 1),
85 c * sizeof(*val));
86
87 com->size--;
88
89 if (com->size > 0)
90 com->val = XREALLOC(MTYPE_COMMUNITY_VAL,
91 com->val, com_length(com));
92 else {
93 XFREE(MTYPE_COMMUNITY_VAL, com->val);
94 com->val = NULL;
95 }
96 return;
97 }
98 i++;
718e3744 99 }
718e3744 100}
101
102/* Delete all communities listed in com2 from com1 */
d62a17ae 103struct community *community_delete(struct community *com1,
104 struct community *com2)
718e3744 105{
d62a17ae 106 int i = 0;
718e3744 107
d62a17ae 108 while (i < com2->size) {
109 community_del_val(com1, com2->val + i);
110 i++;
111 }
718e3744 112
d62a17ae 113 return com1;
718e3744 114}
115
116/* Callback function from qsort(). */
d62a17ae 117static int community_compare(const void *a1, const void *a2)
718e3744 118{
d62a17ae 119 u_int32_t v1;
120 u_int32_t v2;
121
122 memcpy(&v1, a1, sizeof(u_int32_t));
123 memcpy(&v2, a2, sizeof(u_int32_t));
124 v1 = ntohl(v1);
125 v2 = ntohl(v2);
126
127 if (v1 < v2)
128 return -1;
129 if (v1 > v2)
130 return 1;
131 return 0;
718e3744 132}
133
d62a17ae 134int community_include(struct community *com, u_int32_t val)
718e3744 135{
d62a17ae 136 int i;
718e3744 137
d62a17ae 138 val = htonl(val);
718e3744 139
d62a17ae 140 for (i = 0; i < com->size; i++)
141 if (memcmp(&val, com_nthval(com, i), sizeof(u_int32_t)) == 0)
142 return 1;
718e3744 143
d62a17ae 144 return 0;
718e3744 145}
146
d62a17ae 147u_int32_t community_val_get(struct community *com, int i)
718e3744 148{
d62a17ae 149 u_char *p;
150 u_int32_t val;
718e3744 151
d62a17ae 152 p = (u_char *)com->val;
153 p += (i * 4);
718e3744 154
d62a17ae 155 memcpy(&val, p, sizeof(u_int32_t));
718e3744 156
d62a17ae 157 return ntohl(val);
718e3744 158}
159
160/* Sort and uniq given community. */
d62a17ae 161struct community *community_uniq_sort(struct community *com)
718e3744 162{
d62a17ae 163 int i;
164 struct community *new;
165 u_int32_t val;
166
167 if (!com)
168 return NULL;
169
170 new = community_new();
171 ;
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
181 qsort(new->val, new->size, sizeof(u_int32_t), community_compare);
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"
718e3744 191 0xFFFFFF01 "no-export"
192 0xFFFFFF02 "no-advertise"
193 0xFFFFFF03 "local-AS"
7f323236 194 0xFFFF0000 "graceful-shutdown"
718e3744 195
196 For other values, "AS:VAL" format is used. */
d62a17ae 197static void set_community_string(struct community *com)
718e3744 198{
d62a17ae 199 int i;
200 char *str;
201 char *pnt;
202 int len;
203 int first;
204 u_int32_t comval;
205 u_int16_t as;
206 u_int16_t val;
207 json_object *json_community_list = NULL;
208 json_object *json_string = NULL;
209
210 if (!com)
211 return;
212
213 com->json = json_object_new_object();
214 json_community_list = json_object_new_array();
215
216 /* When communities attribute is empty. */
217 if (com->size == 0) {
218 str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
219 str[0] = '\0';
220
221 json_object_string_add(com->json, "string", "");
222 json_object_object_add(com->json, "list", json_community_list);
223 com->str = str;
224 return;
718e3744 225 }
d62a17ae 226
227 /* Memory allocation is time consuming work. So we calculate
228 required string length first. */
229 len = 0;
230
231 for (i = 0; i < com->size; i++) {
232 memcpy(&comval, com_nthval(com, i), sizeof(u_int32_t));
233 comval = ntohl(comval);
234
235 switch (comval) {
236 case COMMUNITY_INTERNET:
237 len += strlen(" internet");
238 break;
239 case COMMUNITY_NO_EXPORT:
240 len += strlen(" no-export");
241 break;
242 case COMMUNITY_NO_ADVERTISE:
243 len += strlen(" no-advertise");
244 break;
245 case COMMUNITY_LOCAL_AS:
246 len += strlen(" local-AS");
247 break;
7f323236
DW
248 case COMMUNITY_GSHUT:
249 len += strlen(" graceful-shutdown");
250 break;
d62a17ae 251 default:
252 len += strlen(" 65536:65535");
253 break;
254 }
255 }
256
257 /* Allocate memory. */
258 str = pnt = XMALLOC(MTYPE_COMMUNITY_STR, len);
259 first = 1;
260
261 /* Fill in string. */
262 for (i = 0; i < com->size; i++) {
263 memcpy(&comval, com_nthval(com, i), sizeof(u_int32_t));
264 comval = ntohl(comval);
265
266 if (first)
267 first = 0;
268 else
269 *pnt++ = ' ';
270
271 switch (comval) {
272 case COMMUNITY_INTERNET:
273 strcpy(pnt, "internet");
274 pnt += strlen("internet");
275 json_string = json_object_new_string("internet");
276 json_object_array_add(json_community_list, json_string);
277 break;
278 case COMMUNITY_NO_EXPORT:
279 strcpy(pnt, "no-export");
280 pnt += strlen("no-export");
281 json_string = json_object_new_string("noExport");
282 json_object_array_add(json_community_list, json_string);
283 break;
284 case COMMUNITY_NO_ADVERTISE:
285 strcpy(pnt, "no-advertise");
286 pnt += strlen("no-advertise");
287 json_string = json_object_new_string("noAdvertise");
288 json_object_array_add(json_community_list, json_string);
289 break;
290 case COMMUNITY_LOCAL_AS:
291 strcpy(pnt, "local-AS");
292 pnt += strlen("local-AS");
293 json_string = json_object_new_string("localAs");
294 json_object_array_add(json_community_list, json_string);
295 break;
7f323236
DW
296 case COMMUNITY_GSHUT:
297 strcpy(pnt, "graceful-shutdown");
298 pnt += strlen("graceful-shutdown");
299 json_string = json_object_new_string("gracefulShutdown");
300 json_object_array_add(json_community_list, json_string);
301 break;
d62a17ae 302 default:
303 as = (comval >> 16) & 0xFFFF;
304 val = comval & 0xFFFF;
305 sprintf(pnt, "%u:%d", as, val);
306 json_string = json_object_new_string(pnt);
307 json_object_array_add(json_community_list, json_string);
308 pnt += strlen(pnt);
309 break;
310 }
718e3744 311 }
d62a17ae 312 *pnt = '\0';
718e3744 313
d62a17ae 314 json_object_string_add(com->json, "string", str);
315 json_object_object_add(com->json, "list", json_community_list);
316 com->str = str;
718e3744 317}
318
319/* Intern communities attribute. */
d62a17ae 320struct community *community_intern(struct community *com)
718e3744 321{
d62a17ae 322 struct community *find;
718e3744 323
d62a17ae 324 /* Assert this community structure is not interned. */
325 assert(com->refcnt == 0);
718e3744 326
d62a17ae 327 /* Lookup community hash. */
328 find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
718e3744 329
d62a17ae 330 /* Arguemnt com is allocated temporary. So when it is not used in
331 hash, it should be freed. */
332 if (find != com)
333 community_free(com);
718e3744 334
d62a17ae 335 /* Increment refrence counter. */
336 find->refcnt++;
718e3744 337
d62a17ae 338 /* Make string. */
339 if (!find->str)
340 set_community_string(find);
718e3744 341
d62a17ae 342 return find;
718e3744 343}
344
345/* Free community attribute. */
d62a17ae 346void community_unintern(struct community **com)
718e3744 347{
d62a17ae 348 struct community *ret;
718e3744 349
d62a17ae 350 if ((*com)->refcnt)
351 (*com)->refcnt--;
718e3744 352
d62a17ae 353 /* Pull off from hash. */
354 if ((*com)->refcnt == 0) {
355 /* Community value com must exist in hash. */
356 ret = (struct community *)hash_release(comhash, *com);
357 assert(ret != NULL);
718e3744 358
d62a17ae 359 community_free(*com);
360 *com = NULL;
361 }
718e3744 362}
363
364/* Create new community attribute. */
d62a17ae 365struct community *community_parse(u_int32_t *pnt, u_short length)
718e3744 366{
d62a17ae 367 struct community tmp;
368 struct community *new;
718e3744 369
d62a17ae 370 /* If length is malformed return NULL. */
371 if (length % 4)
372 return NULL;
718e3744 373
d62a17ae 374 /* Make temporary community for hash look up. */
375 tmp.size = length / 4;
376 tmp.val = pnt;
718e3744 377
d62a17ae 378 new = community_uniq_sort(&tmp);
718e3744 379
d62a17ae 380 return community_intern(new);
718e3744 381}
382
d62a17ae 383struct community *community_dup(struct community *com)
718e3744 384{
d62a17ae 385 struct community *new;
386
387 new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
388 new->size = com->size;
389 if (new->size) {
390 new->val = XMALLOC(MTYPE_COMMUNITY_VAL, com->size * 4);
391 memcpy(new->val, com->val, com->size * 4);
392 } else
393 new->val = NULL;
394 return new;
718e3744 395}
396
397/* Retrun string representation of communities attribute. */
d62a17ae 398char *community_str(struct community *com)
718e3744 399{
d62a17ae 400 if (!com)
401 return NULL;
f1aa5d8a 402
d62a17ae 403 if (!com->str)
404 set_community_string(com);
405 return com->str;
718e3744 406}
407
408/* Make hash value of community attribute. This function is used by
409 hash package.*/
d62a17ae 410unsigned int community_hash_make(struct community *com)
718e3744 411{
d62a17ae 412 unsigned char *pnt = (unsigned char *)com->val;
413 int size = com->size * 4;
414 unsigned int key = 0;
415 int c;
416
417 for (c = 0; c < size; c += 4) {
418 key += pnt[c];
419 key += pnt[c + 1];
420 key += pnt[c + 2];
421 key += pnt[c + 3];
422 }
423
424 return key;
718e3744 425}
426
d62a17ae 427int community_match(const struct community *com1, const struct community *com2)
718e3744 428{
d62a17ae 429 int i = 0;
430 int j = 0;
431
432 if (com1 == NULL && com2 == NULL)
433 return 1;
434
435 if (com1 == NULL || com2 == NULL)
436 return 0;
437
438 if (com1->size < com2->size)
439 return 0;
440
441 /* Every community on com2 needs to be on com1 for this to match */
442 while (i < com1->size && j < com2->size) {
443 if (memcmp(com1->val + i, com2->val + j, sizeof(u_int32_t))
444 == 0)
445 j++;
446 i++;
447 }
448
449 if (j == com2->size)
450 return 1;
451 else
452 return 0;
718e3744 453}
454
455/* If two aspath have same value then return 1 else return 0. This
456 function is used by hash package. */
d62a17ae 457int community_cmp(const struct community *com1, const struct community *com2)
718e3744 458{
d62a17ae 459 if (com1 == NULL && com2 == NULL)
460 return 1;
461 if (com1 == NULL || com2 == NULL)
462 return 0;
463
464 if (com1->size == com2->size)
465 if (memcmp(com1->val, com2->val, com1->size * 4) == 0)
466 return 1;
467 return 0;
718e3744 468}
469
470/* Add com2 to the end of com1. */
d62a17ae 471struct community *community_merge(struct community *com1,
472 struct community *com2)
718e3744 473{
d62a17ae 474 if (com1->val)
475 com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
476 (com1->size + com2->size) * 4);
477 else
478 com1->val = XMALLOC(MTYPE_COMMUNITY_VAL,
479 (com1->size + com2->size) * 4);
718e3744 480
d62a17ae 481 memcpy(com1->val + com1->size, com2->val, com2->size * 4);
482 com1->size += com2->size;
718e3744 483
d62a17ae 484 return com1;
718e3744 485}
486
487/* Community token enum. */
d62a17ae 488enum community_token {
489 community_token_val,
490 community_token_no_export,
491 community_token_no_advertise,
492 community_token_local_as,
7f323236 493 community_token_gshut,
d62a17ae 494 community_token_unknown
718e3744 495};
496
497/* Get next community token from string. */
94f2b392 498static const char *
d62a17ae 499community_gettoken(const char *buf, enum community_token *token, u_int32_t *val)
718e3744 500{
d62a17ae 501 const char *p = buf;
502
503 /* Skip white space. */
504 while (isspace((int)*p))
505 p++;
506
507 /* Check the end of the line. */
508 if (*p == '\0')
509 return NULL;
510
511 /* Well known community string check. */
512 if (isalpha((int)*p)) {
513 if (strncmp(p, "internet", strlen("internet")) == 0) {
514 *val = COMMUNITY_INTERNET;
515 *token = community_token_no_export;
516 p += strlen("internet");
517 return p;
518 }
519 if (strncmp(p, "no-export", strlen("no-export")) == 0) {
520 *val = COMMUNITY_NO_EXPORT;
521 *token = community_token_no_export;
522 p += strlen("no-export");
523 return p;
524 }
525 if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
526 *val = COMMUNITY_NO_ADVERTISE;
527 *token = community_token_no_advertise;
528 p += strlen("no-advertise");
529 return p;
530 }
531 if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
532 *val = COMMUNITY_LOCAL_AS;
533 *token = community_token_local_as;
534 p += strlen("local-AS");
535 return p;
536 }
7f323236
DW
537 if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown")) == 0) {
538 *val = COMMUNITY_GSHUT;
539 *token = community_token_gshut;
540 p += strlen("graceful-shutdown");
541 return p;
542 }
d62a17ae 543
544 /* Unknown string. */
545 *token = community_token_unknown;
546 return NULL;
718e3744 547 }
548
d62a17ae 549 /* Community value. */
550 if (isdigit((int)*p)) {
551 int separator = 0;
552 int digit = 0;
553 u_int32_t community_low = 0;
554 u_int32_t community_high = 0;
555
556 while (isdigit((int)*p) || *p == ':') {
557 if (*p == ':') {
558 if (separator) {
559 *token = community_token_unknown;
560 return NULL;
561 } else {
562 separator = 1;
563 digit = 0;
564
565 if (community_low > UINT16_MAX) {
566 *token =
567 community_token_unknown;
568 return NULL;
569 }
570
571 community_high = community_low << 16;
572 community_low = 0;
573 }
574 } else {
575 digit = 1;
576 community_low *= 10;
577 community_low += (*p - '0');
578 }
579 p++;
718e3744 580 }
d62a17ae 581 if (!digit) {
582 *token = community_token_unknown;
583 return NULL;
584 }
585
586 if (community_low > UINT16_MAX) {
587 *token = community_token_unknown;
588 return NULL;
718e3744 589 }
859d388e 590
d62a17ae 591 *val = community_high + community_low;
592 *token = community_token_val;
593 return p;
594 }
595 *token = community_token_unknown;
596 return NULL;
718e3744 597}
598
599/* convert string to community structure */
d62a17ae 600struct community *community_str2com(const char *str)
718e3744 601{
d62a17ae 602 struct community *com = NULL;
603 struct community *com_sort = NULL;
604 u_int32_t val = 0;
605 enum community_token token = community_token_unknown;
606
607 do {
608 str = community_gettoken(str, &token, &val);
609
610 switch (token) {
611 case community_token_val:
612 case community_token_no_export:
613 case community_token_no_advertise:
614 case community_token_local_as:
7f323236 615 case community_token_gshut:
d62a17ae 616 if (com == NULL) {
617 com = community_new();
618 com->json = NULL;
619 }
620 community_add_val(com, val);
621 break;
622 case community_token_unknown:
623 default:
624 if (com)
625 community_free(com);
626 return NULL;
627 }
628 } while (str);
629
630 if (!com)
631 return NULL;
718e3744 632
d62a17ae 633 com_sort = community_uniq_sort(com);
634 community_free(com);
718e3744 635
d62a17ae 636 return com_sort;
718e3744 637}
638
639/* Return communities hash entry count. */
d62a17ae 640unsigned long community_count(void)
718e3744 641{
d62a17ae 642 return comhash->count;
718e3744 643}
644
645/* Return communities hash. */
d62a17ae 646struct hash *community_hash(void)
718e3744 647{
d62a17ae 648 return comhash;
718e3744 649}
650
651/* Initialize comminity related hash. */
d62a17ae 652void community_init(void)
718e3744 653{
d62a17ae 654 comhash = hash_create(
655 (unsigned int (*)(void *))community_hash_make,
656 (int (*)(const void *, const void *))community_cmp, NULL);
718e3744 657}
228da428 658
d62a17ae 659void community_finish(void)
228da428 660{
d62a17ae 661 hash_free(comhash);
662 comhash = NULL;
228da428 663}