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 "lib/printfrr.h"
34 #include "bgpd/bgpd.h"
35 #include "bgpd/bgp_ecommunity.h"
36 #include "bgpd/bgp_lcommunity.h"
37 #include "bgpd/bgp_aspath.h"
38 #include "bgpd/bgp_flowspec_private.h"
39 #include "bgpd/bgp_pbr.h"
41 /* struct used to dump the rate contained in FS set traffic-rate EC */
47 /* Hash of community attribute. */
48 static struct hash
*ecomhash
;
50 /* Allocate a new ecommunities. */
51 struct ecommunity
*ecommunity_new(void)
53 struct ecommunity
*ecom
;
55 ecom
= (struct ecommunity
*)XCALLOC(MTYPE_ECOMMUNITY
,
56 sizeof(struct ecommunity
));
57 ecom
->unit_size
= ECOMMUNITY_SIZE
;
61 void ecommunity_strfree(char **s
)
63 XFREE(MTYPE_ECOMMUNITY_STR
, *s
);
66 /* Allocate ecommunities. */
67 void ecommunity_free(struct ecommunity
**ecom
)
72 XFREE(MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
73 XFREE(MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
74 XFREE(MTYPE_ECOMMUNITY
, *ecom
);
77 static void ecommunity_hash_free(struct ecommunity
*ecom
)
79 ecommunity_free(&ecom
);
83 /* Add a new Extended Communities value to Extended Communities
84 Attribute structure. When the value is already exists in the
85 structure, we don't add the value. Newly added value is sorted by
86 numerical order. When the value is added to the structure return 1
88 The additional parameters 'unique' and 'overwrite' ensure a particular
89 extended community (based on type and sub-type) is present only
90 once and whether the new value should replace what is existing or
93 static bool ecommunity_add_val_internal(struct ecommunity
*ecom
,
95 bool unique
, bool overwrite
,
99 const struct ecommunity_val
*eval4
= (struct ecommunity_val
*)eval
;
100 const struct ecommunity_val_ipv6
*eval6
=
101 (struct ecommunity_val_ipv6
*)eval
;
103 /* When this is fist value, just add it. */
104 if (ecom
->val
== NULL
) {
106 ecom
->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
107 ecom_length_size(ecom
, ecom_size
));
108 memcpy(ecom
->val
, eval
, ecom_size
);
112 /* If the value already exists in the structure return 0. */
113 /* check also if the extended community itself exists. */
117 for (uint8_t *p
= ecom
->val
; c
< ecom
->size
;
118 p
+= ecom_size
, c
++) {
120 if (ecom_size
== ECOMMUNITY_SIZE
) {
121 if (p
[0] == eval4
->val
[0] &&
122 p
[1] == eval4
->val
[1]) {
124 memcpy(p
, eval4
->val
,
131 if (p
[0] == eval6
->val
[0] &&
132 p
[1] == eval6
->val
[1]) {
134 memcpy(p
, eval6
->val
,
142 int ret
= memcmp(p
, eval
, ecom_size
);
156 /* Add the value to the structure with numerical sorting. */
158 ecom
->val
= XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->val
,
159 ecom_length_size(ecom
, ecom_size
));
162 memmove(ecom
->val
+ ((ins_idx
+ 1) * ecom_size
),
163 ecom
->val
+ (ins_idx
* ecom_size
),
164 (ecom
->size
- 1 - ins_idx
) * ecom_size
);
165 memcpy(ecom
->val
+ (ins_idx
* ecom_size
),
171 /* Add a new Extended Communities value to Extended Communities
172 * Attribute structure. When the value is already exists in the
173 * structure, we don't add the value. Newly added value is sorted by
174 * numerical order. When the value is added to the structure return 1
177 bool ecommunity_add_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
,
178 bool unique
, bool overwrite
)
180 return ecommunity_add_val_internal(ecom
, (const void *)eval
, unique
,
181 overwrite
, ECOMMUNITY_SIZE
);
184 bool ecommunity_add_val_ipv6(struct ecommunity
*ecom
,
185 struct ecommunity_val_ipv6
*eval
,
186 bool unique
, bool overwrite
)
188 return ecommunity_add_val_internal(ecom
, (const void *)eval
, unique
,
189 overwrite
, IPV6_ECOMMUNITY_SIZE
);
192 static struct ecommunity
*
193 ecommunity_uniq_sort_internal(struct ecommunity
*ecom
,
194 unsigned short ecom_size
)
197 struct ecommunity
*new;
203 new = ecommunity_new();
204 new->unit_size
= ecom_size
;
206 for (i
= 0; i
< ecom
->size
; i
++) {
207 eval
= (void *)(ecom
->val
+ (i
* ecom_size
));
208 ecommunity_add_val_internal(new, eval
, false, false, ecom_size
);
213 /* This function takes pointer to Extended Communites strucutre then
214 * create a new Extended Communities structure by uniq and sort each
215 * Extended Communities value.
217 struct ecommunity
*ecommunity_uniq_sort(struct ecommunity
*ecom
)
219 return ecommunity_uniq_sort_internal(ecom
, ECOMMUNITY_SIZE
);
222 /* Parse Extended Communites Attribute in BGP packet. */
223 static struct ecommunity
*ecommunity_parse_internal(uint8_t *pnt
,
224 unsigned short length
,
225 unsigned short size_ecom
)
227 struct ecommunity tmp
;
228 struct ecommunity
*new;
231 if (length
% size_ecom
)
234 /* Prepare tmporary structure for making a new Extended Communities
236 tmp
.size
= length
/ size_ecom
;
239 /* Create a new Extended Communities Attribute by uniq and sort each
240 Extended Communities value */
241 new = ecommunity_uniq_sort_internal(&tmp
, size_ecom
);
243 return ecommunity_intern(new);
246 struct ecommunity
*ecommunity_parse(uint8_t *pnt
,
247 unsigned short length
)
249 return ecommunity_parse_internal(pnt
, length
, ECOMMUNITY_SIZE
);
252 struct ecommunity
*ecommunity_parse_ipv6(uint8_t *pnt
,
253 unsigned short length
)
255 return ecommunity_parse_internal(pnt
, length
,
256 IPV6_ECOMMUNITY_SIZE
);
259 /* Duplicate the Extended Communities Attribute structure. */
260 struct ecommunity
*ecommunity_dup(struct ecommunity
*ecom
)
262 struct ecommunity
*new;
264 new = XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
265 new->size
= ecom
->size
;
266 new->unit_size
= ecom
->unit_size
;
268 new->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
269 ecom
->size
* ecom
->unit_size
);
270 memcpy(new->val
, ecom
->val
,
271 (size_t)ecom
->size
* (size_t)ecom
->unit_size
);
277 /* Retrun string representation of communities attribute. */
278 char *ecommunity_str(struct ecommunity
*ecom
)
282 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
286 /* Merge two Extended Communities Attribute structure. */
287 struct ecommunity
*ecommunity_merge(struct ecommunity
*ecom1
,
288 struct ecommunity
*ecom2
)
291 ecom1
->val
= XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
292 (size_t)(ecom1
->size
+ ecom2
->size
)
293 * (size_t)ecom1
->unit_size
);
295 ecom1
->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
296 (size_t)(ecom1
->size
+ ecom2
->size
)
297 * (size_t)ecom1
->unit_size
);
299 memcpy(ecom1
->val
+ (ecom1
->size
* ecom1
->unit_size
), ecom2
->val
,
300 (size_t)ecom2
->size
* (size_t)ecom1
->unit_size
);
301 ecom1
->size
+= ecom2
->size
;
306 /* Intern Extended Communities Attribute. */
307 struct ecommunity
*ecommunity_intern(struct ecommunity
*ecom
)
309 struct ecommunity
*find
;
311 assert(ecom
->refcnt
== 0);
312 find
= (struct ecommunity
*)hash_get(ecomhash
, ecom
, hash_alloc_intern
);
314 ecommunity_free(&ecom
);
320 ecommunity_ecom2str(find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
325 /* Unintern Extended Communities Attribute. */
326 void ecommunity_unintern(struct ecommunity
**ecom
)
328 struct ecommunity
*ret
;
333 /* Pull off from hash. */
334 if ((*ecom
)->refcnt
== 0) {
335 /* Extended community must be in the hash. */
336 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
339 ecommunity_free(ecom
);
343 /* Utinity function to make hash key. */
344 unsigned int ecommunity_hash_make(const void *arg
)
346 const struct ecommunity
*ecom
= arg
;
347 int size
= ecom
->size
* ecom
->unit_size
;
349 return jhash(ecom
->val
, size
, 0x564321ab);
352 /* Compare two Extended Communities Attribute structure. */
353 bool ecommunity_cmp(const void *arg1
, const void *arg2
)
355 const struct ecommunity
*ecom1
= arg1
;
356 const struct ecommunity
*ecom2
= arg2
;
358 if (ecom1
== NULL
&& ecom2
== NULL
)
361 if (ecom1
== NULL
|| ecom2
== NULL
)
364 if (ecom1
->unit_size
!= ecom2
->unit_size
)
367 return (ecom1
->size
== ecom2
->size
368 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
*
369 ecom1
->unit_size
) == 0);
372 /* Initialize Extended Comminities related hash. */
373 void ecommunity_init(void)
375 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
376 "BGP ecommunity hash");
379 void ecommunity_finish(void)
381 hash_clean(ecomhash
, (void (*)(void *))ecommunity_hash_free
);
386 /* Extended Communities token enum. */
387 enum ecommunity_token
{
388 ecommunity_token_unknown
= 0,
390 ecommunity_token_soo
,
391 ecommunity_token_val
,
392 ecommunity_token_rt6
,
393 ecommunity_token_val6
,
396 static int ecommunity_encode_internal(uint8_t type
, uint8_t sub_type
,
399 struct in6_addr
*ip6
,
403 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
404 struct ecommunity_val_ipv6
*eval6
=
405 (struct ecommunity_val_ipv6
*)eval_ptr
;
408 if (type
== ECOMMUNITY_ENCODE_AS
) {
411 } else if (type
== ECOMMUNITY_ENCODE_IP
412 || type
== ECOMMUNITY_ENCODE_AS4
) {
413 if (val
> UINT16_MAX
)
415 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
416 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
&&
417 (!ip6
|| val
> UINT16_MAX
)) {
421 /* Fill in the values. */
424 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
425 eval
->val
[1] = sub_type
;
426 if (type
== ECOMMUNITY_ENCODE_AS
) {
427 eval
->val
[2] = (as
>> 8) & 0xff;
428 eval
->val
[3] = as
& 0xff;
429 eval
->val
[4] = (val
>> 24) & 0xff;
430 eval
->val
[5] = (val
>> 16) & 0xff;
431 eval
->val
[6] = (val
>> 8) & 0xff;
432 eval
->val
[7] = val
& 0xff;
433 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
434 memcpy(&eval
->val
[2], ip
, sizeof(struct in_addr
));
435 eval
->val
[6] = (val
>> 8) & 0xff;
436 eval
->val
[7] = val
& 0xff;
437 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
438 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
439 memcpy(&eval6
->val
[2], ip6
, sizeof(struct in6_addr
));
440 eval6
->val
[18] = (val
>> 8) & 0xff;
441 eval6
->val
[19] = val
& 0xff;
443 eval
->val
[2] = (as
>> 24) & 0xff;
444 eval
->val
[3] = (as
>> 16) & 0xff;
445 eval
->val
[4] = (as
>> 8) & 0xff;
446 eval
->val
[5] = as
& 0xff;
447 eval
->val
[6] = (val
>> 8) & 0xff;
448 eval
->val
[7] = val
& 0xff;
455 * Encode BGP extended community from passed values. Supports types
456 * defined in RFC 4360 and well-known sub-types.
458 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
459 struct in_addr ip
, uint32_t val
,
460 struct ecommunity_val
*eval
)
462 return ecommunity_encode_internal(type
, sub_type
, trans
, as
,
463 &ip
, NULL
, val
, (void *)eval
);
466 /* Get next Extended Communities token from the string. */
467 static const char *ecommunity_gettoken(const char *str
,
469 enum ecommunity_token
*token
)
482 char buf
[INET_ADDRSTRLEN
+ 1];
483 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
484 /* Skip white space. */
485 while (isspace((unsigned char)*p
)) {
490 /* Check the end of the line. */
494 /* "rt" and "soo" keyword parse. */
495 if (!isdigit((unsigned char)*p
)) {
496 /* "rt" match check. */
497 if (tolower((unsigned char)*p
) == 'r') {
499 if (tolower((unsigned char)*p
) == 't') {
501 if (*p
!= '\0' && tolower((int)*p
) == '6')
502 *token
= ecommunity_token_rt6
;
504 *token
= ecommunity_token_rt
;
507 if (isspace((unsigned char)*p
) || *p
== '\0') {
508 *token
= ecommunity_token_rt
;
513 /* "soo" match check. */
514 else if (tolower((unsigned char)*p
) == 's') {
516 if (tolower((unsigned char)*p
) == 'o') {
518 if (tolower((unsigned char)*p
) == 'o') {
520 *token
= ecommunity_token_soo
;
523 if (isspace((unsigned char)*p
) || *p
== '\0') {
524 *token
= ecommunity_token_soo
;
529 if (isspace((unsigned char)*p
) || *p
== '\0') {
530 *token
= ecommunity_token_soo
;
538 /* What a mess, there are several possibilities:
543 * d) <IPV6>:MN (only with rt6)
545 * A.B.C.D: Four Byte IP
547 * GHJK: Four-byte ASN
549 * OPQR: Four byte value
552 /* IPv6 case : look for last ':' */
553 if (*token
== ecommunity_token_rt6
||
554 *token
== ecommunity_token_val6
) {
557 limit
= endptr
= strrchr(p
, ':');
562 as
= strtoul(endptr
, &endptr
, 10);
563 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
566 memcpy(buf
, p
, (limit
- p
));
567 buf
[limit
- p
] = '\0';
568 ret
= inet_pton(AF_INET6
, buf
, &ip6
);
572 ecomm_type
= ECOMMUNITY_ENCODE_TRANS_EXP
;
573 if (ecommunity_encode_internal(ecomm_type
,
574 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
,
575 1, 0, NULL
, &ip6
, as
, eval_ptr
))
578 *token
= ecommunity_token_val6
;
579 while (isdigit((int)*p
) || *p
== ':' || *p
== '.') {
584 while (isdigit((unsigned char)*p
) || *p
== ':' || *p
== '.') {
592 if ((p
- str
) > INET_ADDRSTRLEN
)
594 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
595 memcpy(buf
, str
, p
- str
);
598 /* Parsing A.B.C.D in:
601 ret
= inet_aton(buf
, &ip
);
606 as
= strtoul(buf
, &endptr
, 10);
607 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
610 } else if (*p
== '.') {
619 /* We're past the IP/ASN part */
628 /* Low digit part must be there. */
629 if (!digit
|| !separator
)
632 /* Encode result into extended community. */
634 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
635 else if (as
> BGP_AS_MAX
)
636 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
638 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
639 if (ecommunity_encode(ecomm_type
, 0, 1, as
, ip
, val
, eval
))
641 *token
= ecommunity_token_val
;
645 *token
= ecommunity_token_unknown
;
649 static struct ecommunity
*ecommunity_str2com_internal(const char *str
, int type
,
650 int keyword_included
,
651 bool is_ipv6_extcomm
)
653 struct ecommunity
*ecom
= NULL
;
654 enum ecommunity_token token
= ecommunity_token_unknown
;
655 struct ecommunity_val_ipv6 eval
;
659 token
= ecommunity_token_rt6
;
660 while ((str
= ecommunity_gettoken(str
, (void *)&eval
, &token
))) {
662 case ecommunity_token_rt
:
663 case ecommunity_token_soo
:
664 if (!keyword_included
|| keyword
) {
666 ecommunity_free(&ecom
);
671 if (token
== ecommunity_token_rt
||
672 token
== ecommunity_token_rt6
) {
673 type
= ECOMMUNITY_ROUTE_TARGET
;
675 if (token
== ecommunity_token_soo
) {
676 type
= ECOMMUNITY_SITE_ORIGIN
;
679 case ecommunity_token_val
:
680 if (keyword_included
) {
682 ecommunity_free(&ecom
);
688 ecom
= ecommunity_new();
690 ecommunity_add_val_internal(ecom
, (void *)&eval
,
694 case ecommunity_token_val6
:
695 if (keyword_included
) {
697 ecommunity_free(&ecom
);
703 ecom
= ecommunity_new();
704 ecom
->unit_size
= IPV6_ECOMMUNITY_SIZE
;
706 ecommunity_add_val_internal(ecom
, (void *)&eval
, false, false,
709 case ecommunity_token_unknown
:
712 ecommunity_free(&ecom
);
719 /* Convert string to extended community attribute.
721 * When type is already known, please specify both str and type. str
722 * should not include keyword such as "rt" and "soo". Type is
723 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
724 * keyword_included should be zero.
726 * For example route-map's "set extcommunity" command case:
728 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
729 * type = ECOMMUNITY_ROUTE_TARGET
730 * keyword_included = 0
732 * "soo 100:1" -> str = "100:1"
733 * type = ECOMMUNITY_SITE_ORIGIN
734 * keyword_included = 0
736 * When string includes keyword for each extended community value.
737 * Please specify keyword_included as non-zero value.
739 * For example standard extcommunity-list case:
741 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
743 * keyword_include = 1
745 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
746 int keyword_included
)
748 return ecommunity_str2com_internal(str
, type
,
749 keyword_included
, false);
752 struct ecommunity
*ecommunity_str2com_ipv6(const char *str
, int type
,
753 int keyword_included
)
755 return ecommunity_str2com_internal(str
, type
,
756 keyword_included
, true);
759 static int ecommunity_rt_soo_str_internal(char *buf
, size_t bufsz
,
760 const uint8_t *pnt
, int type
,
761 int sub_type
, int format
,
762 unsigned short ecom_size
)
766 char buf_local
[INET6_ADDRSTRLEN
];
768 /* For parse Extended Community attribute tupple. */
769 struct ecommunity_as eas
;
770 struct ecommunity_ip eip
;
771 struct ecommunity_ip6 eip6
;
773 /* Determine prefix for string, if any. */
775 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
776 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
778 case ECOMMUNITY_FORMAT_DISPLAY
:
779 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
781 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
789 /* Put string into buffer. */
790 if (type
== ECOMMUNITY_ENCODE_AS4
) {
791 pnt
= ptr_get_be32(pnt
, &eas
.as
);
792 eas
.val
= (*pnt
++ << 8);
795 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
796 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
797 if (ecom_size
== ECOMMUNITY_SIZE
) {
798 eas
.as
= (*pnt
++ << 8);
800 pnt
= ptr_get_be32(pnt
, &eas
.val
);
802 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
,
805 /* this is an IPv6 ext community
806 * first 16 bytes stands for IPv6 addres
808 memcpy(&eip6
.ip
, pnt
, 16);
810 eip6
.val
= (*pnt
++ << 8);
811 eip6
.val
|= (*pnt
++);
813 inet_ntop(AF_INET6
, &eip6
.ip
, buf_local
,
815 len
= snprintf(buf
, bufsz
, "%s%s:%u", prefix
,
816 buf_local
, eip6
.val
);
818 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
819 memcpy(&eip
.ip
, pnt
, 4);
821 eip
.val
= (*pnt
++ << 8);
824 len
= snprintfrr(buf
, bufsz
, "%s%pI4:%u", prefix
, &eip
.ip
,
834 static int ecommunity_rt_soo_str(char *buf
, size_t bufsz
, const uint8_t *pnt
,
835 int type
, int sub_type
, int format
)
837 return ecommunity_rt_soo_str_internal(buf
, bufsz
, pnt
, type
,
842 static int ecommunity_lb_str(char *buf
, size_t bufsz
, const uint8_t *pnt
)
847 char bps_buf
[20] = {0};
849 #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
850 #define ONE_MBPS_BYTES (1000 * 1000 / 8)
851 #define ONE_KBPS_BYTES (1000 / 8)
855 (void)ptr_get_be32(pnt
, &bw
);
856 if (bw
>= ONE_GBPS_BYTES
)
857 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Gbps",
858 (float)(bw
/ ONE_GBPS_BYTES
));
859 else if (bw
>= ONE_MBPS_BYTES
)
860 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Mbps",
861 (float)(bw
/ ONE_MBPS_BYTES
));
862 else if (bw
>= ONE_KBPS_BYTES
)
863 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Kbps",
864 (float)(bw
/ ONE_KBPS_BYTES
));
866 snprintf(bps_buf
, sizeof(bps_buf
), "%u bps", bw
* 8);
868 len
= snprintf(buf
, bufsz
, "LB:%u:%u (%s)", as
, bw
, bps_buf
);
872 /* Convert extended community attribute to string.
874 Due to historical reason of industry standard implementation, there
875 are three types of format.
877 route-map set extcommunity format
878 "rt 100:1 100:2soo 100:3"
881 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
882 "RT:100:1 RT:100:2 SoO:100:3"
884 For each formath please use below definition for format:
886 ECOMMUNITY_FORMAT_ROUTE_MAP
887 ECOMMUNITY_FORMAT_COMMUNITY_LIST
888 ECOMMUNITY_FORMAT_DISPLAY
890 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
893 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
898 uint8_t sub_type
= 0;
899 #define ECOMMUNITY_STRLEN 64
904 return XCALLOC(MTYPE_ECOMMUNITY_STR
, 1);
906 /* ecom strlen + space + null term */
907 str_size
= (ecom
->size
* (ECOMMUNITY_STRLEN
+ 1)) + 1;
908 str_buf
= XCALLOC(MTYPE_ECOMMUNITY_STR
, str_size
);
912 for (i
= 0; i
< ecom
->size
; i
++) {
914 memset(encbuf
, 0x00, sizeof(encbuf
));
916 /* Space between each value. */
918 strlcat(str_buf
, " ", str_size
);
920 /* Retrieve value field */
921 pnt
= ecom
->val
+ (i
* 8);
923 /* High-order octet is the type */
926 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
927 || type
== ECOMMUNITY_ENCODE_AS4
) {
928 /* Low-order octet of type. */
930 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
931 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
) {
933 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4
&&
934 type
== ECOMMUNITY_ENCODE_IP
) {
935 struct in_addr
*ipv4
=
936 (struct in_addr
*)pnt
;
937 char ipv4str
[INET_ADDRSTRLEN
];
939 inet_ntop(AF_INET
, ipv4
,
942 snprintf(encbuf
, sizeof(encbuf
),
943 "NH:%s:%d", ipv4str
, pnt
[5]);
944 } else if (sub_type
==
945 ECOMMUNITY_LINK_BANDWIDTH
&&
946 type
== ECOMMUNITY_ENCODE_AS
) {
947 ecommunity_lb_str(encbuf
,
948 sizeof(encbuf
), pnt
);
952 ecommunity_rt_soo_str(encbuf
, sizeof(encbuf
),
956 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
957 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
959 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
961 memcpy(&tunneltype
, pnt
+ 5, 2);
962 tunneltype
= ntohs(tunneltype
);
964 snprintf(encbuf
, sizeof(encbuf
), "ET:%d",
966 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
967 strlcpy(encbuf
, "Default Gateway",
972 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
973 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
975 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
978 memcpy(&rmac
, pnt
, ETH_ALEN
);
980 snprintf(encbuf
, sizeof(encbuf
),
981 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
982 (uint8_t)rmac
.octet
[0],
983 (uint8_t)rmac
.octet
[1],
984 (uint8_t)rmac
.octet
[2],
985 (uint8_t)rmac
.octet
[3],
986 (uint8_t)rmac
.octet
[4],
987 (uint8_t)rmac
.octet
[5]);
989 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
991 uint8_t flags
= *++pnt
;
993 memcpy(&seqnum
, pnt
+ 2, 4);
994 seqnum
= ntohl(seqnum
);
996 snprintf(encbuf
, sizeof(encbuf
), "MM:%u",
1001 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
))
1002 strlcat(encbuf
, ", sticky MAC",
1004 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ND
) {
1005 uint8_t flags
= *++pnt
;
1009 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG
))
1010 strlcpy(encbuf
, "ND:Router Flag",
1014 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG
))
1015 strlcpy(encbuf
, "ND:Proxy",
1018 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT
) {
1022 memcpy(&mac
, pnt
, ETH_ALEN
);
1025 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1026 (uint8_t)mac
.octet
[0],
1027 (uint8_t)mac
.octet
[1],
1028 (uint8_t)mac
.octet
[2],
1029 (uint8_t)mac
.octet
[3],
1030 (uint8_t)mac
.octet
[4],
1031 (uint8_t)mac
.octet
[5]);
1033 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL
) {
1034 uint8_t flags
= *++pnt
;
1037 sizeof(encbuf
), "ESI-label-Rt:%s",
1039 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG
) ?
1042 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION
) {
1048 memcpy(&bmap
, pnt
+ 2, 2);
1050 memcpy(&pref
, pnt
+ 5, 2);
1055 encbuf
, sizeof(encbuf
),
1056 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1059 snprintf(encbuf
, sizeof(encbuf
),
1060 "DF: (alg: %u, pref: %u)", alg
,
1064 } else if (type
== ECOMMUNITY_ENCODE_REDIRECT_IP_NH
) {
1066 if (sub_type
== ECOMMUNITY_REDIRECT_IP_NH
) {
1067 snprintf(encbuf
, sizeof(encbuf
),
1068 "FS:redirect IP 0x%x", *(pnt
+ 5));
1071 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
||
1072 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_2
||
1073 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_3
) {
1076 if (sub_type
== ECOMMUNITY_ROUTE_TARGET
) {
1077 char buf
[ECOMMUNITY_STRLEN
];
1079 memset(buf
, 0, sizeof(buf
));
1080 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1081 (const uint8_t *)pnt
,
1083 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1084 ECOMMUNITY_ROUTE_TARGET
,
1087 snprintf(encbuf
, sizeof(encbuf
), "%s", buf
);
1088 } else if (sub_type
==
1089 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
1092 memset(buf
, 0, sizeof(buf
));
1093 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1094 (const uint8_t *)pnt
,
1096 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1097 ECOMMUNITY_ROUTE_TARGET
,
1098 ECOMMUNITY_FORMAT_DISPLAY
,
1100 snprintf(encbuf
, sizeof(encbuf
),
1101 "FS:redirect VRF %s", buf
);
1102 } else if (sub_type
== ECOMMUNITY_REDIRECT_VRF
) {
1105 memset(buf
, 0, sizeof(buf
));
1106 ecommunity_rt_soo_str(buf
, sizeof(buf
),
1107 (const uint8_t *)pnt
,
1109 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1110 ECOMMUNITY_ROUTE_TARGET
,
1111 ECOMMUNITY_FORMAT_DISPLAY
);
1112 snprintf(encbuf
, sizeof(encbuf
),
1113 "FS:redirect VRF %s", buf
);
1114 snprintf(encbuf
, sizeof(encbuf
),
1115 "FS:redirect VRF %s", buf
);
1116 } else if (type
!= ECOMMUNITY_ENCODE_TRANS_EXP
)
1118 else if (sub_type
== ECOMMUNITY_TRAFFIC_ACTION
) {
1122 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
)
1123 strlcpy(action
, "terminate (apply)",
1126 strlcpy(action
, "eval stops",
1130 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1131 strlcat(action
, ", sample",
1135 snprintf(encbuf
, sizeof(encbuf
), "FS:action %s",
1137 } else if (sub_type
== ECOMMUNITY_TRAFFIC_RATE
) {
1138 union traffic_rate data
;
1140 data
.rate_byte
[3] = *(pnt
+2);
1141 data
.rate_byte
[2] = *(pnt
+3);
1142 data
.rate_byte
[1] = *(pnt
+4);
1143 data
.rate_byte
[0] = *(pnt
+5);
1144 snprintf(encbuf
, sizeof(encbuf
), "FS:rate %f",
1146 } else if (sub_type
== ECOMMUNITY_TRAFFIC_MARKING
) {
1147 snprintf(encbuf
, sizeof(encbuf
),
1148 "FS:marking %u", *(pnt
+ 5));
1151 } else if (type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) {
1153 if (sub_type
== ECOMMUNITY_LINK_BANDWIDTH
)
1154 ecommunity_lb_str(encbuf
, sizeof(encbuf
), pnt
);
1163 snprintf(encbuf
, sizeof(encbuf
), "UNK:%d, %d", type
,
1166 int r
= strlcat(str_buf
, encbuf
, str_size
);
1167 assert(r
< str_size
);
1173 bool ecommunity_match(const struct ecommunity
*ecom1
,
1174 const struct ecommunity
*ecom2
)
1179 if (ecom1
== NULL
&& ecom2
== NULL
)
1182 if (ecom1
== NULL
|| ecom2
== NULL
)
1185 if (ecom1
->size
< ecom2
->size
)
1188 /* Every community on com2 needs to be on com1 for this to match */
1189 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
1190 if (memcmp(ecom1
->val
+ i
* ecom1
->unit_size
,
1191 ecom2
->val
+ j
* ecom2
->unit_size
,
1198 if (j
== ecom2
->size
)
1204 /* return first occurence of type */
1205 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
1206 uint8_t type
, uint8_t subtype
)
1211 /* If the value already exists in the structure return 0. */
1213 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1217 if (p
[0] == type
&& p
[1] == subtype
)
1218 return (struct ecommunity_val
*)p
;
1223 /* remove ext. community matching type and subtype
1224 * return 1 on success ( removed ), 0 otherwise (not present)
1226 bool ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
1229 uint8_t *p
, *q
, *new;
1231 /* When this is fist value, just add it. */
1232 if (ecom
== NULL
|| ecom
->val
== NULL
)
1235 /* Check if any existing ext community matches. */
1236 /* Certain extended communities like the Route Target can be present
1237 * multiple times, handle that.
1240 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1241 if (p
[0] == type
&& p
[1] == subtype
)
1244 /* If no matching ext community exists, return. */
1248 /* Handle the case where everything needs to be stripped. */
1249 if (found
== ecom
->size
) {
1250 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1255 /* Strip matching ext community(ies). */
1256 new = XMALLOC(MTYPE_ECOMMUNITY_VAL
,
1257 (ecom
->size
- found
) * ecom
->unit_size
);
1259 for (c
= 0, p
= ecom
->val
; c
< ecom
->size
; c
++, p
+= ecom
->unit_size
) {
1260 if (!(p
[0] == type
&& p
[1] == subtype
)) {
1261 memcpy(q
, p
, ecom
->unit_size
);
1262 q
+= ecom
->unit_size
;
1265 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1267 ecom
->size
-= found
;
1272 * Remove specified extended community value from extended community.
1273 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1275 bool ecommunity_del_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
1280 /* Make sure specified value exists. */
1281 if (ecom
== NULL
|| ecom
->val
== NULL
)
1284 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1285 if (!memcmp(p
, eval
->val
, ecom
->unit_size
)) {
1293 /* Delete the selected value */
1295 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ecom
->unit_size
);
1297 memcpy(p
, ecom
->val
, c
* ecom
->unit_size
);
1298 if ((ecom
->size
- c
) != 0)
1299 memcpy(p
+ (c
)*ecom
->unit_size
,
1300 ecom
->val
+ (c
+ 1) * ecom
->unit_size
,
1301 (ecom
->size
- c
) * ecom
->unit_size
);
1302 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1307 int ecommunity_fill_pbr_action(struct ecommunity_val
*ecom_eval
,
1308 struct bgp_pbr_entry_action
*api
,
1311 if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_RATE
) {
1312 api
->action
= ACTION_TRAFFICRATE
;
1313 api
->u
.r
.rate_info
[3] = ecom_eval
->val
[4];
1314 api
->u
.r
.rate_info
[2] = ecom_eval
->val
[5];
1315 api
->u
.r
.rate_info
[1] = ecom_eval
->val
[6];
1316 api
->u
.r
.rate_info
[0] = ecom_eval
->val
[7];
1317 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_ACTION
) {
1318 api
->action
= ACTION_TRAFFIC_ACTION
;
1319 /* else distribute code is set by default */
1320 if (ecom_eval
->val
[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
))
1321 api
->u
.za
.filter
|= TRAFFIC_ACTION_TERMINATE
;
1323 api
->u
.za
.filter
|= TRAFFIC_ACTION_DISTRIBUTE
;
1324 if (ecom_eval
->val
[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1325 api
->u
.za
.filter
|= TRAFFIC_ACTION_SAMPLE
;
1327 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_MARKING
) {
1328 api
->action
= ACTION_MARKING
;
1329 api
->u
.marking_dscp
= ecom_eval
->val
[7];
1330 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_VRF
) {
1331 /* must use external function */
1333 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_IP_NH
&&
1335 /* see draft-ietf-idr-flowspec-redirect-ip-02
1336 * Q1: how come a ext. community can host ipv6 address
1337 * Q2 : from cisco documentation:
1338 * Announces the reachability of one or more flowspec NLRI.
1339 * When a BGP speaker receives an UPDATE message with the
1340 * redirect-to-IP extended community, it is expected to
1341 * create a traffic filtering rule for every flow-spec
1342 * NLRI in the message that has this path as its best
1343 * path. The filter entry matches the IP packets
1344 * described in the NLRI field and redirects them or
1345 * copies them towards the IPv4 or IPv6 address specified
1346 * in the 'Network Address of Next- Hop'
1347 * field of the associated MP_REACH_NLRI.
1349 struct ecommunity_ip
*ip_ecom
= (struct ecommunity_ip
*)
1352 api
->u
.zr
.redirect_ip_v4
= ip_ecom
->ip
;
1358 static struct ecommunity
*bgp_aggr_ecommunity_lookup(
1359 struct bgp_aggregate
*aggregate
,
1360 struct ecommunity
*ecommunity
)
1362 return hash_lookup(aggregate
->ecommunity_hash
, ecommunity
);
1365 static void *bgp_aggr_ecommunty_hash_alloc(void *p
)
1367 struct ecommunity
*ref
= (struct ecommunity
*)p
;
1368 struct ecommunity
*ecommunity
= NULL
;
1370 ecommunity
= ecommunity_dup(ref
);
1374 static void bgp_aggr_ecommunity_prepare(struct hash_bucket
*hb
, void *arg
)
1376 struct ecommunity
*hb_ecommunity
= hb
->data
;
1377 struct ecommunity
**aggr_ecommunity
= arg
;
1379 if (*aggr_ecommunity
)
1380 *aggr_ecommunity
= ecommunity_merge(*aggr_ecommunity
,
1383 *aggr_ecommunity
= ecommunity_dup(hb_ecommunity
);
1386 void bgp_aggr_ecommunity_remove(void *arg
)
1388 struct ecommunity
*ecommunity
= arg
;
1390 ecommunity_free(&ecommunity
);
1393 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate
*aggregate
,
1394 struct ecommunity
*ecommunity
)
1396 bgp_compute_aggregate_ecommunity_hash(aggregate
, ecommunity
);
1397 bgp_compute_aggregate_ecommunity_val(aggregate
);
1401 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate
*aggregate
,
1402 struct ecommunity
*ecommunity
)
1404 struct ecommunity
*aggr_ecommunity
= NULL
;
1406 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1409 /* Create hash if not already created.
1411 if (aggregate
->ecommunity_hash
== NULL
)
1412 aggregate
->ecommunity_hash
= hash_create(
1413 ecommunity_hash_make
, ecommunity_cmp
,
1414 "BGP Aggregator ecommunity hash");
1416 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1417 if (aggr_ecommunity
== NULL
) {
1418 /* Insert ecommunity into hash.
1420 aggr_ecommunity
= hash_get(aggregate
->ecommunity_hash
,
1422 bgp_aggr_ecommunty_hash_alloc
);
1425 /* Increment reference counter.
1427 aggr_ecommunity
->refcnt
++;
1430 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate
*aggregate
)
1432 struct ecommunity
*ecommerge
= NULL
;
1434 if (aggregate
== NULL
)
1437 /* Re-compute aggregate's ecommunity.
1439 if (aggregate
->ecommunity
)
1440 ecommunity_free(&aggregate
->ecommunity
);
1441 if (aggregate
->ecommunity_hash
1442 && aggregate
->ecommunity_hash
->count
) {
1443 hash_iterate(aggregate
->ecommunity_hash
,
1444 bgp_aggr_ecommunity_prepare
,
1445 &aggregate
->ecommunity
);
1446 ecommerge
= aggregate
->ecommunity
;
1447 aggregate
->ecommunity
= ecommunity_uniq_sort(ecommerge
);
1449 ecommunity_free(&ecommerge
);
1453 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
1454 struct ecommunity
*ecommunity
)
1456 struct ecommunity
*aggr_ecommunity
= NULL
;
1457 struct ecommunity
*ret_ecomm
= NULL
;
1460 || (!aggregate
->ecommunity_hash
)
1464 /* Look-up the ecommunity in the hash.
1466 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1467 if (aggr_ecommunity
) {
1468 aggr_ecommunity
->refcnt
--;
1470 if (aggr_ecommunity
->refcnt
== 0) {
1471 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1473 ecommunity_free(&ret_ecomm
);
1474 bgp_compute_aggregate_ecommunity_val(aggregate
);
1479 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1480 struct ecommunity
*ecommunity
)
1483 struct ecommunity
*aggr_ecommunity
= NULL
;
1484 struct ecommunity
*ret_ecomm
= NULL
;
1487 || (!aggregate
->ecommunity_hash
)
1491 /* Look-up the ecommunity in the hash.
1493 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1494 if (aggr_ecommunity
) {
1495 aggr_ecommunity
->refcnt
--;
1497 if (aggr_ecommunity
->refcnt
== 0) {
1498 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1500 ecommunity_free(&ret_ecomm
);
1506 * return the BGP link bandwidth extended community, if present;
1507 * the actual bandwidth is returned via param
1509 const uint8_t *ecommunity_linkbw_present(struct ecommunity
*ecom
, uint32_t *bw
)
1511 const uint8_t *eval
;
1517 if (!ecom
|| !ecom
->size
)
1520 for (i
= 0; i
< ecom
->size
; i
++) {
1522 uint8_t type
, sub_type
;
1525 eval
= pnt
= (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
1529 if ((type
== ECOMMUNITY_ENCODE_AS
||
1530 type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) &&
1531 sub_type
== ECOMMUNITY_LINK_BANDWIDTH
) {
1532 pnt
+= 2; /* bandwidth is encoded as AS:val */
1533 pnt
= ptr_get_be32(pnt
, &bwval
);
1534 (void)pnt
; /* consume value */
1545 struct ecommunity
*ecommunity_replace_linkbw(as_t as
,
1546 struct ecommunity
*ecom
,
1549 struct ecommunity
*new;
1550 struct ecommunity_val lb_eval
;
1551 const uint8_t *eval
;
1555 /* Nothing to replace if link-bandwidth doesn't exist or
1556 * is non-transitive - just return existing extcommunity.
1559 if (!ecom
|| !ecom
->size
)
1562 eval
= ecommunity_linkbw_present(ecom
, &cur_bw
);
1567 if (type
& ECOMMUNITY_FLAG_NON_TRANSITIVE
)
1570 /* Transitive link-bandwidth exists, replace with the passed
1571 * (cumulative) bandwidth value. We need to create a new
1572 * extcommunity for this - refer to AS-Path replace function
1575 if (cum_bw
> 0xFFFFFFFF)
1576 cum_bw
= 0xFFFFFFFF;
1577 encode_lb_extcomm(as
> BGP_AS_MAX
? BGP_AS_TRANS
: as
, cum_bw
,
1579 new = ecommunity_dup(ecom
);
1580 ecommunity_add_val(new, &lb_eval
, true, true);