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
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_ecommunity.h"
32 #include "bgpd/bgp_aspath.h"
34 /* Hash of community attribute. */
35 static struct hash
*ecomhash
;
37 /* Allocate a new ecommunities. */
38 static struct ecommunity
*
41 return (struct ecommunity
*) XCALLOC (MTYPE_ECOMMUNITY
,
42 sizeof (struct ecommunity
));
45 /* Allocate ecommunities. */
47 ecommunity_free (struct ecommunity
**ecom
)
50 XFREE (MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
52 XFREE (MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
53 XFREE (MTYPE_ECOMMUNITY
, *ecom
);
57 /* Add a new Extended Communities value to Extended Communities
58 Attribute structure. When the value is already exists in the
59 structure, we don't add the value. Newly added value is sorted by
60 numerical order. When the value is added to the structure return 1
63 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
69 /* When this is fist value, just add it. */
70 if (ecom
->val
== NULL
)
73 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
74 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
78 /* If the value already exists in the structure return 0. */
80 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
82 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
89 /* Add the value to the structure with numerical sorting. */
91 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
93 memmove (ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
94 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
95 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
96 memcpy (ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
101 /* This function takes pointer to Extended Communites strucutre then
102 create a new Extended Communities structure by uniq and sort each
103 Extended Communities value. */
105 ecommunity_uniq_sort (struct ecommunity
*ecom
)
108 struct ecommunity
*new;
109 struct ecommunity_val
*eval
;
114 new = ecommunity_new ();
116 for (i
= 0; i
< ecom
->size
; i
++)
118 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
119 ecommunity_add_val (new, eval
);
124 /* Parse Extended Communites Attribute in BGP packet. */
126 ecommunity_parse (u_int8_t
*pnt
, u_short length
)
128 struct ecommunity tmp
;
129 struct ecommunity
*new;
132 if (length
% ECOMMUNITY_SIZE
)
135 /* Prepare tmporary structure for making a new Extended Communities
137 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
140 /* Create a new Extended Communities Attribute by uniq and sort each
141 Extended Communities value */
142 new = ecommunity_uniq_sort (&tmp
);
144 return ecommunity_intern (new);
147 /* Duplicate the Extended Communities Attribute structure. */
149 ecommunity_dup (struct ecommunity
*ecom
)
151 struct ecommunity
*new;
153 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
154 new->size
= ecom
->size
;
157 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
158 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
165 /* Retrun string representation of communities attribute. */
167 ecommunity_str (struct ecommunity
*ecom
)
170 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
174 /* Merge two Extended Communities Attribute structure. */
176 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
179 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
180 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
182 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
183 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
185 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
186 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
187 ecom1
->size
+= ecom2
->size
;
192 /* Intern Extended Communities Attribute. */
194 ecommunity_intern (struct ecommunity
*ecom
)
196 struct ecommunity
*find
;
198 assert (ecom
->refcnt
== 0);
200 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
203 ecommunity_free (&ecom
);
208 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
);
213 /* Unintern Extended Communities Attribute. */
215 ecommunity_unintern (struct ecommunity
**ecom
)
217 struct ecommunity
*ret
;
222 /* Pull off from hash. */
223 if ((*ecom
)->refcnt
== 0)
225 /* Extended community must be in the hash. */
226 ret
= (struct ecommunity
*) hash_release (ecomhash
, *ecom
);
227 assert (ret
!= NULL
);
229 ecommunity_free (ecom
);
233 /* Utinity function to make hash key. */
235 ecommunity_hash_make (void *arg
)
237 const struct ecommunity
*ecom
= arg
;
238 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
239 u_int8_t
*pnt
= ecom
->val
;
240 unsigned int key
= 0;
243 for (c
= 0; c
< size
; c
+= ECOMMUNITY_SIZE
)
258 /* Compare two Extended Communities Attribute structure. */
260 ecommunity_cmp (const void *arg1
, const void *arg2
)
262 const struct ecommunity
*ecom1
= arg1
;
263 const struct ecommunity
*ecom2
= arg2
;
265 if (ecom1
== NULL
&& ecom2
== NULL
)
268 if (ecom1
== NULL
|| ecom2
== NULL
)
271 return (ecom1
->size
== ecom2
->size
272 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0);
275 /* Initialize Extended Comminities related hash. */
277 ecommunity_init (void)
279 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
283 ecommunity_finish (void)
285 hash_free (ecomhash
);
289 /* Extended Communities token enum. */
290 enum ecommunity_token
292 ecommunity_token_unknown
= 0,
294 ecommunity_token_soo
,
295 ecommunity_token_val
,
298 /* Get next Extended Communities token from the string. */
300 ecommunity_gettoken (const char *str
, struct ecommunity_val
*eval
,
301 enum ecommunity_token
*token
)
312 char buf
[INET_ADDRSTRLEN
+ 1];
314 /* Skip white space. */
315 while (isspace ((int) *p
))
321 /* Check the end of the line. */
325 /* "rt" and "soo" keyword parse. */
326 if (! isdigit ((int) *p
))
328 /* "rt" match check. */
329 if (tolower ((int) *p
) == 'r')
332 if (tolower ((int) *p
) == 't')
335 *token
= ecommunity_token_rt
;
338 if (isspace ((int) *p
) || *p
== '\0')
340 *token
= ecommunity_token_rt
;
345 /* "soo" match check. */
346 else if (tolower ((int) *p
) == 's')
349 if (tolower ((int) *p
) == 'o')
352 if (tolower ((int) *p
) == 'o')
355 *token
= ecommunity_token_soo
;
358 if (isspace ((int) *p
) || *p
== '\0')
360 *token
= ecommunity_token_soo
;
365 if (isspace ((int) *p
) || *p
== '\0')
367 *token
= ecommunity_token_soo
;
375 /* What a mess, there are several possibilities:
381 * A.B.C.D: Four Byte IP
383 * GHJK: Four-byte ASN
385 * OPQR: Four byte value
388 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
398 if ((p
- str
) > INET_ADDRSTRLEN
)
400 memset (buf
, 0, INET_ADDRSTRLEN
+ 1);
401 memcpy (buf
, str
, p
- str
);
405 /* Parsing A.B.C.D in:
408 ret
= inet_aton (buf
, &ip
);
415 as
= strtoul (buf
, &endptr
, 10);
416 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
432 /* We're past the IP/ASN part */
442 /* Low digit part must be there. */
443 if (!digit
|| !separator
)
446 /* Encode result into routing distinguisher. */
449 if (val
> UINT16_MAX
)
452 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
454 memcpy (&eval
->val
[2], &ip
, sizeof (struct in_addr
));
455 eval
->val
[6] = (val
>> 8) & 0xff;
456 eval
->val
[7] = val
& 0xff;
458 else if (as
> BGP_AS_MAX
)
460 if (val
> UINT16_MAX
)
463 eval
->val
[0] = ECOMMUNITY_ENCODE_AS4
;
465 eval
->val
[2] = (as
>>24) & 0xff;
466 eval
->val
[3] = (as
>>16) & 0xff;
467 eval
->val
[4] = (as
>>8) & 0xff;
468 eval
->val
[5] = as
& 0xff;
469 eval
->val
[6] = (val
>> 8) & 0xff;
470 eval
->val
[7] = val
& 0xff;
474 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
477 eval
->val
[2] = (as
>>8) & 0xff;
478 eval
->val
[3] = as
& 0xff;
479 eval
->val
[4] = (val
>>24) & 0xff;
480 eval
->val
[5] = (val
>>16) & 0xff;
481 eval
->val
[6] = (val
>>8) & 0xff;
482 eval
->val
[7] = val
& 0xff;
484 *token
= ecommunity_token_val
;
488 *token
= ecommunity_token_unknown
;
492 /* Convert string to extended community attribute.
494 When type is already known, please specify both str and type. str
495 should not include keyword such as "rt" and "soo". Type is
496 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
497 keyword_included should be zero.
499 For example route-map's "set extcommunity" command case:
501 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
502 type = ECOMMUNITY_ROUTE_TARGET
505 "soo 100:1" -> str = "100:1"
506 type = ECOMMUNITY_SITE_ORIGIN
509 When string includes keyword for each extended community value.
510 Please specify keyword_included as non-zero value.
512 For example standard extcommunity-list case:
514 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
519 ecommunity_str2com (const char *str
, int type
, int keyword_included
)
521 struct ecommunity
*ecom
= NULL
;
522 enum ecommunity_token token
= ecommunity_token_unknown
;
523 struct ecommunity_val eval
;
526 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
530 case ecommunity_token_rt
:
531 case ecommunity_token_soo
:
532 if (! keyword_included
|| keyword
)
535 ecommunity_free (&ecom
);
540 if (token
== ecommunity_token_rt
)
542 type
= ECOMMUNITY_ROUTE_TARGET
;
544 if (token
== ecommunity_token_soo
)
546 type
= ECOMMUNITY_SITE_ORIGIN
;
549 case ecommunity_token_val
:
550 if (keyword_included
)
555 ecommunity_free (&ecom
);
561 ecom
= ecommunity_new ();
563 ecommunity_add_val (ecom
, &eval
);
565 case ecommunity_token_unknown
:
568 ecommunity_free (&ecom
);
575 /* Convert extended community attribute to string.
577 Due to historical reason of industry standard implementation, there
578 are three types of format.
580 route-map set extcommunity format
585 "rt 100:1 rt 100:2 soo 100:3"
587 "show ip bgp" and extcommunity-list regular expression matching
588 "RT:100:1 RT:100:2 SoO:100:3"
590 For each formath please use below definition for format:
592 ECOMMUNITY_FORMAT_ROUTE_MAP
593 ECOMMUNITY_FORMAT_COMMUNITY_LIST
594 ECOMMUNITY_FORMAT_DISPLAY
597 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
)
603 #define ECOMMUNITY_STR_DEFAULT_LEN 27
611 /* For parse Extended Community attribute tupple. */
626 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
631 /* Prepare buffer. */
632 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
633 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
636 for (i
= 0; i
< ecom
->size
; i
++)
638 /* Make it sure size is enough. */
639 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
642 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
645 /* Space between each value. */
647 str_buf
[str_pnt
++] = ' ';
649 pnt
= ecom
->val
+ (i
* 8);
651 /* High-order octet of type. */
656 case ECOMMUNITY_ENCODE_AS
:
657 case ECOMMUNITY_ENCODE_IP
:
658 case ECOMMUNITY_ENCODE_AS4
:
661 case ECOMMUNITY_ENCODE_OPAQUE
:
662 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
)
665 memcpy (&tunneltype
, pnt
+ 5, 2);
666 tunneltype
= ntohs(tunneltype
);
667 len
= sprintf (str_buf
+ str_pnt
, "ET:%d", tunneltype
);
675 len
= sprintf (str_buf
+ str_pnt
, "?");
681 /* Low-order octet of type. */
683 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
685 len
= sprintf (str_buf
+ str_pnt
, "?");
693 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
694 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
696 case ECOMMUNITY_FORMAT_DISPLAY
:
697 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
699 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
707 /* Put string into buffer. */
708 if (encode
== ECOMMUNITY_ENCODE_AS4
)
710 eas
.as
= (*pnt
++ << 24);
711 eas
.as
|= (*pnt
++ << 16);
712 eas
.as
|= (*pnt
++ << 8);
715 eas
.val
= (*pnt
++ << 8);
718 len
= sprintf( str_buf
+ str_pnt
, "%s%u:%u", prefix
,
723 if (encode
== ECOMMUNITY_ENCODE_AS
)
725 eas
.as
= (*pnt
++ << 8);
728 eas
.val
= (*pnt
++ << 24);
729 eas
.val
|= (*pnt
++ << 16);
730 eas
.val
|= (*pnt
++ << 8);
733 len
= sprintf (str_buf
+ str_pnt
, "%s%u:%u", prefix
,
738 else if (encode
== ECOMMUNITY_ENCODE_IP
)
740 memcpy (&eip
.ip
, pnt
, 4);
742 eip
.val
= (*pnt
++ << 8);
745 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%u", prefix
,
746 inet_ntoa (eip
.ip
), eip
.val
);
755 ecommunity_match (const struct ecommunity
*ecom1
,
756 const struct ecommunity
*ecom2
)
761 if (ecom1
== NULL
&& ecom2
== NULL
)
764 if (ecom1
== NULL
|| ecom2
== NULL
)
767 if (ecom1
->size
< ecom2
->size
)
770 /* Every community on com2 needs to be on com1 for this to match */
771 while (i
< ecom1
->size
&& j
< ecom2
->size
)
773 if (memcmp (ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
774 ecom2
->val
+ j
* ECOMMUNITY_SIZE
,
775 ECOMMUNITY_SIZE
) == 0)
780 if (j
== ecom2
->size
)