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