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