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 along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32 #include "bgpd/bgpd.h"
33 #include "bgpd/bgp_ecommunity.h"
34 #include "bgpd/bgp_lcommunity.h"
35 #include "bgpd/bgp_aspath.h"
37 /* Hash of community attribute. */
38 static struct hash
*ecomhash
;
40 /* Allocate a new ecommunities. */
41 struct ecommunity
*ecommunity_new(void)
43 return (struct ecommunity
*)XCALLOC(MTYPE_ECOMMUNITY
,
44 sizeof(struct ecommunity
));
47 /* Allocate ecommunities. */
48 void ecommunity_free(struct ecommunity
**ecom
)
51 XFREE(MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
53 XFREE(MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
54 XFREE(MTYPE_ECOMMUNITY
, *ecom
);
58 static void ecommunity_hash_free(struct ecommunity
*ecom
)
60 ecommunity_free(&ecom
);
64 /* Add a new Extended Communities value to Extended Communities
65 Attribute structure. When the value is already exists in the
66 structure, we don't add the value. Newly added value is sorted by
67 numerical order. When the value is added to the structure return 1
69 int ecommunity_add_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
75 /* When this is fist value, just add it. */
76 if (ecom
->val
== NULL
) {
78 ecom
->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom_length(ecom
));
79 memcpy(ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
83 /* If the value already exists in the structure return 0. */
85 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
86 ret
= memcmp(p
, eval
->val
, ECOMMUNITY_SIZE
);
93 /* Add the value to the structure with numerical sorting. */
96 XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length(ecom
));
98 memmove(ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
99 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
100 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
101 memcpy(ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
106 /* This function takes pointer to Extended Communites strucutre then
107 create a new Extended Communities structure by uniq and sort each
108 Extended Communities value. */
109 struct ecommunity
*ecommunity_uniq_sort(struct ecommunity
*ecom
)
112 struct ecommunity
*new;
113 struct ecommunity_val
*eval
;
118 new = ecommunity_new();
120 for (i
= 0; i
< ecom
->size
; i
++) {
121 eval
= (struct ecommunity_val
*)(ecom
->val
122 + (i
* ECOMMUNITY_SIZE
));
123 ecommunity_add_val(new, eval
);
128 /* Parse Extended Communites Attribute in BGP packet. */
129 struct ecommunity
*ecommunity_parse(uint8_t *pnt
, unsigned short length
)
131 struct ecommunity tmp
;
132 struct ecommunity
*new;
135 if (length
% ECOMMUNITY_SIZE
)
138 /* Prepare tmporary structure for making a new Extended Communities
140 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
143 /* Create a new Extended Communities Attribute by uniq and sort each
144 Extended Communities value */
145 new = ecommunity_uniq_sort(&tmp
);
147 return ecommunity_intern(new);
150 /* Duplicate the Extended Communities Attribute structure. */
151 struct ecommunity
*ecommunity_dup(struct ecommunity
*ecom
)
153 struct ecommunity
*new;
155 new = XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
156 new->size
= ecom
->size
;
158 new->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
159 ecom
->size
* ECOMMUNITY_SIZE
);
160 memcpy(new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
166 /* Retrun string representation of communities attribute. */
167 char *ecommunity_str(struct ecommunity
*ecom
)
171 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
175 /* Merge two Extended Communities Attribute structure. */
176 struct ecommunity
*ecommunity_merge(struct ecommunity
*ecom1
,
177 struct ecommunity
*ecom2
)
181 XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
182 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
185 XMALLOC(MTYPE_ECOMMUNITY_VAL
,
186 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
188 memcpy(ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
), ecom2
->val
,
189 ecom2
->size
* ECOMMUNITY_SIZE
);
190 ecom1
->size
+= ecom2
->size
;
195 /* Intern Extended Communities Attribute. */
196 struct ecommunity
*ecommunity_intern(struct ecommunity
*ecom
)
198 struct ecommunity
*find
;
200 assert(ecom
->refcnt
== 0);
202 find
= (struct ecommunity
*)hash_get(ecomhash
, ecom
, hash_alloc_intern
);
205 ecommunity_free(&ecom
);
211 ecommunity_ecom2str(find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
216 /* Unintern Extended Communities Attribute. */
217 void ecommunity_unintern(struct ecommunity
**ecom
)
219 struct ecommunity
*ret
;
224 /* Pull off from hash. */
225 if ((*ecom
)->refcnt
== 0) {
226 /* Extended community must be in the hash. */
227 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
230 ecommunity_free(ecom
);
234 /* Utinity function to make hash key. */
235 unsigned int ecommunity_hash_make(void *arg
)
237 const struct ecommunity
*ecom
= arg
;
238 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
240 return jhash(ecom
->val
, size
, 0x564321ab);
243 /* Compare two Extended Communities Attribute structure. */
244 int ecommunity_cmp(const void *arg1
, const void *arg2
)
246 const struct ecommunity
*ecom1
= arg1
;
247 const struct ecommunity
*ecom2
= arg2
;
249 if (ecom1
== NULL
&& ecom2
== NULL
)
252 if (ecom1
== NULL
|| ecom2
== NULL
)
255 return (ecom1
->size
== ecom2
->size
256 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
)
260 /* Initialize Extended Comminities related hash. */
261 void ecommunity_init(void)
263 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
264 "BGP ecommunity hash");
267 void ecommunity_finish(void)
269 hash_clean(ecomhash
, (void (*)(void *))ecommunity_hash_free
);
274 /* Extended Communities token enum. */
275 enum ecommunity_token
{
276 ecommunity_token_unknown
= 0,
278 ecommunity_token_soo
,
279 ecommunity_token_val
,
283 * Encode BGP extended community from passed values. Supports types
284 * defined in RFC 4360 and well-known sub-types.
286 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
287 struct in_addr ip
, uint32_t val
,
288 struct ecommunity_val
*eval
)
291 if (type
== ECOMMUNITY_ENCODE_AS
) {
294 } else if (type
== ECOMMUNITY_ENCODE_IP
295 || type
== ECOMMUNITY_ENCODE_AS4
) {
296 if (val
> UINT16_MAX
)
300 /* Fill in the values. */
303 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
304 eval
->val
[1] = sub_type
;
305 if (type
== ECOMMUNITY_ENCODE_AS
) {
306 eval
->val
[2] = (as
>> 8) & 0xff;
307 eval
->val
[3] = as
& 0xff;
308 eval
->val
[4] = (val
>> 24) & 0xff;
309 eval
->val
[5] = (val
>> 16) & 0xff;
310 eval
->val
[6] = (val
>> 8) & 0xff;
311 eval
->val
[7] = val
& 0xff;
312 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
313 memcpy(&eval
->val
[2], &ip
, sizeof(struct in_addr
));
314 eval
->val
[6] = (val
>> 8) & 0xff;
315 eval
->val
[7] = val
& 0xff;
317 eval
->val
[2] = (as
>> 24) & 0xff;
318 eval
->val
[3] = (as
>> 16) & 0xff;
319 eval
->val
[4] = (as
>> 8) & 0xff;
320 eval
->val
[5] = as
& 0xff;
321 eval
->val
[6] = (val
>> 8) & 0xff;
322 eval
->val
[7] = val
& 0xff;
328 /* Get next Extended Communities token from the string. */
329 static const char *ecommunity_gettoken(const char *str
,
330 struct ecommunity_val
*eval
,
331 enum ecommunity_token
*token
)
343 char buf
[INET_ADDRSTRLEN
+ 1];
345 /* Skip white space. */
346 while (isspace((int)*p
)) {
351 /* Check the end of the line. */
355 /* "rt" and "soo" keyword parse. */
356 if (!isdigit((int)*p
)) {
357 /* "rt" match check. */
358 if (tolower((int)*p
) == 'r') {
360 if (tolower((int)*p
) == 't') {
362 *token
= ecommunity_token_rt
;
365 if (isspace((int)*p
) || *p
== '\0') {
366 *token
= ecommunity_token_rt
;
371 /* "soo" match check. */
372 else if (tolower((int)*p
) == 's') {
374 if (tolower((int)*p
) == 'o') {
376 if (tolower((int)*p
) == 'o') {
378 *token
= ecommunity_token_soo
;
381 if (isspace((int)*p
) || *p
== '\0') {
382 *token
= ecommunity_token_soo
;
387 if (isspace((int)*p
) || *p
== '\0') {
388 *token
= ecommunity_token_soo
;
396 /* What a mess, there are several possibilities:
402 * A.B.C.D: Four Byte IP
404 * GHJK: Four-byte ASN
406 * OPQR: Four byte value
409 while (isdigit((int)*p
) || *p
== ':' || *p
== '.') {
417 if ((p
- str
) > INET_ADDRSTRLEN
)
419 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
420 memcpy(buf
, str
, p
- str
);
423 /* Parsing A.B.C.D in:
426 ret
= inet_aton(buf
, &ip
);
431 as
= strtoul(buf
, &endptr
, 10);
432 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
435 } else if (*p
== '.') {
444 /* We're past the IP/ASN part */
453 /* Low digit part must be there. */
454 if (!digit
|| !separator
)
457 /* Encode result into extended community. */
459 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
460 else if (as
> BGP_AS_MAX
)
461 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
463 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
464 if (ecommunity_encode(ecomm_type
, 0, 1, as
, ip
, val
, eval
))
466 *token
= ecommunity_token_val
;
470 *token
= ecommunity_token_unknown
;
474 /* Convert string to extended community attribute.
476 When type is already known, please specify both str and type. str
477 should not include keyword such as "rt" and "soo". Type is
478 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
479 keyword_included should be zero.
481 For example route-map's "set extcommunity" command case:
483 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
484 type = ECOMMUNITY_ROUTE_TARGET
487 "soo 100:1" -> str = "100:1"
488 type = ECOMMUNITY_SITE_ORIGIN
491 When string includes keyword for each extended community value.
492 Please specify keyword_included as non-zero value.
494 For example standard extcommunity-list case:
496 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
500 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
501 int keyword_included
)
503 struct ecommunity
*ecom
= NULL
;
504 enum ecommunity_token token
= ecommunity_token_unknown
;
505 struct ecommunity_val eval
;
508 while ((str
= ecommunity_gettoken(str
, &eval
, &token
))) {
510 case ecommunity_token_rt
:
511 case ecommunity_token_soo
:
512 if (!keyword_included
|| keyword
) {
514 ecommunity_free(&ecom
);
519 if (token
== ecommunity_token_rt
) {
520 type
= ECOMMUNITY_ROUTE_TARGET
;
522 if (token
== ecommunity_token_soo
) {
523 type
= ECOMMUNITY_SITE_ORIGIN
;
526 case ecommunity_token_val
:
527 if (keyword_included
) {
530 ecommunity_free(&ecom
);
536 ecom
= ecommunity_new();
538 ecommunity_add_val(ecom
, &eval
);
540 case ecommunity_token_unknown
:
543 ecommunity_free(&ecom
);
550 static int ecommunity_rt_soo_str(char *buf
, uint8_t *pnt
, int type
,
551 int sub_type
, int format
)
556 /* For parse Extended Community attribute tupple. */
557 struct ecommunity_as eas
;
558 struct ecommunity_ip eip
;
561 /* Determine prefix for string, if any. */
563 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
564 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
566 case ECOMMUNITY_FORMAT_DISPLAY
:
567 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
569 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
577 /* Put string into buffer. */
578 if (type
== ECOMMUNITY_ENCODE_AS4
) {
579 pnt
= ptr_get_be32(pnt
, &eas
.as
);
580 eas
.val
= (*pnt
++ << 8);
583 len
= sprintf(buf
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
584 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
585 eas
.as
= (*pnt
++ << 8);
587 pnt
= ptr_get_be32(pnt
, &eas
.val
);
589 len
= sprintf(buf
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
590 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
591 memcpy(&eip
.ip
, pnt
, 4);
593 eip
.val
= (*pnt
++ << 8);
596 len
= sprintf(buf
, "%s%s:%u", prefix
, inet_ntoa(eip
.ip
),
599 (void)pnt
; /* consume value */
604 /* Convert extended community attribute to string.
606 Due to historical reason of industry standard implementation, there
607 are three types of format.
609 route-map set extcommunity format
614 "rt 100:1 rt 100:2 soo 100:3"
616 "show [ip] bgp" and extcommunity-list regular expression matching
617 "RT:100:1 RT:100:2 SoO:100:3"
619 For each formath please use below definition for format:
621 ECOMMUNITY_FORMAT_ROUTE_MAP
622 ECOMMUNITY_FORMAT_COMMUNITY_LIST
623 ECOMMUNITY_FORMAT_DISPLAY
625 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
628 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
634 #define ECOMMUNITY_STR_DEFAULT_LEN 27
641 if (ecom
->size
== 0) {
642 str_buf
= XMALLOC(MTYPE_ECOMMUNITY_STR
, 1);
647 /* Prepare buffer. */
648 str_buf
= XMALLOC(MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
649 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
653 for (i
= 0; i
< ecom
->size
; i
++) {
656 /* Make it sure size is enough. */
657 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
) {
659 str_buf
= XREALLOC(MTYPE_ECOMMUNITY_STR
, str_buf
,
663 /* Space between each value. */
665 str_buf
[str_pnt
++] = ' ';
667 pnt
= ecom
->val
+ (i
* 8);
669 /* High-order octet of type. */
672 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
673 || type
== ECOMMUNITY_ENCODE_AS4
) {
674 /* Low-order octet of type. */
676 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
677 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
)
680 len
= ecommunity_rt_soo_str(str_buf
+ str_pnt
,
683 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
684 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
686 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
688 memcpy(&tunneltype
, pnt
+ 5, 2);
689 tunneltype
= ntohs(tunneltype
);
690 len
= sprintf(str_buf
+ str_pnt
, "ET:%d",
692 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
693 len
= sprintf(str_buf
+ str_pnt
,
697 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
698 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
700 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
703 memcpy(&rmac
, pnt
, ETH_ALEN
);
706 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
707 (uint8_t)rmac
.octet
[0],
708 (uint8_t)rmac
.octet
[1],
709 (uint8_t)rmac
.octet
[2],
710 (uint8_t)rmac
.octet
[3],
711 (uint8_t)rmac
.octet
[4],
712 (uint8_t)rmac
.octet
[5]);
714 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
716 uint8_t flags
= *++pnt
;
718 memcpy(&seqnum
, pnt
+ 2, 4);
719 seqnum
= ntohl(seqnum
);
721 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
)
722 len
= sprintf(str_buf
+ str_pnt
,
726 len
= sprintf(str_buf
+ str_pnt
,
734 len
= sprintf(str_buf
+ str_pnt
, "?");
743 int ecommunity_match(const struct ecommunity
*ecom1
,
744 const struct ecommunity
*ecom2
)
749 if (ecom1
== NULL
&& ecom2
== NULL
)
752 if (ecom1
== NULL
|| ecom2
== NULL
)
755 if (ecom1
->size
< ecom2
->size
)
758 /* Every community on com2 needs to be on com1 for this to match */
759 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
760 if (memcmp(ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
761 ecom2
->val
+ j
* ECOMMUNITY_SIZE
, ECOMMUNITY_SIZE
)
767 if (j
== ecom2
->size
)
773 /* return first occurence of type */
774 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
775 uint8_t type
, uint8_t subtype
)
780 /* If the value already exists in the structure return 0. */
782 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
786 if (p
[0] == type
&& p
[1] == subtype
)
787 return (struct ecommunity_val
*)p
;
792 /* remove ext. community matching type and subtype
793 * return 1 on success ( removed ), 0 otherwise (not present)
795 extern int ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
800 /* When this is fist value, just add it. */
801 if (ecom
== NULL
|| ecom
->val
== NULL
) {
805 /* If the value already exists in the structure return 0. */
807 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
808 if (p
[0] == type
&& p
[1] == subtype
) {
815 /* Strip The selected value */
817 /* size is reduced. no memmove to do */
818 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
820 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
821 if ((ecom
->size
- c
) != 0)
822 memcpy(p
+ (c
)*ECOMMUNITY_SIZE
,
823 ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
824 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
825 /* shift last ecommunities */
826 XFREE(MTYPE_ECOMMUNITY
, ecom
->val
);