]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
lib, bgpd: Remove 'struct fifo' from lib/zebra.h
[mirror_frr.git] / bgpd / bgp_ecommunity.c
1 /* BGP Extended Communities Attribute
2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
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
17 along with GNU Zebra; see the file COPYING. If not, write to the Free
18 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA. */
20
21 #include <zebra.h>
22
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
27 #include "queue.h"
28 #include "filter.h"
29
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_ecommunity.h"
32 #include "bgpd/bgp_aspath.h"
33
34 /* Hash of community attribute. */
35 static struct hash *ecomhash;
36
37 /* Allocate a new ecommunities. */
38 static struct ecommunity *
39 ecommunity_new (void)
40 {
41 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
42 sizeof (struct ecommunity));
43 }
44
45 /* Allocate ecommunities. */
46 void
47 ecommunity_free (struct ecommunity **ecom)
48 {
49 if ((*ecom)->val)
50 XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
51 if ((*ecom)->str)
52 XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str);
53 XFREE (MTYPE_ECOMMUNITY, *ecom);
54 ecom = NULL;
55 }
56
57 /* Add a new Extended Communities value to Extended Communities
58 Attribute structure. When the value is already exists in the
59 structure, we don't add the value. Newly added value is sorted by
60 numerical order. When the value is added to the structure return 1
61 else return 0. */
62 static int
63 ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
64 {
65 u_int8_t *p;
66 int ret;
67 int c;
68
69 /* When this is fist value, just add it. */
70 if (ecom->val == NULL)
71 {
72 ecom->size++;
73 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
74 memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
75 return 1;
76 }
77
78 /* If the value already exists in the structure return 0. */
79 c = 0;
80 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
81 {
82 ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
83 if (ret == 0)
84 return 0;
85 if (ret > 0)
86 break;
87 }
88
89 /* Add the value to the structure with numerical sorting. */
90 ecom->size++;
91 ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
92
93 memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
94 ecom->val + c * ECOMMUNITY_SIZE,
95 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
96 memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
97
98 return 1;
99 }
100
101 /* This function takes pointer to Extended Communites strucutre then
102 create a new Extended Communities structure by uniq and sort each
103 Extended Communities value. */
104 struct ecommunity *
105 ecommunity_uniq_sort (struct ecommunity *ecom)
106 {
107 int i;
108 struct ecommunity *new;
109 struct ecommunity_val *eval;
110
111 if (! ecom)
112 return NULL;
113
114 new = ecommunity_new ();
115
116 for (i = 0; i < ecom->size; i++)
117 {
118 eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
119 ecommunity_add_val (new, eval);
120 }
121 return new;
122 }
123
124 /* Parse Extended Communites Attribute in BGP packet. */
125 struct ecommunity *
126 ecommunity_parse (u_int8_t *pnt, u_short length)
127 {
128 struct ecommunity tmp;
129 struct ecommunity *new;
130
131 /* Length check. */
132 if (length % ECOMMUNITY_SIZE)
133 return NULL;
134
135 /* Prepare tmporary structure for making a new Extended Communities
136 Attribute. */
137 tmp.size = length / ECOMMUNITY_SIZE;
138 tmp.val = pnt;
139
140 /* Create a new Extended Communities Attribute by uniq and sort each
141 Extended Communities value */
142 new = ecommunity_uniq_sort (&tmp);
143
144 return ecommunity_intern (new);
145 }
146
147 /* Duplicate the Extended Communities Attribute structure. */
148 struct ecommunity *
149 ecommunity_dup (struct ecommunity *ecom)
150 {
151 struct ecommunity *new;
152
153 new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
154 new->size = ecom->size;
155 if (new->size)
156 {
157 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
158 memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
159 }
160 else
161 new->val = NULL;
162 return new;
163 }
164
165 /* Retrun string representation of communities attribute. */
166 char *
167 ecommunity_str (struct ecommunity *ecom)
168 {
169 if (! ecom->str)
170 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
171 return ecom->str;
172 }
173
174 /* Merge two Extended Communities Attribute structure. */
175 struct ecommunity *
176 ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
177 {
178 if (ecom1->val)
179 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
180 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
181 else
182 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
183 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
184
185 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
186 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
187 ecom1->size += ecom2->size;
188
189 return ecom1;
190 }
191
192 /* Intern Extended Communities Attribute. */
193 struct ecommunity *
194 ecommunity_intern (struct ecommunity *ecom)
195 {
196 struct ecommunity *find;
197
198 assert (ecom->refcnt == 0);
199
200 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
201
202 if (find != ecom)
203 ecommunity_free (&ecom);
204
205 find->refcnt++;
206
207 if (! find->str)
208 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
209
210 return find;
211 }
212
213 /* Unintern Extended Communities Attribute. */
214 void
215 ecommunity_unintern (struct ecommunity **ecom)
216 {
217 struct ecommunity *ret;
218
219 if ((*ecom)->refcnt)
220 (*ecom)->refcnt--;
221
222 /* Pull off from hash. */
223 if ((*ecom)->refcnt == 0)
224 {
225 /* Extended community must be in the hash. */
226 ret = (struct ecommunity *) hash_release (ecomhash, *ecom);
227 assert (ret != NULL);
228
229 ecommunity_free (ecom);
230 }
231 }
232
233 /* Utinity function to make hash key. */
234 unsigned int
235 ecommunity_hash_make (void *arg)
236 {
237 const struct ecommunity *ecom = arg;
238 int size = ecom->size * ECOMMUNITY_SIZE;
239 u_int8_t *pnt = ecom->val;
240 unsigned int key = 0;
241 int c;
242
243 for (c = 0; c < size; c += ECOMMUNITY_SIZE)
244 {
245 key += pnt[c];
246 key += pnt[c + 1];
247 key += pnt[c + 2];
248 key += pnt[c + 3];
249 key += pnt[c + 4];
250 key += pnt[c + 5];
251 key += pnt[c + 6];
252 key += pnt[c + 7];
253 }
254
255 return key;
256 }
257
258 /* Compare two Extended Communities Attribute structure. */
259 int
260 ecommunity_cmp (const void *arg1, const void *arg2)
261 {
262 const struct ecommunity *ecom1 = arg1;
263 const struct ecommunity *ecom2 = arg2;
264
265 if (ecom1 == NULL && ecom2 == NULL)
266 return 1;
267
268 if (ecom1 == NULL || ecom2 == NULL)
269 return 0;
270
271 return (ecom1->size == ecom2->size
272 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
273 }
274
275 /* Initialize Extended Comminities related hash. */
276 void
277 ecommunity_init (void)
278 {
279 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
280 }
281
282 void
283 ecommunity_finish (void)
284 {
285 hash_free (ecomhash);
286 ecomhash = NULL;
287 }
288
289 /* Extended Communities token enum. */
290 enum ecommunity_token
291 {
292 ecommunity_token_unknown = 0,
293 ecommunity_token_rt,
294 ecommunity_token_soo,
295 ecommunity_token_val,
296 };
297
298 /* Get next Extended Communities token from the string. */
299 static const char *
300 ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
301 enum ecommunity_token *token)
302 {
303 int ret;
304 int dot = 0;
305 int digit = 0;
306 int separator = 0;
307 const char *p = str;
308 char *endptr;
309 struct in_addr ip;
310 as_t as = 0;
311 u_int32_t val = 0;
312 char buf[INET_ADDRSTRLEN + 1];
313
314 /* Skip white space. */
315 while (isspace ((int) *p))
316 {
317 p++;
318 str++;
319 }
320
321 /* Check the end of the line. */
322 if (*p == '\0')
323 return NULL;
324
325 /* "rt" and "soo" keyword parse. */
326 if (! isdigit ((int) *p))
327 {
328 /* "rt" match check. */
329 if (tolower ((int) *p) == 'r')
330 {
331 p++;
332 if (tolower ((int) *p) == 't')
333 {
334 p++;
335 *token = ecommunity_token_rt;
336 return p;
337 }
338 if (isspace ((int) *p) || *p == '\0')
339 {
340 *token = ecommunity_token_rt;
341 return p;
342 }
343 goto error;
344 }
345 /* "soo" match check. */
346 else if (tolower ((int) *p) == 's')
347 {
348 p++;
349 if (tolower ((int) *p) == 'o')
350 {
351 p++;
352 if (tolower ((int) *p) == 'o')
353 {
354 p++;
355 *token = ecommunity_token_soo;
356 return p;
357 }
358 if (isspace ((int) *p) || *p == '\0')
359 {
360 *token = ecommunity_token_soo;
361 return p;
362 }
363 goto error;
364 }
365 if (isspace ((int) *p) || *p == '\0')
366 {
367 *token = ecommunity_token_soo;
368 return p;
369 }
370 goto error;
371 }
372 goto error;
373 }
374
375 /* What a mess, there are several possibilities:
376 *
377 * a) A.B.C.D:MN
378 * b) EF:OPQR
379 * c) GHJK:MN
380 *
381 * A.B.C.D: Four Byte IP
382 * EF: Two byte ASN
383 * GHJK: Four-byte ASN
384 * MN: Two byte value
385 * OPQR: Four byte value
386 *
387 */
388 while (isdigit ((int) *p) || *p == ':' || *p == '.')
389 {
390 if (*p == ':')
391 {
392 if (separator)
393 goto error;
394
395 separator = 1;
396 digit = 0;
397
398 if ((p - str) > INET_ADDRSTRLEN)
399 goto error;
400 memset (buf, 0, INET_ADDRSTRLEN + 1);
401 memcpy (buf, str, p - str);
402
403 if (dot)
404 {
405 /* Parsing A.B.C.D in:
406 * A.B.C.D:MN
407 */
408 ret = inet_aton (buf, &ip);
409 if (ret == 0)
410 goto error;
411 }
412 else
413 {
414 /* ASN */
415 as = strtoul (buf, &endptr, 10);
416 if (*endptr != '\0' || as == BGP_AS4_MAX)
417 goto error;
418 }
419 }
420 else if (*p == '.')
421 {
422 if (separator)
423 goto error;
424 dot++;
425 if (dot > 4)
426 goto error;
427 }
428 else
429 {
430 digit = 1;
431
432 /* We're past the IP/ASN part */
433 if (separator)
434 {
435 val *= 10;
436 val += (*p - '0');
437 }
438 }
439 p++;
440 }
441
442 /* Low digit part must be there. */
443 if (!digit || !separator)
444 goto error;
445
446 /* Encode result into routing distinguisher. */
447 if (dot)
448 {
449 if (val > UINT16_MAX)
450 goto error;
451
452 eval->val[0] = ECOMMUNITY_ENCODE_IP;
453 eval->val[1] = 0;
454 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
455 eval->val[6] = (val >> 8) & 0xff;
456 eval->val[7] = val & 0xff;
457 }
458 else if (as > BGP_AS_MAX)
459 {
460 if (val > UINT16_MAX)
461 goto error;
462
463 eval->val[0] = ECOMMUNITY_ENCODE_AS4;
464 eval->val[1] = 0;
465 eval->val[2] = (as >>24) & 0xff;
466 eval->val[3] = (as >>16) & 0xff;
467 eval->val[4] = (as >>8) & 0xff;
468 eval->val[5] = as & 0xff;
469 eval->val[6] = (val >> 8) & 0xff;
470 eval->val[7] = val & 0xff;
471 }
472 else
473 {
474 eval->val[0] = ECOMMUNITY_ENCODE_AS;
475 eval->val[1] = 0;
476
477 eval->val[2] = (as >>8) & 0xff;
478 eval->val[3] = as & 0xff;
479 eval->val[4] = (val >>24) & 0xff;
480 eval->val[5] = (val >>16) & 0xff;
481 eval->val[6] = (val >>8) & 0xff;
482 eval->val[7] = val & 0xff;
483 }
484 *token = ecommunity_token_val;
485 return p;
486
487 error:
488 *token = ecommunity_token_unknown;
489 return p;
490 }
491
492 /* Convert string to extended community attribute.
493
494 When type is already known, please specify both str and type. str
495 should not include keyword such as "rt" and "soo". Type is
496 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
497 keyword_included should be zero.
498
499 For example route-map's "set extcommunity" command case:
500
501 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
502 type = ECOMMUNITY_ROUTE_TARGET
503 keyword_included = 0
504
505 "soo 100:1" -> str = "100:1"
506 type = ECOMMUNITY_SITE_ORIGIN
507 keyword_included = 0
508
509 When string includes keyword for each extended community value.
510 Please specify keyword_included as non-zero value.
511
512 For example standard extcommunity-list case:
513
514 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
515 type = 0
516 keyword_include = 1
517 */
518 struct ecommunity *
519 ecommunity_str2com (const char *str, int type, int keyword_included)
520 {
521 struct ecommunity *ecom = NULL;
522 enum ecommunity_token token = ecommunity_token_unknown;
523 struct ecommunity_val eval;
524 int keyword = 0;
525
526 while ((str = ecommunity_gettoken (str, &eval, &token)))
527 {
528 switch (token)
529 {
530 case ecommunity_token_rt:
531 case ecommunity_token_soo:
532 if (! keyword_included || keyword)
533 {
534 if (ecom)
535 ecommunity_free (&ecom);
536 return NULL;
537 }
538 keyword = 1;
539
540 if (token == ecommunity_token_rt)
541 {
542 type = ECOMMUNITY_ROUTE_TARGET;
543 }
544 if (token == ecommunity_token_soo)
545 {
546 type = ECOMMUNITY_SITE_ORIGIN;
547 }
548 break;
549 case ecommunity_token_val:
550 if (keyword_included)
551 {
552 if (! keyword)
553 {
554 if (ecom)
555 ecommunity_free (&ecom);
556 return NULL;
557 }
558 keyword = 0;
559 }
560 if (ecom == NULL)
561 ecom = ecommunity_new ();
562 eval.val[1] = type;
563 ecommunity_add_val (ecom, &eval);
564 break;
565 case ecommunity_token_unknown:
566 default:
567 if (ecom)
568 ecommunity_free (&ecom);
569 return NULL;
570 }
571 }
572 return ecom;
573 }
574
575 /* Convert extended community attribute to string.
576
577 Due to historical reason of industry standard implementation, there
578 are three types of format.
579
580 route-map set extcommunity format
581 "rt 100:1 100:2"
582 "soo 100:3"
583
584 extcommunity-list
585 "rt 100:1 rt 100:2 soo 100:3"
586
587 "show ip bgp" and extcommunity-list regular expression matching
588 "RT:100:1 RT:100:2 SoO:100:3"
589
590 For each formath please use below definition for format:
591
592 ECOMMUNITY_FORMAT_ROUTE_MAP
593 ECOMMUNITY_FORMAT_COMMUNITY_LIST
594 ECOMMUNITY_FORMAT_DISPLAY
595 */
596 char *
597 ecommunity_ecom2str (struct ecommunity *ecom, int format)
598 {
599 int i;
600 u_int8_t *pnt;
601 int encode = 0;
602 int type = 0;
603 #define ECOMMUNITY_STR_DEFAULT_LEN 27
604 int str_size;
605 int str_pnt;
606 char *str_buf;
607 const char *prefix;
608 int len = 0;
609 int first = 1;
610
611 /* For parse Extended Community attribute tupple. */
612 struct ecommunity_as
613 {
614 as_t as;
615 u_int32_t val;
616 } eas;
617
618 struct ecommunity_ip
619 {
620 struct in_addr ip;
621 u_int16_t val;
622 } eip;
623
624 if (ecom->size == 0)
625 {
626 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
627 str_buf[0] = '\0';
628 return str_buf;
629 }
630
631 /* Prepare buffer. */
632 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
633 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
634 str_pnt = 0;
635
636 for (i = 0; i < ecom->size; i++)
637 {
638 /* Make it sure size is enough. */
639 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
640 {
641 str_size *= 2;
642 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
643 }
644
645 /* Space between each value. */
646 if (! first)
647 str_buf[str_pnt++] = ' ';
648
649 pnt = ecom->val + (i * 8);
650
651 /* High-order octet of type. */
652 encode = *pnt++;
653
654 switch (encode)
655 {
656 case ECOMMUNITY_ENCODE_AS:
657 case ECOMMUNITY_ENCODE_IP:
658 case ECOMMUNITY_ENCODE_AS4:
659 break;
660
661 case ECOMMUNITY_ENCODE_OPAQUE:
662 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
663 {
664 uint16_t tunneltype;
665 memcpy (&tunneltype, pnt + 5, 2);
666 tunneltype = ntohs(tunneltype);
667 len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
668 str_pnt += len;
669 first = 0;
670 continue;
671 }
672 /* fall through */
673
674 default:
675 len = sprintf (str_buf + str_pnt, "?");
676 str_pnt += len;
677 first = 0;
678 continue;
679 }
680
681 /* Low-order octet of type. */
682 type = *pnt++;
683 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
684 {
685 len = sprintf (str_buf + str_pnt, "?");
686 str_pnt += len;
687 first = 0;
688 continue;
689 }
690
691 switch (format)
692 {
693 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
694 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
695 break;
696 case ECOMMUNITY_FORMAT_DISPLAY:
697 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
698 break;
699 case ECOMMUNITY_FORMAT_ROUTE_MAP:
700 prefix = "";
701 break;
702 default:
703 prefix = "";
704 break;
705 }
706
707 /* Put string into buffer. */
708 if (encode == ECOMMUNITY_ENCODE_AS4)
709 {
710 eas.as = (*pnt++ << 24);
711 eas.as |= (*pnt++ << 16);
712 eas.as |= (*pnt++ << 8);
713 eas.as |= (*pnt++);
714
715 eas.val = (*pnt++ << 8);
716 eas.val |= (*pnt++);
717
718 len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
719 eas.as, eas.val );
720 str_pnt += len;
721 first = 0;
722 }
723 if (encode == ECOMMUNITY_ENCODE_AS)
724 {
725 eas.as = (*pnt++ << 8);
726 eas.as |= (*pnt++);
727
728 eas.val = (*pnt++ << 24);
729 eas.val |= (*pnt++ << 16);
730 eas.val |= (*pnt++ << 8);
731 eas.val |= (*pnt++);
732
733 len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix,
734 eas.as, eas.val);
735 str_pnt += len;
736 first = 0;
737 }
738 else if (encode == ECOMMUNITY_ENCODE_IP)
739 {
740 memcpy (&eip.ip, pnt, 4);
741 pnt += 4;
742 eip.val = (*pnt++ << 8);
743 eip.val |= (*pnt++);
744
745 len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix,
746 inet_ntoa (eip.ip), eip.val);
747 str_pnt += len;
748 first = 0;
749 }
750 }
751 return str_buf;
752 }
753
754 int
755 ecommunity_match (const struct ecommunity *ecom1,
756 const struct ecommunity *ecom2)
757 {
758 int i = 0;
759 int j = 0;
760
761 if (ecom1 == NULL && ecom2 == NULL)
762 return 1;
763
764 if (ecom1 == NULL || ecom2 == NULL)
765 return 0;
766
767 if (ecom1->size < ecom2->size)
768 return 0;
769
770 /* Every community on com2 needs to be on com1 for this to match */
771 while (i < ecom1->size && j < ecom2->size)
772 {
773 if (memcmp (ecom1->val + i * ECOMMUNITY_SIZE,
774 ecom2->val + j * ECOMMUNITY_SIZE,
775 ECOMMUNITY_SIZE) == 0)
776 j++;
777 i++;
778 }
779
780 if (j == ecom2->size)
781 return 1;
782 else
783 return 0;
784 }