blob: 2f9cc9459df1bd58b3596eb186636bfacee629e8 [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{
61 u_char *p;
62 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 *
122ecommunity_parse (char *pnt, u_short length)
123{
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
161/* Merge two Extended Communities Attribute structure. */
162struct ecommunity *
163ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
164{
165 if (ecom1->val)
166 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
167 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
168 else
169 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
170 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
171
172 memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
173 ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
174 ecom1->size += ecom2->size;
175
176 return ecom1;
177}
178
179/* Intern Extended Communities Attribute. */
180struct ecommunity *
181ecommunity_intern (struct ecommunity *ecom)
182{
183 struct ecommunity *find;
184
185 assert (ecom->refcnt == 0);
186
187 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
188
189 if (find != ecom)
190 ecommunity_free (ecom);
191
192 find->refcnt++;
193
194 if (! find->str)
195 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
196
197 return find;
198}
199
200/* Unintern Extended Communities Attribute. */
201void
202ecommunity_unintern (struct ecommunity *ecom)
203{
204 struct ecommunity *ret;
205
206 if (ecom->refcnt)
207 ecom->refcnt--;
208
209 /* Pull off from hash. */
210 if (ecom->refcnt == 0)
211 {
212 /* Extended community must be in the hash. */
213 ret = (struct ecommunity *) hash_release (ecomhash, ecom);
214 assert (ret != NULL);
215
216 ecommunity_free (ecom);
217 }
218}
219
220/* Utinity function to make hash key. */
221unsigned int
222ecommunity_hash_make (struct ecommunity *ecom)
223{
224 int c;
225 unsigned int key;
226 unsigned char *pnt;
227
228 key = 0;
229 pnt = ecom->val;
230
231 for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
232 key += pnt[c];
233
234 return key;
235}
236
237/* Compare two Extended Communities Attribute structure. */
238int
239ecommunity_cmp (struct ecommunity *ecom1, struct ecommunity *ecom2)
240{
241 if (ecom1->size == ecom2->size
242 && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0)
243 return 1;
244 return 0;
245}
246
247/* Initialize Extended Comminities related hash. */
248void
249ecommunity_init ()
250{
251 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
252}
253
254/* Extended Communities token enum. */
255enum ecommunity_token
256{
257 ecommunity_token_rt,
258 ecommunity_token_soo,
259 ecommunity_token_val,
260 ecommunity_token_unknown
261};
262
263/* Get next Extended Communities token from the string. */
264char *
265ecommunity_gettoken (char *str, struct ecommunity_val *eval,
266 enum ecommunity_token *token)
267{
268 int ret;
269 int dot = 0;
270 int digit = 0;
271 int separator = 0;
272 u_int32_t val_low = 0;
273 u_int32_t val_high = 0;
274 char *p = str;
275 struct in_addr ip;
276 char ipstr[INET_ADDRSTRLEN + 1];
277
278 /* Skip white space. */
279 while (isspace ((int) *p))
280 {
281 p++;
282 str++;
283 }
284
285 /* Check the end of the line. */
286 if (*p == '\0')
287 return NULL;
288
289 /* "rt" and "soo" keyword parse. */
290 if (! isdigit ((int) *p))
291 {
292 /* "rt" match check. */
293 if (tolower ((int) *p) == 'r')
294 {
295 p++;
296 if (tolower ((int) *p) == 't')
297 {
298 p++;
299 *token = ecommunity_token_rt;
300 return p;
301 }
302 if (isspace ((int) *p) || *p == '\0')
303 {
304 *token = ecommunity_token_rt;
305 return p;
306 }
307 goto error;
308 }
309 /* "soo" match check. */
310 else if (tolower ((int) *p) == 's')
311 {
312 p++;
313 if (tolower ((int) *p) == 'o')
314 {
315 p++;
316 if (tolower ((int) *p) == 'o')
317 {
318 p++;
319 *token = ecommunity_token_soo;
320 return p;
321 }
322 if (isspace ((int) *p) || *p == '\0')
323 {
324 *token = ecommunity_token_soo;
325 return p;
326 }
327 goto error;
328 }
329 if (isspace ((int) *p) || *p == '\0')
330 {
331 *token = ecommunity_token_soo;
332 return p;
333 }
334 goto error;
335 }
336 goto error;
337 }
338
339 while (isdigit ((int) *p) || *p == ':' || *p == '.')
340 {
341 if (*p == ':')
342 {
343 if (separator)
344 goto error;
345
346 separator = 1;
347 digit = 0;
348
349 if (dot)
350 {
351 if ((p - str) > INET_ADDRSTRLEN)
352 goto error;
353
354 memset (ipstr, 0, INET_ADDRSTRLEN + 1);
355 memcpy (ipstr, str, p - str);
356
357 ret = inet_aton (ipstr, &ip);
358 if (ret == 0)
359 goto error;
360 }
361 else
362 val_high = val_low;
363
364 val_low = 0;
365 }
366 else if (*p == '.')
367 {
368 if (separator)
369 goto error;
370 dot++;
371 if (dot > 4)
372 goto error;
373 }
374 else
375 {
376 digit = 1;
377 val_low *= 10;
378 val_low += (*p - '0');
379 }
380 p++;
381 }
382
383 /* Low digit part must be there. */
384 if (! digit || ! separator)
385 goto error;
386
387 /* Encode result into routing distinguisher. */
388 if (dot)
389 {
390 eval->val[0] = ECOMMUNITY_ENCODE_IP;
391 eval->val[1] = 0;
392 memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
393 eval->val[6] = (val_low >> 8) & 0xff;
394 eval->val[7] = val_low & 0xff;
395 }
396 else
397 {
398 eval->val[0] = ECOMMUNITY_ENCODE_AS;
399 eval->val[1] = 0;
400 eval->val[2] = (val_high >>8) & 0xff;
401 eval->val[3] = val_high & 0xff;
402 eval->val[4] = (val_low >>24) & 0xff;
403 eval->val[5] = (val_low >>16) & 0xff;
404 eval->val[6] = (val_low >>8) & 0xff;
405 eval->val[7] = val_low & 0xff;
406 }
407 *token = ecommunity_token_val;
408 return p;
409
410 error:
411 *token = ecommunity_token_unknown;
412 return p;
413}
414
415/* Convert string to extended community attribute.
416
417 When type is already known, please specify both str and type. str
418 should not include keyword such as "rt" and "soo". Type is
419 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
420 keyword_included should be zero.
421
422 For example route-map's "set extcommunity" command case:
423
424 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
425 type = ECOMMUNITY_ROUTE_TARGET
426 keyword_included = 0
427
428 "soo 100:1" -> str = "100:1"
429 type = ECOMMUNITY_SITE_ORIGIN
430 keyword_included = 0
431
432 When string includes keyword for each extended community value.
433 Please specify keyword_included as non-zero value.
434
435 For example standard extcommunity-list case:
436
437 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
438 type = 0
439 keyword_include = 1
440*/
441struct ecommunity *
442ecommunity_str2com (char *str, int type, int keyword_included)
443{
444 struct ecommunity *ecom = NULL;
445 enum ecommunity_token token;
446 struct ecommunity_val eval;
447 int keyword = 0;
448
449 while ((str = ecommunity_gettoken (str, &eval, &token)))
450 {
451 switch (token)
452 {
453 case ecommunity_token_rt:
454 case ecommunity_token_soo:
455 if (! keyword_included || keyword)
456 {
457 if (ecom)
458 ecommunity_free (ecom);
459 return NULL;
460 }
461 keyword = 1;
462
463 if (token == ecommunity_token_rt)
464 {
465 type = ECOMMUNITY_ROUTE_TARGET;
466 }
467 if (token == ecommunity_token_soo)
468 {
469 type = ECOMMUNITY_SITE_ORIGIN;
470 }
471 break;
472 case ecommunity_token_val:
473 if (keyword_included)
474 {
475 if (! keyword)
476 {
477 if (ecom)
478 ecommunity_free (ecom);
479 return NULL;
480 }
481 keyword = 0;
482 }
483 if (ecom == NULL)
484 ecom = ecommunity_new ();
485 eval.val[1] = type;
486 ecommunity_add_val (ecom, &eval);
487 break;
488 case ecommunity_token_unknown:
489 default:
490 if (ecom)
491 ecommunity_free (ecom);
492 return NULL;
493 break;
494 }
495 }
496 return ecom;
497}
498
499/* Convert extended community attribute to string.
500
501 Due to historical reason of industry standard implementation, there
502 are three types of format.
503
504 route-map set extcommunity format
505 "rt 100:1 100:2"
506 "soo 100:3"
507
508 extcommunity-list
509 "rt 100:1 rt 100:2 soo 100:3"
510
511 "show ip bgp" and extcommunity-list regular expression matching
512 "RT:100:1 RT:100:2 SoO:100:3"
513
514 For each formath please use below definition for format:
515
516 ECOMMUNITY_FORMAT_ROUTE_MAP
517 ECOMMUNITY_FORMAT_COMMUNITY_LIST
518 ECOMMUNITY_FORMAT_DISPLAY
519*/
520char *
521ecommunity_ecom2str (struct ecommunity *ecom, int format)
522{
523 int i;
524 u_char *pnt;
525 int encode = 0;
526 int type = 0;
527#define ECOMMUNITY_STR_DEFAULT_LEN 26
528 int str_size;
529 int str_pnt;
530 u_char *str_buf;
531 char *prefix;
532 int len = 0;
533 int first = 1;
534
535 /* For parse Extended Community attribute tupple. */
536 struct ecommunity_as
537 {
538 as_t as;
539 u_int32_t val;
540 } eas;
541
542 struct ecommunity_ip
543 {
544 struct in_addr ip;
545 u_int16_t val;
546 } eip;
547
548 if (ecom->size == 0)
549 {
550 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
551 str_buf[0] = '\0';
552 return str_buf;
553 }
554
555 /* Prepare buffer. */
556 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
557 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
558 str_pnt = 0;
559
560 for (i = 0; i < ecom->size; i++)
561 {
562 pnt = ecom->val + (i * 8);
563
564 /* High-order octet of type. */
565 encode = *pnt++;
566 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP)
567 {
568 if (str_buf)
569 XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
570 return "Unknown";
571 }
572
573 /* Low-order octet of type. */
574 type = *pnt++;
575 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
576 {
577 if (str_buf)
578 XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
579 return "Unknown";
580 }
581
582 switch (format)
583 {
584 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
585 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
586 break;
587 case ECOMMUNITY_FORMAT_DISPLAY:
588 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
589 break;
590 case ECOMMUNITY_FORMAT_ROUTE_MAP:
591 prefix = "";
592 break;
593 default:
594 if (str_buf)
595 XFREE (MTYPE_ECOMMUNITY_STR, str_buf);
596 return "Unknown";
597 break;
598 }
599
600 /* Make it sure size is enough. */
601 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
602 {
603 str_size *= 2;
604 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
605 }
606
607 /* Space between each value. */
608 if (! first)
609 str_buf[str_pnt++] = ' ';
610
611 /* Put string into buffer. */
612 if (encode == ECOMMUNITY_ENCODE_AS)
613 {
614 eas.as = (*pnt++ << 8);
615 eas.as |= (*pnt++);
616
617 eas.val = (*pnt++ << 24);
618 eas.val |= (*pnt++ << 16);
619 eas.val |= (*pnt++ << 8);
620 eas.val |= (*pnt++);
621
622 len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix,
623 eas.as, eas.val);
624 str_pnt += len;
625 first = 0;
626 }
627 else if (encode == ECOMMUNITY_ENCODE_IP)
628 {
629 memcpy (&eip.ip, pnt, 4);
630 pnt += 4;
631 eip.val = (*pnt++ << 8);
632 eip.val |= (*pnt++);
633
634 len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
635 inet_ntoa (eip.ip), eip.val);
636 str_pnt += len;
637 first = 0;
638 }
639 }
640 return str_buf;
641}