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
30 #include "bgpd/bgpd.h"
31 #include "bgpd/bgp_ecommunity.h"
32 #include "bgpd/bgp_lcommunity.h"
33 #include "bgpd/bgp_aspath.h"
35 /* Hash of community attribute. */
36 static struct hash
*ecomhash
;
38 /* Allocate a new ecommunities. */
42 return (struct ecommunity
*) XCALLOC (MTYPE_ECOMMUNITY
,
43 sizeof (struct ecommunity
));
46 /* Allocate ecommunities. */
48 ecommunity_free (struct ecommunity
**ecom
)
51 XFREE (MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
53 XFREE (MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
54 XFREE (MTYPE_ECOMMUNITY
, *ecom
);
59 ecommunity_hash_free (struct ecommunity
*ecom
)
61 ecommunity_free(&ecom
);
65 /* Add a new Extended Communities value to Extended Communities
66 Attribute structure. When the value is already exists in the
67 structure, we don't add the value. Newly added value is sorted by
68 numerical order. When the value is added to the structure return 1
71 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
77 /* When this is fist value, just add it. */
78 if (ecom
->val
== NULL
)
81 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
82 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
86 /* If the value already exists in the structure return 0. */
88 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
90 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
97 /* Add the value to the structure with numerical sorting. */
99 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
101 memmove (ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
102 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
103 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
104 memcpy (ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
109 /* This function takes pointer to Extended Communites strucutre then
110 create a new Extended Communities structure by uniq and sort each
111 Extended Communities value. */
113 ecommunity_uniq_sort (struct ecommunity
*ecom
)
116 struct ecommunity
*new;
117 struct ecommunity_val
*eval
;
122 new = ecommunity_new ();
124 for (i
= 0; i
< ecom
->size
; i
++)
126 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
127 ecommunity_add_val (new, eval
);
132 /* Parse Extended Communites Attribute in BGP packet. */
134 ecommunity_parse (u_int8_t
*pnt
, u_short length
)
136 struct ecommunity tmp
;
137 struct ecommunity
*new;
140 if (length
% ECOMMUNITY_SIZE
)
143 /* Prepare tmporary structure for making a new Extended Communities
145 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
148 /* Create a new Extended Communities Attribute by uniq and sort each
149 Extended Communities value */
150 new = ecommunity_uniq_sort (&tmp
);
152 return ecommunity_intern (new);
155 /* Duplicate the Extended Communities Attribute structure. */
157 ecommunity_dup (struct ecommunity
*ecom
)
159 struct ecommunity
*new;
161 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
162 new->size
= ecom
->size
;
165 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
166 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
173 /* Retrun string representation of communities attribute. */
175 ecommunity_str (struct ecommunity
*ecom
)
178 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
182 /* Merge two Extended Communities Attribute structure. */
184 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
187 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
188 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
190 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
191 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
193 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
194 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
195 ecom1
->size
+= ecom2
->size
;
200 /* Intern Extended Communities Attribute. */
202 ecommunity_intern (struct ecommunity
*ecom
)
204 struct ecommunity
*find
;
206 assert (ecom
->refcnt
== 0);
208 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
211 ecommunity_free (&ecom
);
216 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
221 /* Unintern Extended Communities Attribute. */
223 ecommunity_unintern (struct ecommunity
**ecom
)
225 struct ecommunity
*ret
;
230 /* Pull off from hash. */
231 if ((*ecom
)->refcnt
== 0)
233 /* Extended community must be in the hash. */
234 ret
= (struct ecommunity
*) hash_release (ecomhash
, *ecom
);
235 assert (ret
!= NULL
);
237 ecommunity_free (ecom
);
241 /* Utinity function to make hash key. */
243 ecommunity_hash_make (void *arg
)
245 const struct ecommunity
*ecom
= arg
;
246 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
247 u_int8_t
*pnt
= ecom
->val
;
248 unsigned int key
= 0;
251 for (c
= 0; c
< size
; c
+= ECOMMUNITY_SIZE
)
266 /* Compare two Extended Communities Attribute structure. */
268 ecommunity_cmp (const void *arg1
, const void *arg2
)
270 const struct ecommunity
*ecom1
= arg1
;
271 const struct ecommunity
*ecom2
= arg2
;
273 if (ecom1
== NULL
&& ecom2
== NULL
)
276 if (ecom1
== NULL
|| ecom2
== NULL
)
279 return (ecom1
->size
== ecom2
->size
280 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0);
283 /* Initialize Extended Comminities related hash. */
285 ecommunity_init (void)
287 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
291 ecommunity_finish (void)
293 hash_clean (ecomhash
, (void (*)(void *))ecommunity_hash_free
);
294 hash_free (ecomhash
);
298 /* Extended Communities token enum. */
299 enum ecommunity_token
301 ecommunity_token_unknown
= 0,
303 ecommunity_token_soo
,
304 ecommunity_token_val
,
307 /* Get next Extended Communities token from the string. */
309 ecommunity_gettoken (const char *str
, struct ecommunity_val
*eval
,
310 enum ecommunity_token
*token
)
321 char buf
[INET_ADDRSTRLEN
+ 1];
323 /* Skip white space. */
324 while (isspace ((int) *p
))
330 /* Check the end of the line. */
334 /* "rt" and "soo" keyword parse. */
335 if (! isdigit ((int) *p
))
337 /* "rt" match check. */
338 if (tolower ((int) *p
) == 'r')
341 if (tolower ((int) *p
) == 't')
344 *token
= ecommunity_token_rt
;
347 if (isspace ((int) *p
) || *p
== '\0')
349 *token
= ecommunity_token_rt
;
354 /* "soo" match check. */
355 else if (tolower ((int) *p
) == 's')
358 if (tolower ((int) *p
) == 'o')
361 if (tolower ((int) *p
) == 'o')
364 *token
= ecommunity_token_soo
;
367 if (isspace ((int) *p
) || *p
== '\0')
369 *token
= ecommunity_token_soo
;
374 if (isspace ((int) *p
) || *p
== '\0')
376 *token
= ecommunity_token_soo
;
384 /* What a mess, there are several possibilities:
390 * A.B.C.D: Four Byte IP
392 * GHJK: Four-byte ASN
394 * OPQR: Four byte value
397 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
407 if ((p
- str
) > INET_ADDRSTRLEN
)
409 memset (buf
, 0, INET_ADDRSTRLEN
+ 1);
410 memcpy (buf
, str
, p
- str
);
414 /* Parsing A.B.C.D in:
417 ret
= inet_aton (buf
, &ip
);
424 as
= strtoul (buf
, &endptr
, 10);
425 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
441 /* We're past the IP/ASN part */
451 /* Low digit part must be there. */
452 if (!digit
|| !separator
)
455 /* Encode result into routing distinguisher. */
458 if (val
> UINT16_MAX
)
461 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
463 memcpy (&eval
->val
[2], &ip
, sizeof (struct in_addr
));
464 eval
->val
[6] = (val
>> 8) & 0xff;
465 eval
->val
[7] = val
& 0xff;
467 else if (as
> BGP_AS_MAX
)
469 if (val
> UINT16_MAX
)
472 eval
->val
[0] = ECOMMUNITY_ENCODE_AS4
;
474 eval
->val
[2] = (as
>>24) & 0xff;
475 eval
->val
[3] = (as
>>16) & 0xff;
476 eval
->val
[4] = (as
>>8) & 0xff;
477 eval
->val
[5] = as
& 0xff;
478 eval
->val
[6] = (val
>> 8) & 0xff;
479 eval
->val
[7] = val
& 0xff;
483 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
486 eval
->val
[2] = (as
>>8) & 0xff;
487 eval
->val
[3] = as
& 0xff;
488 eval
->val
[4] = (val
>>24) & 0xff;
489 eval
->val
[5] = (val
>>16) & 0xff;
490 eval
->val
[6] = (val
>>8) & 0xff;
491 eval
->val
[7] = val
& 0xff;
493 *token
= ecommunity_token_val
;
497 *token
= ecommunity_token_unknown
;
501 /* Convert string to extended community attribute.
503 When type is already known, please specify both str and type. str
504 should not include keyword such as "rt" and "soo". Type is
505 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
506 keyword_included should be zero.
508 For example route-map's "set extcommunity" command case:
510 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
511 type = ECOMMUNITY_ROUTE_TARGET
514 "soo 100:1" -> str = "100:1"
515 type = ECOMMUNITY_SITE_ORIGIN
518 When string includes keyword for each extended community value.
519 Please specify keyword_included as non-zero value.
521 For example standard extcommunity-list case:
523 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
528 ecommunity_str2com (const char *str
, int type
, int keyword_included
)
530 struct ecommunity
*ecom
= NULL
;
531 enum ecommunity_token token
= ecommunity_token_unknown
;
532 struct ecommunity_val eval
;
535 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
539 case ecommunity_token_rt
:
540 case ecommunity_token_soo
:
541 if (! keyword_included
|| keyword
)
544 ecommunity_free (&ecom
);
549 if (token
== ecommunity_token_rt
)
551 type
= ECOMMUNITY_ROUTE_TARGET
;
553 if (token
== ecommunity_token_soo
)
555 type
= ECOMMUNITY_SITE_ORIGIN
;
558 case ecommunity_token_val
:
559 if (keyword_included
)
564 ecommunity_free (&ecom
);
570 ecom
= ecommunity_new ();
572 ecommunity_add_val (ecom
, &eval
);
574 case ecommunity_token_unknown
:
577 ecommunity_free (&ecom
);
584 /* Convert extended community attribute to string.
586 Due to historical reason of industry standard implementation, there
587 are three types of format.
589 route-map set extcommunity format
594 "rt 100:1 rt 100:2 soo 100:3"
596 "show [ip] bgp" and extcommunity-list regular expression matching
597 "RT:100:1 RT:100:2 SoO:100:3"
599 For each formath please use below definition for format:
601 ECOMMUNITY_FORMAT_ROUTE_MAP
602 ECOMMUNITY_FORMAT_COMMUNITY_LIST
603 ECOMMUNITY_FORMAT_DISPLAY
605 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
609 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
, int filter
)
615 #define ECOMMUNITY_STR_DEFAULT_LEN 27
623 /* For parse Extended Community attribute tupple. */
638 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
643 /* Prepare buffer. */
644 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
645 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
649 for (i
= 0; i
< ecom
->size
; i
++)
651 /* Make it sure size is enough. */
652 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
655 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
658 /* Space between each value. */
660 str_buf
[str_pnt
++] = ' ';
662 pnt
= ecom
->val
+ (i
* 8);
664 /* High-order octet of type. */
669 case ECOMMUNITY_ENCODE_AS
:
670 case ECOMMUNITY_ENCODE_IP
:
671 case ECOMMUNITY_ENCODE_AS4
:
674 case ECOMMUNITY_ENCODE_OPAQUE
:
675 if(filter
== ECOMMUNITY_ROUTE_TARGET
)
679 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
)
682 memcpy (&tunneltype
, pnt
+ 5, 2);
683 tunneltype
= ntohs(tunneltype
);
684 len
= sprintf (str_buf
+ str_pnt
, "ET:%d", tunneltype
);
689 len
= sprintf (str_buf
+ str_pnt
, "?");
693 case ECOMMUNITY_ENCODE_EVPN
:
694 if(filter
== ECOMMUNITY_ROUTE_TARGET
)
698 if (*pnt
== ECOMMUNITY_SITE_ORIGIN
)
702 memcpy(&macaddr
, pnt
, 6);
703 len
= sprintf(str_buf
+ str_pnt
, "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
704 macaddr
[0], macaddr
[1], macaddr
[2],
705 macaddr
[3], macaddr
[4], macaddr
[5]);
710 len
= sprintf (str_buf
+ str_pnt
, "?");
715 len
= sprintf (str_buf
+ str_pnt
, "?");
721 /* Low-order octet of type. */
723 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
725 len
= sprintf (str_buf
+ str_pnt
, "?");
733 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
734 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
736 case ECOMMUNITY_FORMAT_DISPLAY
:
737 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
739 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
747 /* Put string into buffer. */
748 if (encode
== ECOMMUNITY_ENCODE_AS4
)
750 eas
.as
= (*pnt
++ << 24);
751 eas
.as
|= (*pnt
++ << 16);
752 eas
.as
|= (*pnt
++ << 8);
755 eas
.val
= (*pnt
++ << 8);
758 len
= sprintf( str_buf
+ str_pnt
, "%s%u:%u", prefix
,
763 if (encode
== ECOMMUNITY_ENCODE_AS
)
765 eas
.as
= (*pnt
++ << 8);
768 eas
.val
= (*pnt
++ << 24);
769 eas
.val
|= (*pnt
++ << 16);
770 eas
.val
|= (*pnt
++ << 8);
773 len
= sprintf (str_buf
+ str_pnt
, "%s%u:%u", prefix
,
778 else if (encode
== ECOMMUNITY_ENCODE_IP
)
780 memcpy (&eip
.ip
, pnt
, 4);
782 eip
.val
= (*pnt
++ << 8);
785 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%u", prefix
,
786 inet_ntoa (eip
.ip
), eip
.val
);
795 ecommunity_match (const struct ecommunity
*ecom1
,
796 const struct ecommunity
*ecom2
)
801 if (ecom1
== NULL
&& ecom2
== NULL
)
804 if (ecom1
== NULL
|| ecom2
== NULL
)
807 if (ecom1
->size
< ecom2
->size
)
810 /* Every community on com2 needs to be on com1 for this to match */
811 while (i
< ecom1
->size
&& j
< ecom2
->size
)
813 if (memcmp (ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
814 ecom2
->val
+ j
* ECOMMUNITY_SIZE
,
815 ECOMMUNITY_SIZE
) == 0)
820 if (j
== ecom2
->size
)
826 /* return first occurence of type */
827 extern struct ecommunity_val
*ecommunity_lookup (const struct ecommunity
*ecom
, uint8_t type
, uint8_t subtype
)
832 /* If the value already exists in the structure return 0. */
834 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
840 if(p
[0] == type
&& p
[1] == subtype
)
841 return (struct ecommunity_val
*)p
;
846 /* remove ext. community matching type and subtype
847 * return 1 on success ( removed ), 0 otherwise (not present)
849 extern int ecommunity_strip (struct ecommunity
*ecom
, uint8_t type
, uint8_t subtype
)
853 /* When this is fist value, just add it. */
854 if (ecom
== NULL
|| ecom
->val
== NULL
)
859 /* If the value already exists in the structure return 0. */
861 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
863 if (p
[0] == type
&& p
[1] == subtype
)
871 /* Strip The selected value */
873 /* size is reduced. no memmove to do */
874 p
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
876 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
877 if( (ecom
->size
- c
) != 0)
878 memcpy(p
+ (c
) * ECOMMUNITY_SIZE
,
879 ecom
->val
+ (c
+1)* ECOMMUNITY_SIZE
,
880 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
881 /* shift last ecommunities */
882 XFREE (MTYPE_ECOMMUNITY
, ecom
->val
);