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
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
28 #include "bgpd/bgpd.h"
29 #include "bgpd/bgp_ecommunity.h"
31 /* Hash of community attribute. */
32 struct hash
*ecomhash
;
34 /* Allocate a new ecommunities. */
38 return (struct ecommunity
*) XCALLOC (MTYPE_ECOMMUNITY
,
39 sizeof (struct ecommunity
));
42 /* Allocate ecommunities. */
44 ecommunity_free (struct ecommunity
*ecom
)
47 XFREE (MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
49 XFREE (MTYPE_ECOMMUNITY_STR
, ecom
->str
);
50 XFREE (MTYPE_ECOMMUNITY
, ecom
);
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
59 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
65 /* When this is fist value, just add it. */
66 if (ecom
->val
== NULL
)
69 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
70 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
74 /* If the value already exists in the structure return 0. */
76 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
78 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
85 /* Add the value to the structure with numerical sorting. */
87 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
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
);
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. */
101 ecommunity_uniq_sort (struct ecommunity
*ecom
)
104 struct ecommunity
*new;
105 struct ecommunity_val
*eval
;
110 new = ecommunity_new ();;
112 for (i
= 0; i
< ecom
->size
; i
++)
114 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
115 ecommunity_add_val (new, eval
);
120 /* Parse Extended Communites Attribute in BGP packet. */
122 ecommunity_parse (char *pnt
, u_short length
)
124 struct ecommunity tmp
;
125 struct ecommunity
*new;
128 if (length
% ECOMMUNITY_SIZE
)
131 /* Prepare tmporary structure for making a new Extended Communities
133 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
136 /* Create a new Extended Communities Attribute by uniq and sort each
137 Extended Communities value */
138 new = ecommunity_uniq_sort (&tmp
);
140 return ecommunity_intern (new);
143 /* Duplicate the Extended Communities Attribute structure. */
145 ecommunity_dup (struct ecommunity
*ecom
)
147 struct ecommunity
*new;
149 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
150 new->size
= ecom
->size
;
153 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
154 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
161 /* Merge two Extended Communities Attribute structure. */
163 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
166 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
167 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
169 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
170 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
172 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
173 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
174 ecom1
->size
+= ecom2
->size
;
179 /* Intern Extended Communities Attribute. */
181 ecommunity_intern (struct ecommunity
*ecom
)
183 struct ecommunity
*find
;
185 assert (ecom
->refcnt
== 0);
187 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
190 ecommunity_free (ecom
);
195 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
);
200 /* Unintern Extended Communities Attribute. */
202 ecommunity_unintern (struct ecommunity
*ecom
)
204 struct ecommunity
*ret
;
209 /* Pull off from hash. */
210 if (ecom
->refcnt
== 0)
212 /* Extended community must be in the hash. */
213 ret
= (struct ecommunity
*) hash_release (ecomhash
, ecom
);
214 assert (ret
!= NULL
);
216 ecommunity_free (ecom
);
220 /* Utinity function to make hash key. */
222 ecommunity_hash_make (struct ecommunity
*ecom
)
231 for (c
= 0; c
< ecom
->size
* ECOMMUNITY_SIZE
; c
++)
237 /* Compare two Extended Communities Attribute structure. */
239 ecommunity_cmp (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
241 if (ecom1
->size
== ecom2
->size
242 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0)
247 /* Initialize Extended Comminities related hash. */
251 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
254 /* Extended Communities token enum. */
255 enum ecommunity_token
258 ecommunity_token_soo
,
259 ecommunity_token_val
,
260 ecommunity_token_unknown
263 /* Get next Extended Communities token from the string. */
265 ecommunity_gettoken (char *str
, struct ecommunity_val
*eval
,
266 enum ecommunity_token
*token
)
272 u_int32_t val_low
= 0;
273 u_int32_t val_high
= 0;
276 char ipstr
[INET_ADDRSTRLEN
+ 1];
278 /* Skip white space. */
279 while (isspace ((int) *p
))
285 /* Check the end of the line. */
289 /* "rt" and "soo" keyword parse. */
290 if (! isdigit ((int) *p
))
292 /* "rt" match check. */
293 if (tolower ((int) *p
) == 'r')
296 if (tolower ((int) *p
) == 't')
299 *token
= ecommunity_token_rt
;
302 if (isspace ((int) *p
) || *p
== '\0')
304 *token
= ecommunity_token_rt
;
309 /* "soo" match check. */
310 else if (tolower ((int) *p
) == 's')
313 if (tolower ((int) *p
) == 'o')
316 if (tolower ((int) *p
) == 'o')
319 *token
= ecommunity_token_soo
;
322 if (isspace ((int) *p
) || *p
== '\0')
324 *token
= ecommunity_token_soo
;
329 if (isspace ((int) *p
) || *p
== '\0')
331 *token
= ecommunity_token_soo
;
339 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
351 if ((p
- str
) > INET_ADDRSTRLEN
)
354 memset (ipstr
, 0, INET_ADDRSTRLEN
+ 1);
355 memcpy (ipstr
, str
, p
- str
);
357 ret
= inet_aton (ipstr
, &ip
);
378 val_low
+= (*p
- '0');
383 /* Low digit part must be there. */
384 if (! digit
|| ! separator
)
387 /* Encode result into routing distinguisher. */
390 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
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;
398 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
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;
407 *token
= ecommunity_token_val
;
411 *token
= ecommunity_token_unknown
;
415 /* Convert string to extended community attribute.
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.
422 For example route-map's "set extcommunity" command case:
424 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
425 type = ECOMMUNITY_ROUTE_TARGET
428 "soo 100:1" -> str = "100:1"
429 type = ECOMMUNITY_SITE_ORIGIN
432 When string includes keyword for each extended community value.
433 Please specify keyword_included as non-zero value.
435 For example standard extcommunity-list case:
437 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
442 ecommunity_str2com (char *str
, int type
, int keyword_included
)
444 struct ecommunity
*ecom
= NULL
;
445 enum ecommunity_token token
;
446 struct ecommunity_val eval
;
449 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
453 case ecommunity_token_rt
:
454 case ecommunity_token_soo
:
455 if (! keyword_included
|| keyword
)
458 ecommunity_free (ecom
);
463 if (token
== ecommunity_token_rt
)
465 type
= ECOMMUNITY_ROUTE_TARGET
;
467 if (token
== ecommunity_token_soo
)
469 type
= ECOMMUNITY_SITE_ORIGIN
;
472 case ecommunity_token_val
:
473 if (keyword_included
)
478 ecommunity_free (ecom
);
484 ecom
= ecommunity_new ();
486 ecommunity_add_val (ecom
, &eval
);
488 case ecommunity_token_unknown
:
491 ecommunity_free (ecom
);
499 /* Convert extended community attribute to string.
501 Due to historical reason of industry standard implementation, there
502 are three types of format.
504 route-map set extcommunity format
509 "rt 100:1 rt 100:2 soo 100:3"
511 "show ip bgp" and extcommunity-list regular expression matching
512 "RT:100:1 RT:100:2 SoO:100:3"
514 For each formath please use below definition for format:
516 ECOMMUNITY_FORMAT_ROUTE_MAP
517 ECOMMUNITY_FORMAT_COMMUNITY_LIST
518 ECOMMUNITY_FORMAT_DISPLAY
521 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
)
527 #define ECOMMUNITY_STR_DEFAULT_LEN 26
535 /* For parse Extended Community attribute tupple. */
550 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
555 /* Prepare buffer. */
556 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
557 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
560 for (i
= 0; i
< ecom
->size
; i
++)
562 pnt
= ecom
->val
+ (i
* 8);
564 /* High-order octet of type. */
566 if (encode
!= ECOMMUNITY_ENCODE_AS
&& encode
!= ECOMMUNITY_ENCODE_IP
)
569 XFREE (MTYPE_ECOMMUNITY_STR
, str_buf
);
573 /* Low-order octet of type. */
575 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
578 XFREE (MTYPE_ECOMMUNITY_STR
, str_buf
);
584 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
585 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
587 case ECOMMUNITY_FORMAT_DISPLAY
:
588 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
590 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
595 XFREE (MTYPE_ECOMMUNITY_STR
, str_buf
);
600 /* Make it sure size is enough. */
601 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
604 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
607 /* Space between each value. */
609 str_buf
[str_pnt
++] = ' ';
611 /* Put string into buffer. */
612 if (encode
== ECOMMUNITY_ENCODE_AS
)
614 eas
.as
= (*pnt
++ << 8);
617 eas
.val
= (*pnt
++ << 24);
618 eas
.val
|= (*pnt
++ << 16);
619 eas
.val
|= (*pnt
++ << 8);
622 len
= sprintf (str_buf
+ str_pnt
, "%s%d:%d", prefix
,
627 else if (encode
== ECOMMUNITY_ENCODE_IP
)
629 memcpy (&eip
.ip
, pnt
, 4);
631 eip
.val
= (*pnt
++ << 8);
634 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%d", prefix
,
635 inet_ntoa (eip
.ip
), eip
.val
);