bgpd: tighten bounds checking in RR ORF msg reader
[jleu-quagga.git] / bgpd / bgp_ecommunity.c
blob8d5fa741a882ddaab1355be3f3c1c85ef38f87dc
1 /* BGP Extended Communities Attribute
2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
4 This file is part of GNU Zebra.
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.
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.
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. */
21 #include <zebra.h>
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
28 #include "bgpd/bgpd.h"
29 #include "bgpd/bgp_ecommunity.h"
30 #include "bgpd/bgp_aspath.h"
32 /* Hash of community attribute. */
33 static struct hash *ecomhash;
35 /* Allocate a new ecommunities. */
36 static struct ecommunity *
37 ecommunity_new (void)
39 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
40 sizeof (struct ecommunity));
43 /* Allocate ecommunities. */
44 void
45 ecommunity_free (struct ecommunity *ecom)
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);
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)
62 u_int8_t *p;
63 int ret;
64 int c;
66 /* When this is fist value, just add it. */
67 if (ecom->val == NULL)
69 ecom->size++;
70 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
71 memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
72 return 1;
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++)
79 ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
80 if (ret == 0)
81 return 0;
82 if (ret > 0)
83 break;
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));
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);
95 return 1;
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)
104 int i;
105 struct ecommunity *new;
106 struct ecommunity_val *eval;
108 if (! ecom)
109 return NULL;
111 new = ecommunity_new ();
113 for (i = 0; i < ecom->size; i++)
115 eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
116 ecommunity_add_val (new, eval);
118 return new;
121 /* Parse Extended Communites Attribute in BGP packet. */
122 struct ecommunity *
123 ecommunity_parse (u_int8_t *pnt, u_short length)
125 struct ecommunity tmp;
126 struct ecommunity *new;
128 /* Length check. */
129 if (length % ECOMMUNITY_SIZE)
130 return NULL;
132 /* Prepare tmporary structure for making a new Extended Communities
133 Attribute. */
134 tmp.size = length / ECOMMUNITY_SIZE;
135 tmp.val = pnt;
137 /* Create a new Extended Communities Attribute by uniq and sort each
138 Extended Communities value */
139 new = ecommunity_uniq_sort (&tmp);
141 return ecommunity_intern (new);
144 /* Duplicate the Extended Communities Attribute structure. */
145 struct ecommunity *
146 ecommunity_dup (struct ecommunity *ecom)
148 struct ecommunity *new;
150 new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
151 new->size = ecom->size;
152 if (new->size)
154 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
155 memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
157 else
158 new->val = NULL;
159 return new;
162 /* Retrun string representation of communities attribute. */
163 char *
164 ecommunity_str (struct ecommunity *ecom)
166 if (! ecom->str)
167 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
168 return ecom->str;
171 /* Merge two Extended Communities Attribute structure. */
172 struct ecommunity *
173 ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
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);
182 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
183 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
184 ecom1->size += ecom2->size;
186 return ecom1;
189 /* Intern Extended Communities Attribute. */
190 struct ecommunity *
191 ecommunity_intern (struct ecommunity *ecom)
193 struct ecommunity *find;
195 assert (ecom->refcnt == 0);
197 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
199 if (find != ecom)
200 ecommunity_free (ecom);
202 find->refcnt++;
204 if (! find->str)
205 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
207 return find;
210 /* Unintern Extended Communities Attribute. */
211 void
212 ecommunity_unintern (struct ecommunity *ecom)
214 struct ecommunity *ret;
216 if (ecom->refcnt)
217 ecom->refcnt--;
219 /* Pull off from hash. */
220 if (ecom->refcnt == 0)
222 /* Extended community must be in the hash. */
223 ret = (struct ecommunity *) hash_release (ecomhash, ecom);
224 assert (ret != NULL);
226 ecommunity_free (ecom);
230 /* Utinity function to make hash key. */
231 unsigned int
232 ecommunity_hash_make (void *arg)
234 const struct ecommunity *ecom = arg;
235 int c;
236 unsigned int key;
237 u_int8_t *pnt;
239 key = 0;
240 pnt = ecom->val;
242 for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
243 key += pnt[c];
245 return key;
248 /* Compare two Extended Communities Attribute structure. */
250 ecommunity_cmp (const void *arg1, const void *arg2)
252 const struct ecommunity *ecom1 = arg1;
253 const struct ecommunity *ecom2 = arg2;
255 return (ecom1->size == ecom2->size
256 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
259 /* Initialize Extended Comminities related hash. */
260 void
261 ecommunity_init (void)
263 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
266 void
267 ecommunity_finish (void)
269 hash_free (ecomhash);
270 ecomhash = NULL;
273 /* Extended Communities token enum. */
274 enum ecommunity_token
276 ecommunity_token_rt,
277 ecommunity_token_soo,
278 ecommunity_token_val,
279 ecommunity_token_unknown
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)
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];
298 /* Skip white space. */
299 while (isspace ((int) *p))
301 p++;
302 str++;
305 /* Check the end of the line. */
306 if (*p == '\0')
307 return NULL;
309 /* "rt" and "soo" keyword parse. */
310 if (! isdigit ((int) *p))
312 /* "rt" match check. */
313 if (tolower ((int) *p) == 'r')
315 p++;
316 if (tolower ((int) *p) == 't')
318 p++;
319 *token = ecommunity_token_rt;
320 return p;
322 if (isspace ((int) *p) || *p == '\0')
324 *token = ecommunity_token_rt;
325 return p;
327 goto error;
329 /* "soo" match check. */
330 else if (tolower ((int) *p) == 's')
332 p++;
333 if (tolower ((int) *p) == 'o')
335 p++;
336 if (tolower ((int) *p) == 'o')
338 p++;
339 *token = ecommunity_token_soo;
340 return p;
342 if (isspace ((int) *p) || *p == '\0')
344 *token = ecommunity_token_soo;
345 return p;
347 goto error;
349 if (isspace ((int) *p) || *p == '\0')
351 *token = ecommunity_token_soo;
352 return p;
354 goto error;
356 goto error;
359 /* What a mess, there are several possibilities:
361 * a) A.B.C.D:MN
362 * b) EF:OPQR
363 * c) GHJK:MN
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
372 while (isdigit ((int) *p) || *p == ':' || *p == '.')
374 if (*p == ':')
376 if (separator)
377 goto error;
379 separator = 1;
380 digit = 0;
382 if ((p - str) > INET_ADDRSTRLEN)
383 goto error;
384 memset (buf, 0, INET_ADDRSTRLEN + 1);
385 memcpy (buf, str, p - str);
387 if (dot)
389 /* Parsing A.B.C.D in:
390 * A.B.C.D:MN
392 ret = inet_aton (buf, &ip);
393 if (ret == 0)
394 goto error;
396 else
398 /* ASN */
399 as = strtoul (buf, &endptr, 10);
400 if (*endptr != '\0' || as == BGP_AS4_MAX)
401 goto error;
404 else if (*p == '.')
406 if (separator)
407 goto error;
408 dot++;
409 if (dot > 4)
410 goto error;
412 else
414 digit = 1;
416 /* We're past the IP/ASN part */
417 if (separator)
419 val *= 10;
420 val += (*p - '0');
423 p++;
426 /* Low digit part must be there. */
427 if (!digit || !separator)
428 goto error;
430 /* Encode result into routing distinguisher. */
431 if (dot)
433 if (val > UINT16_MAX)
434 goto error;
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;
442 else if (as > BGP_AS_MAX)
444 if (val > UINT16_MAX)
445 goto error;
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;
456 else
458 eval->val[0] = ECOMMUNITY_ENCODE_AS;
459 eval->val[1] = 0;
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;
468 *token = ecommunity_token_val;
469 return p;
471 error:
472 *token = ecommunity_token_unknown;
473 return p;
476 /* Convert string to extended community attribute.
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.
483 For example route-map's "set extcommunity" command case:
485 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
486 type = ECOMMUNITY_ROUTE_TARGET
487 keyword_included = 0
489 "soo 100:1" -> str = "100:1"
490 type = ECOMMUNITY_SITE_ORIGIN
491 keyword_included = 0
493 When string includes keyword for each extended community value.
494 Please specify keyword_included as non-zero value.
496 For example standard extcommunity-list case:
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
502 struct ecommunity *
503 ecommunity_str2com (const char *str, int type, int keyword_included)
505 struct ecommunity *ecom = NULL;
506 enum ecommunity_token token;
507 struct ecommunity_val eval;
508 int keyword = 0;
510 while ((str = ecommunity_gettoken (str, &eval, &token)))
512 switch (token)
514 case ecommunity_token_rt:
515 case ecommunity_token_soo:
516 if (! keyword_included || keyword)
518 if (ecom)
519 ecommunity_free (ecom);
520 return NULL;
522 keyword = 1;
524 if (token == ecommunity_token_rt)
526 type = ECOMMUNITY_ROUTE_TARGET;
528 if (token == ecommunity_token_soo)
530 type = ECOMMUNITY_SITE_ORIGIN;
532 break;
533 case ecommunity_token_val:
534 if (keyword_included)
536 if (! keyword)
538 if (ecom)
539 ecommunity_free (ecom);
540 return NULL;
542 keyword = 0;
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;
556 return ecom;
559 /* Convert extended community attribute to string.
561 Due to historical reason of industry standard implementation, there
562 are three types of format.
564 route-map set extcommunity format
565 "rt 100:1 100:2"
566 "soo 100:3"
568 extcommunity-list
569 "rt 100:1 rt 100:2 soo 100:3"
571 "show ip bgp" and extcommunity-list regular expression matching
572 "RT:100:1 RT:100:2 SoO:100:3"
574 For each formath please use below definition for format:
576 ECOMMUNITY_FORMAT_ROUTE_MAP
577 ECOMMUNITY_FORMAT_COMMUNITY_LIST
578 ECOMMUNITY_FORMAT_DISPLAY
580 char *
581 ecommunity_ecom2str (struct ecommunity *ecom, int format)
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;
595 /* For parse Extended Community attribute tupple. */
596 struct ecommunity_as
598 as_t as;
599 u_int32_t val;
600 } eas;
602 struct ecommunity_ip
604 struct in_addr ip;
605 u_int16_t val;
606 } eip;
608 if (ecom->size == 0)
610 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
611 str_buf[0] = '\0';
612 return str_buf;
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;
620 for (i = 0; i < ecom->size; i++)
622 /* Space between each value. */
623 if (! first)
624 str_buf[str_pnt++] = ' ';
626 pnt = ecom->val + (i * 8);
628 /* High-order octet of type. */
629 encode = *pnt++;
630 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP
631 && encode != ECOMMUNITY_ENCODE_AS4)
633 len = sprintf (str_buf + str_pnt, "?");
634 str_pnt += len;
635 first = 0;
636 continue;
639 /* Low-order octet of type. */
640 type = *pnt++;
641 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
643 len = sprintf (str_buf + str_pnt, "?");
644 str_pnt += len;
645 first = 0;
646 continue;
649 switch (format)
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;
665 /* Make it sure size is enough. */
666 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
668 str_size *= 2;
669 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
672 /* Put string into buffer. */
673 if (encode == ECOMMUNITY_ENCODE_AS4)
675 eas.as = (*pnt++ << 24);
676 eas.as |= (*pnt++ << 16);
677 eas.as |= (*pnt++ << 8);
678 eas.as |= (*pnt++);
680 eas.val = (*pnt++ << 8);
681 eas.val |= (*pnt++);
683 len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix,
684 eas.as, eas.val );
685 str_pnt += len;
686 first = 0;
688 if (encode == ECOMMUNITY_ENCODE_AS)
690 eas.as = (*pnt++ << 8);
691 eas.as |= (*pnt++);
693 eas.val = (*pnt++ << 24);
694 eas.val |= (*pnt++ << 16);
695 eas.val |= (*pnt++ << 8);
696 eas.val |= (*pnt++);
698 len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix,
699 eas.as, eas.val);
700 str_pnt += len;
701 first = 0;
703 else if (encode == ECOMMUNITY_ENCODE_IP)
705 memcpy (&eip.ip, pnt, 4);
706 pnt += 4;
707 eip.val = (*pnt++ << 8);
708 eip.val |= (*pnt++);
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;
716 return str_buf;
720 ecommunity_match (const struct ecommunity *ecom1,
721 const struct ecommunity *ecom2)
723 int i = 0;
724 int j = 0;
726 if (ecom1 == NULL && ecom2 == NULL)
727 return 1;
729 if (ecom1 == NULL || ecom2 == NULL)
730 return 0;
732 if (ecom1->size < ecom2->size)
733 return 0;
735 /* Every community on com2 needs to be on com1 for this to match */
736 while (i < ecom1->size && j < ecom2->size)
738 if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
739 j++;
740 i++;
743 if (j == ecom2->size)
744 return 1;
745 else
746 return 0;