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