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"
30 #include "bgpd/bgp_aspath.h"
32 /* Hash of community attribute. */
33 static struct hash
*ecomhash
;
35 /* Allocate a new ecommunities. */
36 static struct ecommunity
*
39 return (struct ecommunity
*) XCALLOC (MTYPE_ECOMMUNITY
,
40 sizeof (struct ecommunity
));
43 /* Allocate ecommunities. */
45 ecommunity_free (struct ecommunity
*ecom
)
48 XFREE (MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
50 XFREE (MTYPE_ECOMMUNITY_STR
, ecom
->str
);
51 XFREE (MTYPE_ECOMMUNITY
, ecom
);
54 /* Add a new Extended Communities value to Extended Communities
55 Attribute structure. When the value is already exists in the
56 structure, we don't add the value. Newly added value is sorted by
57 numerical order. When the value is added to the structure return 1
60 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
66 /* When this is fist value, just add it. */
67 if (ecom
->val
== NULL
)
70 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
71 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
75 /* If the value already exists in the structure return 0. */
77 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
79 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
86 /* Add the value to the structure with numerical sorting. */
88 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
90 memmove (ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
91 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
92 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
93 memcpy (ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
98 /* This function takes pointer to Extended Communites strucutre then
99 create a new Extended Communities structure by uniq and sort each
100 Extended Communities value. */
101 static struct ecommunity
*
102 ecommunity_uniq_sort (struct ecommunity
*ecom
)
105 struct ecommunity
*new;
106 struct ecommunity_val
*eval
;
111 new = ecommunity_new ();
113 for (i
= 0; i
< ecom
->size
; i
++)
115 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
116 ecommunity_add_val (new, eval
);
121 /* Parse Extended Communites Attribute in BGP packet. */
123 ecommunity_parse (u_int8_t
*pnt
, u_short length
)
125 struct ecommunity tmp
;
126 struct ecommunity
*new;
129 if (length
% ECOMMUNITY_SIZE
)
132 /* Prepare tmporary structure for making a new Extended Communities
134 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
137 /* Create a new Extended Communities Attribute by uniq and sort each
138 Extended Communities value */
139 new = ecommunity_uniq_sort (&tmp
);
141 return ecommunity_intern (new);
144 /* Duplicate the Extended Communities Attribute structure. */
146 ecommunity_dup (struct ecommunity
*ecom
)
148 struct ecommunity
*new;
150 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
151 new->size
= ecom
->size
;
154 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
155 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
162 /* Retrun string representation of communities attribute. */
164 ecommunity_str (struct ecommunity
*ecom
)
167 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
171 /* Merge two Extended Communities Attribute structure. */
173 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
176 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
177 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
179 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
180 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
182 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
183 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
184 ecom1
->size
+= ecom2
->size
;
189 /* Intern Extended Communities Attribute. */
191 ecommunity_intern (struct ecommunity
*ecom
)
193 struct ecommunity
*find
;
195 assert (ecom
->refcnt
== 0);
197 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
200 ecommunity_free (ecom
);
205 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
);
210 /* Unintern Extended Communities Attribute. */
212 ecommunity_unintern (struct ecommunity
*ecom
)
214 struct ecommunity
*ret
;
219 /* Pull off from hash. */
220 if (ecom
->refcnt
== 0)
222 /* Extended community must be in the hash. */
223 ret
= (struct ecommunity
*) hash_release (ecomhash
, ecom
);
224 assert (ret
!= NULL
);
226 ecommunity_free (ecom
);
230 /* Utinity function to make hash key. */
232 ecommunity_hash_make (void *arg
)
234 const struct ecommunity
*ecom
= arg
;
242 for (c
= 0; c
< ecom
->size
* ECOMMUNITY_SIZE
; c
++)
248 /* Compare two Extended Communities Attribute structure. */
250 ecommunity_cmp (const void *arg1
, const void *arg2
)
252 const struct ecommunity
*ecom1
= arg1
;
253 const struct ecommunity
*ecom2
= arg2
;
255 return (ecom1
->size
== ecom2
->size
256 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0);
259 /* Initialize Extended Comminities related hash. */
261 ecommunity_init (void)
263 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
267 ecommunity_finish (void)
269 hash_free (ecomhash
);
273 /* Extended Communities token enum. */
274 enum ecommunity_token
277 ecommunity_token_soo
,
278 ecommunity_token_val
,
279 ecommunity_token_unknown
282 /* Get next Extended Communities token from the string. */
284 ecommunity_gettoken (const char *str
, struct ecommunity_val
*eval
,
285 enum ecommunity_token
*token
)
296 char buf
[INET_ADDRSTRLEN
+ 1];
298 /* Skip white space. */
299 while (isspace ((int) *p
))
305 /* Check the end of the line. */
309 /* "rt" and "soo" keyword parse. */
310 if (! isdigit ((int) *p
))
312 /* "rt" match check. */
313 if (tolower ((int) *p
) == 'r')
316 if (tolower ((int) *p
) == 't')
319 *token
= ecommunity_token_rt
;
322 if (isspace ((int) *p
) || *p
== '\0')
324 *token
= ecommunity_token_rt
;
329 /* "soo" match check. */
330 else if (tolower ((int) *p
) == 's')
333 if (tolower ((int) *p
) == 'o')
336 if (tolower ((int) *p
) == 'o')
339 *token
= ecommunity_token_soo
;
342 if (isspace ((int) *p
) || *p
== '\0')
344 *token
= ecommunity_token_soo
;
349 if (isspace ((int) *p
) || *p
== '\0')
351 *token
= ecommunity_token_soo
;
359 /* What a mess, there are several possibilities:
365 * A.B.C.D: Four Byte IP
367 * GHJK: Four-byte ASN
369 * OPQR: Four byte value
372 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
382 if ((p
- str
) > INET_ADDRSTRLEN
)
384 memset (buf
, 0, INET_ADDRSTRLEN
+ 1);
385 memcpy (buf
, str
, p
- str
);
389 /* Parsing A.B.C.D in:
392 ret
= inet_aton (buf
, &ip
);
399 as
= strtoul (buf
, &endptr
, 10);
400 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
416 /* We're past the IP/ASN part */
426 /* Low digit part must be there. */
427 if (!digit
|| !separator
)
430 /* Encode result into routing distinguisher. */
433 if (val
> UINT16_MAX
)
436 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
438 memcpy (&eval
->val
[2], &ip
, sizeof (struct in_addr
));
439 eval
->val
[6] = (val
>> 8) & 0xff;
440 eval
->val
[7] = val
& 0xff;
442 else if (as
> BGP_AS_MAX
)
444 if (val
> UINT16_MAX
)
447 eval
->val
[0] = ECOMMUNITY_ENCODE_AS4
;
449 eval
->val
[2] = (as
>>24) & 0xff;
450 eval
->val
[3] = (as
>>16) & 0xff;
451 eval
->val
[4] = (as
>>8) & 0xff;
452 eval
->val
[5] = as
& 0xff;
453 eval
->val
[6] = (val
>> 8) & 0xff;
454 eval
->val
[7] = val
& 0xff;
458 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
461 eval
->val
[2] = (as
>>8) & 0xff;
462 eval
->val
[3] = as
& 0xff;
463 eval
->val
[4] = (val
>>24) & 0xff;
464 eval
->val
[5] = (val
>>16) & 0xff;
465 eval
->val
[6] = (val
>>8) & 0xff;
466 eval
->val
[7] = val
& 0xff;
468 *token
= ecommunity_token_val
;
472 *token
= ecommunity_token_unknown
;
476 /* Convert string to extended community attribute.
478 When type is already known, please specify both str and type. str
479 should not include keyword such as "rt" and "soo". Type is
480 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
481 keyword_included should be zero.
483 For example route-map's "set extcommunity" command case:
485 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
486 type = ECOMMUNITY_ROUTE_TARGET
489 "soo 100:1" -> str = "100:1"
490 type = ECOMMUNITY_SITE_ORIGIN
493 When string includes keyword for each extended community value.
494 Please specify keyword_included as non-zero value.
496 For example standard extcommunity-list case:
498 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
503 ecommunity_str2com (const char *str
, int type
, int keyword_included
)
505 struct ecommunity
*ecom
= NULL
;
506 enum ecommunity_token token
;
507 struct ecommunity_val eval
;
510 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
514 case ecommunity_token_rt
:
515 case ecommunity_token_soo
:
516 if (! keyword_included
|| keyword
)
519 ecommunity_free (ecom
);
524 if (token
== ecommunity_token_rt
)
526 type
= ECOMMUNITY_ROUTE_TARGET
;
528 if (token
== ecommunity_token_soo
)
530 type
= ECOMMUNITY_SITE_ORIGIN
;
533 case ecommunity_token_val
:
534 if (keyword_included
)
539 ecommunity_free (ecom
);
545 ecom
= ecommunity_new ();
547 ecommunity_add_val (ecom
, &eval
);
549 case ecommunity_token_unknown
:
552 ecommunity_free (ecom
);
559 /* Convert extended community attribute to string.
561 Due to historical reason of industry standard implementation, there
562 are three types of format.
564 route-map set extcommunity format
569 "rt 100:1 rt 100:2 soo 100:3"
571 "show ip bgp" and extcommunity-list regular expression matching
572 "RT:100:1 RT:100:2 SoO:100:3"
574 For each formath please use below definition for format:
576 ECOMMUNITY_FORMAT_ROUTE_MAP
577 ECOMMUNITY_FORMAT_COMMUNITY_LIST
578 ECOMMUNITY_FORMAT_DISPLAY
581 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
)
587 #define ECOMMUNITY_STR_DEFAULT_LEN 27
595 /* For parse Extended Community attribute tupple. */
610 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
615 /* Prepare buffer. */
616 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
617 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
620 for (i
= 0; i
< ecom
->size
; i
++)
622 /* Space between each value. */
624 str_buf
[str_pnt
++] = ' ';
626 pnt
= ecom
->val
+ (i
* 8);
628 /* High-order octet of type. */
630 if (encode
!= ECOMMUNITY_ENCODE_AS
&& encode
!= ECOMMUNITY_ENCODE_IP
631 && encode
!= ECOMMUNITY_ENCODE_AS4
)
633 len
= sprintf (str_buf
+ str_pnt
, "?");
639 /* Low-order octet of type. */
641 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
643 len
= sprintf (str_buf
+ str_pnt
, "?");
651 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
652 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
654 case ECOMMUNITY_FORMAT_DISPLAY
:
655 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
657 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
665 /* Make it sure size is enough. */
666 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
669 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
672 /* Put string into buffer. */
673 if (encode
== ECOMMUNITY_ENCODE_AS4
)
675 eas
.as
= (*pnt
++ << 24);
676 eas
.as
|= (*pnt
++ << 16);
677 eas
.as
|= (*pnt
++ << 8);
680 eas
.val
= (*pnt
++ << 8);
683 len
= sprintf( str_buf
+ str_pnt
, "%s%u:%d", prefix
,
688 if (encode
== ECOMMUNITY_ENCODE_AS
)
690 eas
.as
= (*pnt
++ << 8);
693 eas
.val
= (*pnt
++ << 24);
694 eas
.val
|= (*pnt
++ << 16);
695 eas
.val
|= (*pnt
++ << 8);
698 len
= sprintf (str_buf
+ str_pnt
, "%s%u:%d", prefix
,
703 else if (encode
== ECOMMUNITY_ENCODE_IP
)
705 memcpy (&eip
.ip
, pnt
, 4);
707 eip
.val
= (*pnt
++ << 8);
710 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%d", prefix
,
711 inet_ntoa (eip
.ip
), eip
.val
);
720 ecommunity_match (const struct ecommunity
*ecom1
,
721 const struct ecommunity
*ecom2
)
726 if (ecom1
== NULL
&& ecom2
== NULL
)
729 if (ecom1
== NULL
|| ecom2
== NULL
)
732 if (ecom1
->size
< ecom2
->size
)
735 /* Every community on com2 needs to be on com1 for this to match */
736 while (i
< ecom1
->size
&& j
< ecom2
->size
)
738 if (memcmp (ecom1
->val
+ i
, ecom2
->val
+ j
, ECOMMUNITY_SIZE
) == 0)
743 if (j
== ecom2
->size
)