blob: b3fc1c3a76c76177963a9aa43bddb5fe7dbc7cdf [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. */
100struct ecommunity *
101ecommunity_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
248ecommunity_cmp (struct ecommunity *ecom1, struct ecommunity *ecom2)
249{
250 if (ecom1->size == ecom2->size
251 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0)
252 return 1;
253 return 0;
254}
255
256/* Initialize Extended Comminities related hash. */
257void
258ecommunity_init ()
259{
260 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
261}
262
263/* Extended Communities token enum. */
264enum ecommunity_token
265{
266 ecommunity_token_rt,
267 ecommunity_token_soo,
268 ecommunity_token_val,
269 ecommunity_token_unknown
270};
271
272/* Get next Extended Communities token from the string. */
273char *
274ecommunity_gettoken (char *str, struct ecommunity_val *eval,
275 enum ecommunity_token *token)
276{
277 int ret;
278 int dot = 0;
279 int digit = 0;
280 int separator = 0;
281 u_int32_t val_low = 0;
282 u_int32_t val_high = 0;
283 char *p = str;
284 struct in_addr ip;
285 char ipstr[INET_ADDRSTRLEN + 1];
286
287 /* Skip white space. */
288 while (isspace ((int) *p))
289 {
290 p++;
291 str++;
292 }
293
294 /* Check the end of the line. */
295 if (*p == '\0')
296 return NULL;
297
298 /* "rt" and "soo" keyword parse. */
299 if (! isdigit ((int) *p))
300 {
301 /* "rt" match check. */
302 if (tolower ((int) *p) == 'r')
303 {
304 p++;
305 if (tolower ((int) *p) == 't')
306 {
307 p++;
308 *token = ecommunity_token_rt;
309 return p;
310 }
311 if (isspace ((int) *p) || *p == '\0')
312 {
313 *token = ecommunity_token_rt;
314 return p;
315 }
316 goto error;
317 }
318 /* "soo" match check. */
319 else if (tolower ((int) *p) == 's')
320 {
321 p++;
322 if (tolower ((int) *p) == 'o')
323 {
324 p++;
325 if (tolower ((int) *p) == 'o')
326 {
327 p++;
328 *token = ecommunity_token_soo;
329 return p;
330 }
331 if (isspace ((int) *p) || *p == '\0')
332 {
333 *token = ecommunity_token_soo;
334 return p;
335 }
336 goto error;
337 }
338 if (isspace ((int) *p) || *p == '\0')
339 {
340 *token = ecommunity_token_soo;
341 return p;
342 }
343 goto error;
344 }
345 goto error;
346 }
347
348 while (isdigit ((int) *p) || *p == ':' || *p == '.')
349 {
350 if (*p == ':')
351 {
352 if (separator)
353 goto error;
354
355 separator = 1;
356 digit = 0;
357
358 if (dot)
359 {
360 if ((p - str) > INET_ADDRSTRLEN)
361 goto error;
362
363 memset (ipstr, 0, INET_ADDRSTRLEN + 1);
364 memcpy (ipstr, str, p - str);
365
366 ret = inet_aton (ipstr, &ip);
367 if (ret == 0)
368 goto error;
369 }
370 else
371 val_high = val_low;
372
373 val_low = 0;
374 }
375 else if (*p == '.')
376 {
377 if (separator)
378 goto error;
379 dot++;
380 if (dot > 4)
381 goto error;
382 }
383 else
384 {
385 digit = 1;
386 val_low *= 10;
387 val_low += (*p - '0');
388 }
389 p++;
390 }
391
392 /* Low digit part must be there. */
393 if (! digit || ! separator)
394 goto error;
395
396 /* Encode result into routing distinguisher. */
397 if (dot)
398 {
399 eval->val[0] = ECOMMUNITY_ENCODE_IP;
400 eval->val[1] = 0;
401 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
402 eval->val[6] = (val_low >> 8) & 0xff;
403 eval->val[7] = val_low & 0xff;
404 }
405 else
406 {
407 eval->val[0] = ECOMMUNITY_ENCODE_AS;
408 eval->val[1] = 0;
409 eval->val[2] = (val_high >>8) & 0xff;
410 eval->val[3] = val_high & 0xff;
411 eval->val[4] = (val_low >>24) & 0xff;
412 eval->val[5] = (val_low >>16) & 0xff;
413 eval->val[6] = (val_low >>8) & 0xff;
414 eval->val[7] = val_low & 0xff;
415 }
416 *token = ecommunity_token_val;
417 return p;
418
419 error:
420 *token = ecommunity_token_unknown;
421 return p;
422}
423
424/* Convert string to extended community attribute.
425
426 When type is already known, please specify both str and type. str
427 should not include keyword such as "rt" and "soo". Type is
428 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
429 keyword_included should be zero.
430
431 For example route-map's "set extcommunity" command case:
432
433 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
434 type = ECOMMUNITY_ROUTE_TARGET
435 keyword_included = 0
436
437 "soo 100:1" -> str = "100:1"
438 type = ECOMMUNITY_SITE_ORIGIN
439 keyword_included = 0
440
441 When string includes keyword for each extended community value.
442 Please specify keyword_included as non-zero value.
443
444 For example standard extcommunity-list case:
445
446 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
447 type = 0
448 keyword_include = 1
449*/
450struct ecommunity *
451ecommunity_str2com (char *str, int type, int keyword_included)
452{
453 struct ecommunity *ecom = NULL;
454 enum ecommunity_token token;
455 struct ecommunity_val eval;
456 int keyword = 0;
457
458 while ((str = ecommunity_gettoken (str, &eval, &token)))
459 {
460 switch (token)
461 {
462 case ecommunity_token_rt:
463 case ecommunity_token_soo:
464 if (! keyword_included || keyword)
465 {
466 if (ecom)
467 ecommunity_free (ecom);
468 return NULL;
469 }
470 keyword = 1;
471
472 if (token == ecommunity_token_rt)
473 {
474 type = ECOMMUNITY_ROUTE_TARGET;
475 }
476 if (token == ecommunity_token_soo)
477 {
478 type = ECOMMUNITY_SITE_ORIGIN;
479 }
480 break;
481 case ecommunity_token_val:
482 if (keyword_included)
483 {
484 if (! keyword)
485 {
486 if (ecom)
487 ecommunity_free (ecom);
488 return NULL;
489 }
490 keyword = 0;
491 }
492 if (ecom == NULL)
493 ecom = ecommunity_new ();
494 eval.val[1] = type;
495 ecommunity_add_val (ecom, &eval);
496 break;
497 case ecommunity_token_unknown:
498 default:
499 if (ecom)
500 ecommunity_free (ecom);
501 return NULL;
502 break;
503 }
504 }
505 return ecom;
506}
507
508/* Convert extended community attribute to string.
509
510 Due to historical reason of industry standard implementation, there
511 are three types of format.
512
513 route-map set extcommunity format
514 "rt 100:1 100:2"
515 "soo 100:3"
516
517 extcommunity-list
518 "rt 100:1 rt 100:2 soo 100:3"
519
520 "show ip bgp" and extcommunity-list regular expression matching
521 "RT:100:1 RT:100:2 SoO:100:3"
522
523 For each formath please use below definition for format:
524
525 ECOMMUNITY_FORMAT_ROUTE_MAP
526 ECOMMUNITY_FORMAT_COMMUNITY_LIST
527 ECOMMUNITY_FORMAT_DISPLAY
528*/
529char *
530ecommunity_ecom2str (struct ecommunity *ecom, int format)
531{
532 int i;
paul5228ad22004-06-04 17:58:18 +0000533 u_int8_t *pnt;
paul718e3742002-12-13 20:15:29 +0000534 int encode = 0;
535 int type = 0;
536#define ECOMMUNITY_STR_DEFAULT_LEN 26
537 int str_size;
538 int str_pnt;
paul5228ad22004-06-04 17:58:18 +0000539 char *str_buf;
paul718e3742002-12-13 20:15:29 +0000540 char *prefix;
541 int len = 0;
542 int first = 1;
543
544 /* For parse Extended Community attribute tupple. */
545 struct ecommunity_as
546 {
547 as_t as;
548 u_int32_t val;
549 } eas;
550
551 struct ecommunity_ip
552 {
553 struct in_addr ip;
554 u_int16_t val;
555 } eip;
556
557 if (ecom->size == 0)
558 {
559 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
560 str_buf[0] = '\0';
561 return str_buf;
562 }
563
564 /* Prepare buffer. */
565 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
566 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
567 str_pnt = 0;
568
569 for (i = 0; i < ecom->size; i++)
570 {
hasso4372df72004-05-20 10:20:02 +0000571 /* Space between each value. */
572 if (! first)
573 str_buf[str_pnt++] = ' ';
574
paul718e3742002-12-13 20:15:29 +0000575 pnt = ecom->val + (i * 8);
576
577 /* High-order octet of type. */
578 encode = *pnt++;
579 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
580 {
hasso4372df72004-05-20 10:20:02 +0000581 len = sprintf (str_buf + str_pnt, "?");
582 str_pnt += len;
583 first = 0;
584 continue;
paul718e3742002-12-13 20:15:29 +0000585 }
586
587 /* Low-order octet of type. */
588 type = *pnt++;
589 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
590 {
hasso4372df72004-05-20 10:20:02 +0000591 len = sprintf (str_buf + str_pnt, "?");
592 str_pnt += len;
593 first = 0;
594 continue;
paul718e3742002-12-13 20:15:29 +0000595 }
596
597 switch (format)
598 {
599 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
600 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
601 break;
602 case ECOMMUNITY_FORMAT_DISPLAY:
603 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
604 break;
605 case ECOMMUNITY_FORMAT_ROUTE_MAP:
606 prefix = "";
607 break;
608 default:
hasso4372df72004-05-20 10:20:02 +0000609 prefix = "";
paul718e3742002-12-13 20:15:29 +0000610 break;
611 }
612
613 /* Make it sure size is enough. */
614 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
615 {
616 str_size *= 2;
617 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
618 }
619
paul718e3742002-12-13 20:15:29 +0000620 /* Put string into buffer. */
621 if (encode == ECOMMUNITY_ENCODE_AS)
622 {
623 eas.as = (*pnt++ << 8);
624 eas.as |= (*pnt++);
625
626 eas.val = (*pnt++ << 24);
627 eas.val |= (*pnt++ << 16);
628 eas.val |= (*pnt++ << 8);
629 eas.val |= (*pnt++);
630
631 len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix,
632 eas.as, eas.val);
633 str_pnt += len;
634 first = 0;
635 }
636 else if (encode == ECOMMUNITY_ENCODE_IP)
637 {
638 memcpy (&eip.ip, pnt, 4);
639 pnt += 4;
640 eip.val = (*pnt++ << 8);
641 eip.val |= (*pnt++);
642
643 len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
644 inet_ntoa (eip.ip), eip.val);
645 str_pnt += len;
646 first = 0;
647 }
648 }
649 return str_buf;
650}
hasso4372df72004-05-20 10:20:02 +0000651
652int
653ecommunity_match (struct ecommunity *ecom1, struct ecommunity *ecom2)
654{
655 int i = 0;
656 int j = 0;
657
658 if (ecom1 == NULL && ecom2 == NULL)
659 return 1;
660
661 if (ecom1 == NULL || ecom2 == NULL)
662 return 0;
663
664 if (ecom1->size < ecom2->size)
665 return 0;
666
667 /* Every community on com2 needs to be on com1 for this to match */
668 while (i < ecom1->size && j < ecom2->size)
669 {
670 if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
671 j++;
672 i++;
673 }
674
675 if (j == ecom2->size)
676 return 1;
677 else
678 return 0;
679}
680