blob: bcd37ea6ec7bcf2e5c33efbce757bc7f9c1a4bb1 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/* BGP Extended Communities Attribute
2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3
4This file is part of GNU Zebra.
5
6GNU Zebra is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by the
8Free Software Foundation; either version 2, or (at your option) any
9later version.
10
11GNU Zebra is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Zebra; see the file COPYING. If not, write to the Free
18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
1902111-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
31/* Hash of community attribute. */
32struct hash *ecomhash;
33
34/* Allocate a new ecommunities. */
35struct ecommunity *
36ecommunity_new ()
37{
38 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
39 sizeof (struct ecommunity));
40}
41
42/* Allocate ecommunities. */
43void
44ecommunity_free (struct ecommunity *ecom)
45{
46 if (ecom->val)
47 XFREE (MTYPE_ECOMMUNITY_VAL, ecom->val);
48 if (ecom->str)
49 XFREE (MTYPE_ECOMMUNITY_STR, ecom->str);
50 XFREE (MTYPE_ECOMMUNITY, ecom);
51}
52
53/* Add a new Extended Communities value to Extended Communities
54 Attribute structure. When the value is already exists in the
55 structure, we don't add the value. Newly added value is sorted by
56 numerical order. When the value is added to the structure return 1
57 else return 0. */
58static int
59ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
60{
paul5228ad22004-06-04 17:58:18 +000061 u_int8_t *p;
paul718e3742002-12-13 20:15:29 +000062 int ret;
63 int c;
64
65 /* When this is fist value, just add it. */
66 if (ecom->val == NULL)
67 {
68 ecom->size++;
69 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
70 memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
71 return 1;
72 }
73
74 /* If the value already exists in the structure return 0. */
75 c = 0;
76 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
77 {
78 ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
79 if (ret == 0)
80 return 0;
81 if (ret > 0)
82 break;
83 }
84
85 /* Add the value to the structure with numerical sorting. */
86 ecom->size++;
87 ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
88
89 memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
90 ecom->val + c * ECOMMUNITY_SIZE,
91 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
92 memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
93
94 return 1;
95}
96
97/* This function takes pointer to Extended Communites strucutre then
98 create a new Extended Communities structure by uniq and sort each
99 Exteneded Communities value. */
paul94f2b392005-06-28 12:44:16 +0000100static struct ecommunity *
paul718e3742002-12-13 20:15:29 +0000101ecommunity_uniq_sort (struct ecommunity *ecom)
102{
103 int i;
104 struct ecommunity *new;
105 struct ecommunity_val *eval;
106
107 if (! ecom)
108 return NULL;
109
110 new = ecommunity_new ();;
111
112 for (i = 0; i < ecom->size; i++)
113 {
114 eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
115 ecommunity_add_val (new, eval);
116 }
117 return new;
118}
119
120/* Parse Extended Communites Attribute in BGP packet. */
121struct ecommunity *
paul5228ad22004-06-04 17:58:18 +0000122ecommunity_parse (u_int8_t *pnt, u_short length)
paul718e3742002-12-13 20:15:29 +0000123{
124 struct ecommunity tmp;
125 struct ecommunity *new;
126
127 /* Length check. */
128 if (length % ECOMMUNITY_SIZE)
129 return NULL;
130
131 /* Prepare tmporary structure for making a new Extended Communities
132 Attribute. */
133 tmp.size = length / ECOMMUNITY_SIZE;
134 tmp.val = pnt;
135
136 /* Create a new Extended Communities Attribute by uniq and sort each
137 Extended Communities value */
138 new = ecommunity_uniq_sort (&tmp);
139
140 return ecommunity_intern (new);
141}
142
143/* Duplicate the Extended Communities Attribute structure. */
144struct ecommunity *
145ecommunity_dup (struct ecommunity *ecom)
146{
147 struct ecommunity *new;
148
149 new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
150 new->size = ecom->size;
151 if (new->size)
152 {
153 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
154 memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
155 }
156 else
157 new->val = NULL;
158 return new;
159}
160
hasso4372df72004-05-20 10:20:02 +0000161/* Retrun string representation of communities attribute. */
162char *
163ecommunity_str (struct ecommunity *ecom)
164{
165 if (! ecom->str)
166 ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
167 return ecom->str;
168}
169
paul718e3742002-12-13 20:15:29 +0000170/* Merge two Extended Communities Attribute structure. */
171struct ecommunity *
172ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
173{
174 if (ecom1->val)
175 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
176 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
177 else
178 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
179 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
180
181 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
182 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
183 ecom1->size += ecom2->size;
184
185 return ecom1;
186}
187
188/* Intern Extended Communities Attribute. */
189struct ecommunity *
190ecommunity_intern (struct ecommunity *ecom)
191{
192 struct ecommunity *find;
193
194 assert (ecom->refcnt == 0);
195
196 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
197
198 if (find != ecom)
199 ecommunity_free (ecom);
200
201 find->refcnt++;
202
203 if (! find->str)
204 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
205
206 return find;
207}
208
209/* Unintern Extended Communities Attribute. */
210void
211ecommunity_unintern (struct ecommunity *ecom)
212{
213 struct ecommunity *ret;
214
215 if (ecom->refcnt)
216 ecom->refcnt--;
217
218 /* Pull off from hash. */
219 if (ecom->refcnt == 0)
220 {
221 /* Extended community must be in the hash. */
222 ret = (struct ecommunity *) hash_release (ecomhash, ecom);
223 assert (ret != NULL);
224
225 ecommunity_free (ecom);
226 }
227}
228
229/* Utinity function to make hash key. */
230unsigned int
231ecommunity_hash_make (struct ecommunity *ecom)
232{
233 int c;
234 unsigned int key;
paul5228ad22004-06-04 17:58:18 +0000235 u_int8_t *pnt;
paul718e3742002-12-13 20:15:29 +0000236
237 key = 0;
238 pnt = ecom->val;
239
240 for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
241 key += pnt[c];
242
243 return key;
244}
245
246/* Compare two Extended Communities Attribute structure. */
247int
paulfd79ac92004-10-13 05:06:08 +0000248ecommunity_cmp (const struct ecommunity *ecom1,
249 const struct ecommunity *ecom2)
paul718e3742002-12-13 20:15:29 +0000250{
251 if (ecom1->size == ecom2->size
252 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0)
253 return 1;
254 return 0;
255}
256
257/* Initialize Extended Comminities related hash. */
258void
259ecommunity_init ()
260{
261 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
262}
263
264/* Extended Communities token enum. */
265enum ecommunity_token
266{
267 ecommunity_token_rt,
268 ecommunity_token_soo,
269 ecommunity_token_val,
270 ecommunity_token_unknown
271};
272
273/* Get next Extended Communities token from the string. */
paul94f2b392005-06-28 12:44:16 +0000274static const char *
paulfd79ac92004-10-13 05:06:08 +0000275ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
paul718e3742002-12-13 20:15:29 +0000276 enum ecommunity_token *token)
277{
278 int ret;
279 int dot = 0;
280 int digit = 0;
281 int separator = 0;
282 u_int32_t val_low = 0;
283 u_int32_t val_high = 0;
paulfd79ac92004-10-13 05:06:08 +0000284 const char *p = str;
paul718e3742002-12-13 20:15:29 +0000285 struct in_addr ip;
286 char ipstr[INET_ADDRSTRLEN + 1];
287
288 /* Skip white space. */
289 while (isspace ((int) *p))
290 {
291 p++;
292 str++;
293 }
294
295 /* Check the end of the line. */
296 if (*p == '\0')
297 return NULL;
298
299 /* "rt" and "soo" keyword parse. */
300 if (! isdigit ((int) *p))
301 {
302 /* "rt" match check. */
303 if (tolower ((int) *p) == 'r')
304 {
305 p++;
306 if (tolower ((int) *p) == 't')
307 {
308 p++;
309 *token = ecommunity_token_rt;
310 return p;
311 }
312 if (isspace ((int) *p) || *p == '\0')
313 {
314 *token = ecommunity_token_rt;
315 return p;
316 }
317 goto error;
318 }
319 /* "soo" match check. */
320 else if (tolower ((int) *p) == 's')
321 {
322 p++;
323 if (tolower ((int) *p) == 'o')
324 {
325 p++;
326 if (tolower ((int) *p) == 'o')
327 {
328 p++;
329 *token = ecommunity_token_soo;
330 return p;
331 }
332 if (isspace ((int) *p) || *p == '\0')
333 {
334 *token = ecommunity_token_soo;
335 return p;
336 }
337 goto error;
338 }
339 if (isspace ((int) *p) || *p == '\0')
340 {
341 *token = ecommunity_token_soo;
342 return p;
343 }
344 goto error;
345 }
346 goto error;
347 }
348
349 while (isdigit ((int) *p) || *p == ':' || *p == '.')
350 {
351 if (*p == ':')
352 {
353 if (separator)
354 goto error;
355
356 separator = 1;
357 digit = 0;
358
359 if (dot)
360 {
361 if ((p - str) > INET_ADDRSTRLEN)
362 goto error;
363
364 memset (ipstr, 0, INET_ADDRSTRLEN + 1);
365 memcpy (ipstr, str, p - str);
366
367 ret = inet_aton (ipstr, &ip);
368 if (ret == 0)
369 goto error;
370 }
371 else
372 val_high = val_low;
373
374 val_low = 0;
375 }
376 else if (*p == '.')
377 {
378 if (separator)
379 goto error;
380 dot++;
381 if (dot > 4)
382 goto error;
383 }
384 else
385 {
386 digit = 1;
387 val_low *= 10;
388 val_low += (*p - '0');
389 }
390 p++;
391 }
392
393 /* Low digit part must be there. */
394 if (! digit || ! separator)
395 goto error;
396
397 /* Encode result into routing distinguisher. */
398 if (dot)
399 {
400 eval->val[0] = ECOMMUNITY_ENCODE_IP;
401 eval->val[1] = 0;
402 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
403 eval->val[6] = (val_low >> 8) & 0xff;
404 eval->val[7] = val_low & 0xff;
405 }
406 else
407 {
408 eval->val[0] = ECOMMUNITY_ENCODE_AS;
409 eval->val[1] = 0;
410 eval->val[2] = (val_high >>8) & 0xff;
411 eval->val[3] = val_high & 0xff;
412 eval->val[4] = (val_low >>24) & 0xff;
413 eval->val[5] = (val_low >>16) & 0xff;
414 eval->val[6] = (val_low >>8) & 0xff;
415 eval->val[7] = val_low & 0xff;
416 }
417 *token = ecommunity_token_val;
418 return p;
419
420 error:
421 *token = ecommunity_token_unknown;
422 return p;
423}
424
425/* Convert string to extended community attribute.
426
427 When type is already known, please specify both str and type. str
428 should not include keyword such as "rt" and "soo". Type is
429 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
430 keyword_included should be zero.
431
432 For example route-map's "set extcommunity" command case:
433
434 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
435 type = ECOMMUNITY_ROUTE_TARGET
436 keyword_included = 0
437
438 "soo 100:1" -> str = "100:1"
439 type = ECOMMUNITY_SITE_ORIGIN
440 keyword_included = 0
441
442 When string includes keyword for each extended community value.
443 Please specify keyword_included as non-zero value.
444
445 For example standard extcommunity-list case:
446
447 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
448 type = 0
449 keyword_include = 1
450*/
451struct ecommunity *
paulfd79ac92004-10-13 05:06:08 +0000452ecommunity_str2com (const char *str, int type, int keyword_included)
paul718e3742002-12-13 20:15:29 +0000453{
454 struct ecommunity *ecom = NULL;
455 enum ecommunity_token token;
456 struct ecommunity_val eval;
457 int keyword = 0;
458
459 while ((str = ecommunity_gettoken (str, &eval, &token)))
460 {
461 switch (token)
462 {
463 case ecommunity_token_rt:
464 case ecommunity_token_soo:
465 if (! keyword_included || keyword)
466 {
467 if (ecom)
468 ecommunity_free (ecom);
469 return NULL;
470 }
471 keyword = 1;
472
473 if (token == ecommunity_token_rt)
474 {
475 type = ECOMMUNITY_ROUTE_TARGET;
476 }
477 if (token == ecommunity_token_soo)
478 {
479 type = ECOMMUNITY_SITE_ORIGIN;
480 }
481 break;
482 case ecommunity_token_val:
483 if (keyword_included)
484 {
485 if (! keyword)
486 {
487 if (ecom)
488 ecommunity_free (ecom);
489 return NULL;
490 }
491 keyword = 0;
492 }
493 if (ecom == NULL)
494 ecom = ecommunity_new ();
495 eval.val[1] = type;
496 ecommunity_add_val (ecom, &eval);
497 break;
498 case ecommunity_token_unknown:
499 default:
500 if (ecom)
501 ecommunity_free (ecom);
502 return NULL;
503 break;
504 }
505 }
506 return ecom;
507}
508
509/* Convert extended community attribute to string.
510
511 Due to historical reason of industry standard implementation, there
512 are three types of format.
513
514 route-map set extcommunity format
515 "rt 100:1 100:2"
516 "soo 100:3"
517
518 extcommunity-list
519 "rt 100:1 rt 100:2 soo 100:3"
520
521 "show ip bgp" and extcommunity-list regular expression matching
522 "RT:100:1 RT:100:2 SoO:100:3"
523
524 For each formath please use below definition for format:
525
526 ECOMMUNITY_FORMAT_ROUTE_MAP
527 ECOMMUNITY_FORMAT_COMMUNITY_LIST
528 ECOMMUNITY_FORMAT_DISPLAY
529*/
530char *
531ecommunity_ecom2str (struct ecommunity *ecom, int format)
532{
533 int i;
paul5228ad22004-06-04 17:58:18 +0000534 u_int8_t *pnt;
paul718e3742002-12-13 20:15:29 +0000535 int encode = 0;
536 int type = 0;
537#define ECOMMUNITY_STR_DEFAULT_LEN 26
538 int str_size;
539 int str_pnt;
paul5228ad22004-06-04 17:58:18 +0000540 char *str_buf;
paulfd79ac92004-10-13 05:06:08 +0000541 const char *prefix;
paul718e3742002-12-13 20:15:29 +0000542 int len = 0;
543 int first = 1;
544
545 /* For parse Extended Community attribute tupple. */
546 struct ecommunity_as
547 {
548 as_t as;
549 u_int32_t val;
550 } eas;
551
552 struct ecommunity_ip
553 {
554 struct in_addr ip;
555 u_int16_t val;
556 } eip;
557
558 if (ecom->size == 0)
559 {
560 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
561 str_buf[0] = '\0';
562 return str_buf;
563 }
564
565 /* Prepare buffer. */
566 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
567 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
568 str_pnt = 0;
569
570 for (i = 0; i < ecom->size; i++)
571 {
hasso4372df72004-05-20 10:20:02 +0000572 /* Space between each value. */
573 if (! first)
574 str_buf[str_pnt++] = ' ';
575
paul718e3742002-12-13 20:15:29 +0000576 pnt = ecom->val + (i * 8);
577
578 /* High-order octet of type. */
579 encode = *pnt++;
580 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
581 {
hasso4372df72004-05-20 10:20:02 +0000582 len = sprintf (str_buf + str_pnt, "?");
583 str_pnt += len;
584 first = 0;
585 continue;
paul718e3742002-12-13 20:15:29 +0000586 }
587
588 /* Low-order octet of type. */
589 type = *pnt++;
590 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
591 {
hasso4372df72004-05-20 10:20:02 +0000592 len = sprintf (str_buf + str_pnt, "?");
593 str_pnt += len;
594 first = 0;
595 continue;
paul718e3742002-12-13 20:15:29 +0000596 }
597
598 switch (format)
599 {
600 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
601 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
602 break;
603 case ECOMMUNITY_FORMAT_DISPLAY:
604 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
605 break;
606 case ECOMMUNITY_FORMAT_ROUTE_MAP:
607 prefix = "";
608 break;
609 default:
hasso4372df72004-05-20 10:20:02 +0000610 prefix = "";
paul718e3742002-12-13 20:15:29 +0000611 break;
612 }
613
614 /* Make it sure size is enough. */
615 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
616 {
617 str_size *= 2;
618 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
619 }
620
paul718e3742002-12-13 20:15:29 +0000621 /* Put string into buffer. */
622 if (encode == ECOMMUNITY_ENCODE_AS)
623 {
624 eas.as = (*pnt++ << 8);
625 eas.as |= (*pnt++);
626
627 eas.val = (*pnt++ << 24);
628 eas.val |= (*pnt++ << 16);
629 eas.val |= (*pnt++ << 8);
630 eas.val |= (*pnt++);
631
632 len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix,
633 eas.as, eas.val);
634 str_pnt += len;
635 first = 0;
636 }
637 else if (encode == ECOMMUNITY_ENCODE_IP)
638 {
639 memcpy (&eip.ip, pnt, 4);
640 pnt += 4;
641 eip.val = (*pnt++ << 8);
642 eip.val |= (*pnt++);
643
644 len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
645 inet_ntoa (eip.ip), eip.val);
646 str_pnt += len;
647 first = 0;
648 }
649 }
650 return str_buf;
651}
hasso4372df72004-05-20 10:20:02 +0000652
653int
paulfd79ac92004-10-13 05:06:08 +0000654ecommunity_match (const struct ecommunity *ecom1,
655 const struct ecommunity *ecom2)
hasso4372df72004-05-20 10:20:02 +0000656{
657 int i = 0;
658 int j = 0;
659
660 if (ecom1 == NULL && ecom2 == NULL)
661 return 1;
662
663 if (ecom1 == NULL || ecom2 == NULL)
664 return 0;
665
666 if (ecom1->size < ecom2->size)
667 return 0;
668
669 /* Every community on com2 needs to be on com1 for this to match */
670 while (i < ecom1->size && j < ecom2->size)
671 {
672 if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
673 j++;
674 i++;
675 }
676
677 if (j == ecom2->size)
678 return 1;
679 else
680 return 0;
681}
682