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
);
55 /* Add a new Extended Communities value to Extended Communities
56 Attribute structure. When the value is already exists in the
57 structure, we don't add the value. Newly added value is sorted by
58 numerical order. When the value is added to the structure return 1
61 ecommunity_add_val (struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
67 /* When this is fist value, just add it. */
68 if (ecom
->val
== NULL
)
71 ecom
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom_length (ecom
));
72 memcpy (ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
76 /* If the value already exists in the structure return 0. */
78 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++)
80 ret
= memcmp (p
, eval
->val
, ECOMMUNITY_SIZE
);
87 /* Add the value to the structure with numerical sorting. */
89 ecom
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length (ecom
));
91 memmove (ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
92 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
93 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
94 memcpy (ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
99 /* This function takes pointer to Extended Communites strucutre then
100 create a new Extended Communities structure by uniq and sort each
101 Extended Communities value. */
102 static struct ecommunity
*
103 ecommunity_uniq_sort (struct ecommunity
*ecom
)
106 struct ecommunity
*new;
107 struct ecommunity_val
*eval
;
112 new = ecommunity_new ();
114 for (i
= 0; i
< ecom
->size
; i
++)
116 eval
= (struct ecommunity_val
*) (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
117 ecommunity_add_val (new, eval
);
122 /* Parse Extended Communites Attribute in BGP packet. */
124 ecommunity_parse (u_int8_t
*pnt
, u_short length
)
126 struct ecommunity tmp
;
127 struct ecommunity
*new;
130 if (length
% ECOMMUNITY_SIZE
)
133 /* Prepare tmporary structure for making a new Extended Communities
135 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
138 /* Create a new Extended Communities Attribute by uniq and sort each
139 Extended Communities value */
140 new = ecommunity_uniq_sort (&tmp
);
142 return ecommunity_intern (new);
145 /* Duplicate the Extended Communities Attribute structure. */
147 ecommunity_dup (struct ecommunity
*ecom
)
149 struct ecommunity
*new;
151 new = XCALLOC (MTYPE_ECOMMUNITY
, sizeof (struct ecommunity
));
152 new->size
= ecom
->size
;
155 new->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
156 memcpy (new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
163 /* Retrun string representation of communities attribute. */
165 ecommunity_str (struct ecommunity
*ecom
)
168 ecom
->str
= ecommunity_ecom2str (ecom
, ECOMMUNITY_FORMAT_DISPLAY
);
172 /* Merge two Extended Communities Attribute structure. */
174 ecommunity_merge (struct ecommunity
*ecom1
, struct ecommunity
*ecom2
)
177 ecom1
->val
= XREALLOC (MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
178 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
180 ecom1
->val
= XMALLOC (MTYPE_ECOMMUNITY_VAL
,
181 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
183 memcpy (ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
),
184 ecom2
->val
, ecom2
->size
* ECOMMUNITY_SIZE
);
185 ecom1
->size
+= ecom2
->size
;
190 /* Intern Extended Communities Attribute. */
192 ecommunity_intern (struct ecommunity
*ecom
)
194 struct ecommunity
*find
;
196 assert (ecom
->refcnt
== 0);
198 find
= (struct ecommunity
*) hash_get (ecomhash
, ecom
, hash_alloc_intern
);
201 ecommunity_free (&ecom
);
206 find
->str
= ecommunity_ecom2str (find
, ECOMMUNITY_FORMAT_DISPLAY
);
211 /* Unintern Extended Communities Attribute. */
213 ecommunity_unintern (struct ecommunity
**ecom
)
215 struct ecommunity
*ret
;
220 /* Pull off from hash. */
221 if ((*ecom
)->refcnt
== 0)
223 /* Extended community must be in the hash. */
224 ret
= (struct ecommunity
*) hash_release (ecomhash
, *ecom
);
225 assert (ret
!= NULL
);
227 ecommunity_free (ecom
);
231 /* Utinity function to make hash key. */
233 ecommunity_hash_make (void *arg
)
235 const struct ecommunity
*ecom
= arg
;
243 for (c
= 0; c
< ecom
->size
* ECOMMUNITY_SIZE
; c
++)
249 /* Compare two Extended Communities Attribute structure. */
251 ecommunity_cmp (const void *arg1
, const void *arg2
)
253 const struct ecommunity
*ecom1
= arg1
;
254 const struct ecommunity
*ecom2
= arg2
;
256 return (ecom1
->size
== ecom2
->size
257 && memcmp (ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
) == 0);
260 /* Initialize Extended Comminities related hash. */
262 ecommunity_init (void)
264 ecomhash
= hash_create (ecommunity_hash_make
, ecommunity_cmp
);
268 ecommunity_finish (void)
270 hash_free (ecomhash
);
274 /* Extended Communities token enum. */
275 enum ecommunity_token
278 ecommunity_token_soo
,
279 ecommunity_token_val
,
280 ecommunity_token_unknown
283 /* Get next Extended Communities token from the string. */
285 ecommunity_gettoken (const char *str
, struct ecommunity_val
*eval
,
286 enum ecommunity_token
*token
)
297 char buf
[INET_ADDRSTRLEN
+ 1];
299 /* Skip white space. */
300 while (isspace ((int) *p
))
306 /* Check the end of the line. */
310 /* "rt" and "soo" keyword parse. */
311 if (! isdigit ((int) *p
))
313 /* "rt" match check. */
314 if (tolower ((int) *p
) == 'r')
317 if (tolower ((int) *p
) == 't')
320 *token
= ecommunity_token_rt
;
323 if (isspace ((int) *p
) || *p
== '\0')
325 *token
= ecommunity_token_rt
;
330 /* "soo" match check. */
331 else if (tolower ((int) *p
) == 's')
334 if (tolower ((int) *p
) == 'o')
337 if (tolower ((int) *p
) == 'o')
340 *token
= ecommunity_token_soo
;
343 if (isspace ((int) *p
) || *p
== '\0')
345 *token
= ecommunity_token_soo
;
350 if (isspace ((int) *p
) || *p
== '\0')
352 *token
= ecommunity_token_soo
;
360 /* What a mess, there are several possibilities:
366 * A.B.C.D: Four Byte IP
368 * GHJK: Four-byte ASN
370 * OPQR: Four byte value
373 while (isdigit ((int) *p
) || *p
== ':' || *p
== '.')
383 if ((p
- str
) > INET_ADDRSTRLEN
)
385 memset (buf
, 0, INET_ADDRSTRLEN
+ 1);
386 memcpy (buf
, str
, p
- str
);
390 /* Parsing A.B.C.D in:
393 ret
= inet_aton (buf
, &ip
);
400 as
= strtoul (buf
, &endptr
, 10);
401 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
417 /* We're past the IP/ASN part */
427 /* Low digit part must be there. */
428 if (!digit
|| !separator
)
431 /* Encode result into routing distinguisher. */
434 if (val
> UINT16_MAX
)
437 eval
->val
[0] = ECOMMUNITY_ENCODE_IP
;
439 memcpy (&eval
->val
[2], &ip
, sizeof (struct in_addr
));
440 eval
->val
[6] = (val
>> 8) & 0xff;
441 eval
->val
[7] = val
& 0xff;
443 else if (as
> BGP_AS_MAX
)
445 if (val
> UINT16_MAX
)
448 eval
->val
[0] = ECOMMUNITY_ENCODE_AS4
;
450 eval
->val
[2] = (as
>>24) & 0xff;
451 eval
->val
[3] = (as
>>16) & 0xff;
452 eval
->val
[4] = (as
>>8) & 0xff;
453 eval
->val
[5] = as
& 0xff;
454 eval
->val
[6] = (val
>> 8) & 0xff;
455 eval
->val
[7] = val
& 0xff;
459 eval
->val
[0] = ECOMMUNITY_ENCODE_AS
;
462 eval
->val
[2] = (as
>>8) & 0xff;
463 eval
->val
[3] = as
& 0xff;
464 eval
->val
[4] = (val
>>24) & 0xff;
465 eval
->val
[5] = (val
>>16) & 0xff;
466 eval
->val
[6] = (val
>>8) & 0xff;
467 eval
->val
[7] = val
& 0xff;
469 *token
= ecommunity_token_val
;
473 *token
= ecommunity_token_unknown
;
477 /* Convert string to extended community attribute.
479 When type is already known, please specify both str and type. str
480 should not include keyword such as "rt" and "soo". Type is
481 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
482 keyword_included should be zero.
484 For example route-map's "set extcommunity" command case:
486 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
487 type = ECOMMUNITY_ROUTE_TARGET
490 "soo 100:1" -> str = "100:1"
491 type = ECOMMUNITY_SITE_ORIGIN
494 When string includes keyword for each extended community value.
495 Please specify keyword_included as non-zero value.
497 For example standard extcommunity-list case:
499 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
504 ecommunity_str2com (const char *str
, int type
, int keyword_included
)
506 struct ecommunity
*ecom
= NULL
;
507 enum ecommunity_token token
;
508 struct ecommunity_val eval
;
511 while ((str
= ecommunity_gettoken (str
, &eval
, &token
)))
515 case ecommunity_token_rt
:
516 case ecommunity_token_soo
:
517 if (! keyword_included
|| keyword
)
520 ecommunity_free (&ecom
);
525 if (token
== ecommunity_token_rt
)
527 type
= ECOMMUNITY_ROUTE_TARGET
;
529 if (token
== ecommunity_token_soo
)
531 type
= ECOMMUNITY_SITE_ORIGIN
;
534 case ecommunity_token_val
:
535 if (keyword_included
)
540 ecommunity_free (&ecom
);
546 ecom
= ecommunity_new ();
548 ecommunity_add_val (ecom
, &eval
);
550 case ecommunity_token_unknown
:
553 ecommunity_free (&ecom
);
560 /* Convert extended community attribute to string.
562 Due to historical reason of industry standard implementation, there
563 are three types of format.
565 route-map set extcommunity format
570 "rt 100:1 rt 100:2 soo 100:3"
572 "show ip bgp" and extcommunity-list regular expression matching
573 "RT:100:1 RT:100:2 SoO:100:3"
575 For each formath please use below definition for format:
577 ECOMMUNITY_FORMAT_ROUTE_MAP
578 ECOMMUNITY_FORMAT_COMMUNITY_LIST
579 ECOMMUNITY_FORMAT_DISPLAY
582 ecommunity_ecom2str (struct ecommunity
*ecom
, int format
)
588 #define ECOMMUNITY_STR_DEFAULT_LEN 27
596 /* For parse Extended Community attribute tupple. */
611 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, 1);
616 /* Prepare buffer. */
617 str_buf
= XMALLOC (MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
618 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
621 for (i
= 0; i
< ecom
->size
; i
++)
623 /* Space between each value. */
625 str_buf
[str_pnt
++] = ' ';
627 pnt
= ecom
->val
+ (i
* 8);
629 /* High-order octet of type. */
631 if (encode
!= ECOMMUNITY_ENCODE_AS
&& encode
!= ECOMMUNITY_ENCODE_IP
632 && encode
!= ECOMMUNITY_ENCODE_AS4
)
634 len
= sprintf (str_buf
+ str_pnt
, "?");
640 /* Low-order octet of type. */
642 if (type
!= ECOMMUNITY_ROUTE_TARGET
&& type
!= ECOMMUNITY_SITE_ORIGIN
)
644 len
= sprintf (str_buf
+ str_pnt
, "?");
652 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
653 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
655 case ECOMMUNITY_FORMAT_DISPLAY
:
656 prefix
= (type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
658 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
666 /* Make it sure size is enough. */
667 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
)
670 str_buf
= XREALLOC (MTYPE_ECOMMUNITY_STR
, str_buf
, str_size
);
673 /* Put string into buffer. */
674 if (encode
== ECOMMUNITY_ENCODE_AS4
)
676 eas
.as
= (*pnt
++ << 24);
677 eas
.as
|= (*pnt
++ << 16);
678 eas
.as
|= (*pnt
++ << 8);
681 eas
.val
= (*pnt
++ << 8);
684 len
= sprintf( str_buf
+ str_pnt
, "%s%u:%d", prefix
,
689 if (encode
== ECOMMUNITY_ENCODE_AS
)
691 eas
.as
= (*pnt
++ << 8);
694 eas
.val
= (*pnt
++ << 24);
695 eas
.val
|= (*pnt
++ << 16);
696 eas
.val
|= (*pnt
++ << 8);
699 len
= sprintf (str_buf
+ str_pnt
, "%s%u:%d", prefix
,
704 else if (encode
== ECOMMUNITY_ENCODE_IP
)
706 memcpy (&eip
.ip
, pnt
, 4);
708 eip
.val
= (*pnt
++ << 8);
711 len
= sprintf (str_buf
+ str_pnt
, "%s%s:%d", prefix
,
712 inet_ntoa (eip
.ip
), eip
.val
);
721 ecommunity_match (const struct ecommunity
*ecom1
,
722 const struct ecommunity
*ecom2
)
727 if (ecom1
== NULL
&& ecom2
== NULL
)
730 if (ecom1
== NULL
|| ecom2
== NULL
)
733 if (ecom1
->size
< ecom2
->size
)
736 /* Every community on com2 needs to be on com1 for this to match */
737 while (i
< ecom1
->size
&& j
< ecom2
->size
)
739 if (memcmp (ecom1
->val
+ i
, ecom2
->val
+ j
, ECOMMUNITY_SIZE
) == 0)
744 if (j
== ecom2
->size
)