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. */
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
);
58 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
70 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
76 /* When this is fist value, just add it. */
77 if (ecom
->val
== NULL
)
80 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
81 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
85 /* If the value already exists in the structure return 0. */
87 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
89 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
96 /* Add the value to the structure with numerical sorting. */
98 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
100 memmove (ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
101 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
102 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
103 memcpy (ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
108 /* This function takes pointer to Extended Communites strucutre then
109 create a new Extended Communities structure by uniq and sort each
110 Extended Communities value. */
112 ecommunity_uniq_sort (struct ecommunity
*ecom
)
115 struct ecommunity
*new;
116 struct ecommunity_val
*eval
;
121 new = ecommunity_new ();
123 for (i
= 0; i
< ecom
->size
; i
++)
125 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
126 ecommunity_add_val (new, eval
);
131 /* Parse Extended Communites Attribute in BGP packet. */
133 ecommunity_parse (u_int8_t
*pnt
, u_short length
)
135 struct ecommunity tmp
;
136 struct ecommunity
*new;
139 if (length
% ECOMMUNITY_SIZE
)
142 /* Prepare tmporary structure for making a new Extended Communities
144 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
147 /* Create a new Extended Communities Attribute by uniq and sort each
148 Extended Communities value */
149 new = ecommunity_uniq_sort (&tmp
);
151 return ecommunity_intern (new);
154 /* Duplicate the Extended Communities Attribute structure. */
156 ecommunity_dup (struct ecommunity
*ecom
)
158 struct ecommunity
*new;
160 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
161 new->size
= ecom
->size
;
164 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
165 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
172 /* Retrun string representation of communities attribute. */
174 ecommunity_str (struct ecommunity
*ecom
)
177 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
181 /* Merge two Extended Communities Attribute structure. */
183 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
186 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
187 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
189 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
190 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
192 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
193 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
194 ecom1
->size
+= ecom2
->size
;
199 /* Intern Extended Communities Attribute. */
201 ecommunity_intern (struct ecommunity
*ecom
)
203 struct ecommunity
*find
;
205 assert (ecom
->refcnt
== 0);
207 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
210 ecommunity_free (&ecom
);
215 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
);
220 /* Unintern Extended Communities Attribute. */
222 ecommunity_unintern (struct ecommunity
**ecom
)
224 struct ecommunity
*ret
;
229 /* Pull off from hash. */
230 if ((*ecom
)->refcnt
== 0)
232 /* Extended community must be in the hash. */
233 ret
= (struct ecommunity
*) hash_release (ecomhash
, *ecom
);
234 assert (ret
!= NULL
);
236 ecommunity_free (ecom
);
240 /* Utinity function to make hash key. */
242 ecommunity_hash_make (void *arg
)
244 const struct ecommunity
*ecom
= arg
;
245 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
246 u_int8_t
*pnt
= ecom
->val
;
247 unsigned int key
= 0;
250 for (c
= 0; c
< size
; c
+= ECOMMUNITY_SIZE
)
265 /* Compare two Extended Communities Attribute structure. */
267 ecommunity_cmp (const void *arg1
, const void *arg2
)
269 const struct ecommunity
*ecom1
= arg1
;
270 const struct ecommunity
*ecom2
= arg2
;
272 if (ecom1
== NULL
&& ecom2
== NULL
)
275 if (ecom1
== NULL
|| ecom2
== NULL
)
278 return (ecom1
->size
== ecom2
->size
279 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0);
282 /* Initialize Extended Comminities related hash. */
284 ecommunity_init (void)
286 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
290 ecommunity_finish (void)
292 hash_clean (ecomhash
, (void (*)(void *))ecommunity_hash_free
);
293 hash_free (ecomhash
);
297 /* Extended Communities token enum. */
298 enum ecommunity_token
300 ecommunity_token_unknown
= 0,
302 ecommunity_token_soo
,
303 ecommunity_token_val
,
306 /* Get next Extended Communities token from the string. */
308 ecommunity_gettoken (const char *str
, struct ecommunity_val
*eval
,
309 enum ecommunity_token
*token
)
320 char buf
[INET_ADDRSTRLEN
+ 1];
322 /* Skip white space. */
323 while (isspace ((int) *p
))
329 /* Check the end of the line. */
333 /* "rt" and "soo" keyword parse. */
334 if (! isdigit ((int) *p
))
336 /* "rt" match check. */
337 if (tolower ((int) *p
) == 'r')
340 if (tolower ((int) *p
) == 't')
343 *token
= ecommunity_token_rt
;
346 if (isspace ((int) *p
) || *p
== '\0')
348 *token
= ecommunity_token_rt
;
353 /* "soo" match check. */
354 else if (tolower ((int) *p
) == 's')
357 if (tolower ((int) *p
) == 'o')
360 if (tolower ((int) *p
) == 'o')
363 *token
= ecommunity_token_soo
;
366 if (isspace ((int) *p
) || *p
== '\0')
368 *token
= ecommunity_token_soo
;
373 if (isspace ((int) *p
) || *p
== '\0')
375 *token
= ecommunity_token_soo
;
383 /* What a mess, there are several possibilities:
389 * A.B.C.D: Four Byte IP
391 * GHJK: Four-byte ASN
393 * OPQR: Four byte value
396 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
406 if ((p
- str
) > INET_ADDRSTRLEN
)
408 memset (buf
, 0, INET_ADDRSTRLEN
+ 1);
409 memcpy (buf
, str
, p
- str
);
413 /* Parsing A.B.C.D in:
416 ret
= inet_aton (buf
, &ip
);
423 as
= strtoul (buf
, &endptr
, 10);
424 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
440 /* We're past the IP/ASN part */
450 /* Low digit part must be there. */
451 if (!digit
|| !separator
)
454 /* Encode result into routing distinguisher. */
457 if (val
> UINT16_MAX
)
460 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
462 memcpy (&eval
->val
[2], &ip
, sizeof (struct in_addr
));
463 eval
->val
[6] = (val
>> 8) & 0xff;
464 eval
->val
[7] = val
& 0xff;
466 else if (as
> BGP_AS_MAX
)
468 if (val
> UINT16_MAX
)
471 eval
->val
[0] = ECOMMUNITY_ENCODE_AS4
;
473 eval
->val
[2] = (as
>>24) & 0xff;
474 eval
->val
[3] = (as
>>16) & 0xff;
475 eval
->val
[4] = (as
>>8) & 0xff;
476 eval
->val
[5] = as
& 0xff;
477 eval
->val
[6] = (val
>> 8) & 0xff;
478 eval
->val
[7] = val
& 0xff;
482 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
485 eval
->val
[2] = (as
>>8) & 0xff;
486 eval
->val
[3] = as
& 0xff;
487 eval
->val
[4] = (val
>>24) & 0xff;
488 eval
->val
[5] = (val
>>16) & 0xff;
489 eval
->val
[6] = (val
>>8) & 0xff;
490 eval
->val
[7] = val
& 0xff;
492 *token
= ecommunity_token_val
;
496 *token
= ecommunity_token_unknown
;
500 /* Convert string to extended community attribute.
502 When type is already known, please specify both str and type. str
503 should not include keyword such as "rt" and "soo". Type is
504 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
505 keyword_included should be zero.
507 For example route-map's "set extcommunity" command case:
509 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
510 type = ECOMMUNITY_ROUTE_TARGET
513 "soo 100:1" -> str = "100:1"
514 type = ECOMMUNITY_SITE_ORIGIN
517 When string includes keyword for each extended community value.
518 Please specify keyword_included as non-zero value.
520 For example standard extcommunity-list case:
522 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
527 ecommunity_str2com (const char *str
, int type
, int keyword_included
)
529 struct ecommunity
*ecom
= NULL
;
530 enum ecommunity_token token
= ecommunity_token_unknown
;
531 struct ecommunity_val eval
;
534 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
538 case ecommunity_token_rt
:
539 case ecommunity_token_soo
:
540 if (! keyword_included
|| keyword
)
543 ecommunity_free (&ecom
);
548 if (token
== ecommunity_token_rt
)
550 type
= ECOMMUNITY_ROUTE_TARGET
;
552 if (token
== ecommunity_token_soo
)
554 type
= ECOMMUNITY_SITE_ORIGIN
;
557 case ecommunity_token_val
:
558 if (keyword_included
)
563 ecommunity_free (&ecom
);
569 ecom
= ecommunity_new ();
571 ecommunity_add_val (ecom
, &eval
);
573 case ecommunity_token_unknown
:
576 ecommunity_free (&ecom
);
583 /* Convert extended community attribute to string.
585 Due to historical reason of industry standard implementation, there
586 are three types of format.
588 route-map set extcommunity format
593 "rt 100:1 rt 100:2 soo 100:3"
595 "show [ip] bgp" and extcommunity-list regular expression matching
596 "RT:100:1 RT:100:2 SoO:100:3"
598 For each formath please use below definition for format:
600 ECOMMUNITY_FORMAT_ROUTE_MAP
601 ECOMMUNITY_FORMAT_COMMUNITY_LIST
602 ECOMMUNITY_FORMAT_DISPLAY
605 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
)
611 #define ECOMMUNITY_STR_DEFAULT_LEN 27
619 /* For parse Extended Community attribute tupple. */
634 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
639 /* Prepare buffer. */
640 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
641 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
644 for (i
= 0; i
< ecom
->size
; i
++)
646 /* Make it sure size is enough. */
647 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
650 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
653 /* Space between each value. */
655 str_buf
[str_pnt
++] = ' ';
657 pnt
= ecom
->val
+ (i
* 8);
659 /* High-order octet of type. */
664 case ECOMMUNITY_ENCODE_AS
:
665 case ECOMMUNITY_ENCODE_IP
:
666 case ECOMMUNITY_ENCODE_AS4
:
669 case ECOMMUNITY_ENCODE_OPAQUE
:
670 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
)
673 memcpy (&tunneltype
, pnt
+ 5, 2);
674 tunneltype
= ntohs(tunneltype
);
675 len
= sprintf (str_buf
+ str_pnt
, "ET:%d", tunneltype
);
683 len
= sprintf (str_buf
+ str_pnt
, "?");
689 /* Low-order octet of type. */
691 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
693 len
= sprintf (str_buf
+ str_pnt
, "?");
701 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
702 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
704 case ECOMMUNITY_FORMAT_DISPLAY
:
705 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
707 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
715 /* Put string into buffer. */
716 if (encode
== ECOMMUNITY_ENCODE_AS4
)
718 eas
.as
= (*pnt
++ << 24);
719 eas
.as
|= (*pnt
++ << 16);
720 eas
.as
|= (*pnt
++ << 8);
723 eas
.val
= (*pnt
++ << 8);
726 len
= sprintf( str_buf
+ str_pnt
, "%s%u:%u", prefix
,
731 if (encode
== ECOMMUNITY_ENCODE_AS
)
733 eas
.as
= (*pnt
++ << 8);
736 eas
.val
= (*pnt
++ << 24);
737 eas
.val
|= (*pnt
++ << 16);
738 eas
.val
|= (*pnt
++ << 8);
741 len
= sprintf (str_buf
+ str_pnt
, "%s%u:%u", prefix
,
746 else if (encode
== ECOMMUNITY_ENCODE_IP
)
748 memcpy (&eip
.ip
, pnt
, 4);
750 eip
.val
= (*pnt
++ << 8);
753 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%u", prefix
,
754 inet_ntoa (eip
.ip
), eip
.val
);
763 ecommunity_match (const struct ecommunity
*ecom1
,
764 const struct ecommunity
*ecom2
)
769 if (ecom1
== NULL
&& ecom2
== NULL
)
772 if (ecom1
== NULL
|| ecom2
== NULL
)
775 if (ecom1
->size
< ecom2
->size
)
778 /* Every community on com2 needs to be on com1 for this to match */
779 while (i
< ecom1
->size
&& j
< ecom2
->size
)
781 if (memcmp (ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
782 ecom2
->val
+ j
* ECOMMUNITY_SIZE
,
783 ECOMMUNITY_SIZE
) == 0)
788 if (j
== ecom2
->size
)