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. */
116 ins_idx
= UINT32_MAX
;
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
);
148 if (ins_idx
== UINT32_MAX
)
153 if (ins_idx
== UINT32_MAX
)
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
;
336 /* Pull off from hash. */
337 if ((*ecom
)->refcnt
== 0) {
338 /* Extended community must be in the hash. */
339 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
342 ecommunity_free(ecom
);
346 /* Utinity function to make hash key. */
347 unsigned int ecommunity_hash_make(const void *arg
)
349 const struct ecommunity
*ecom
= arg
;
350 int size
= ecom
->size
* ecom
->unit_size
;
352 return jhash(ecom
->val
, size
, 0x564321ab);
355 /* Compare two Extended Communities Attribute structure. */
356 bool ecommunity_cmp(const void *arg1
, const void *arg2
)
358 const struct ecommunity
*ecom1
= arg1
;
359 const struct ecommunity
*ecom2
= arg2
;
361 if (ecom1
== NULL
&& ecom2
== NULL
)
364 if (ecom1
== NULL
|| ecom2
== NULL
)
367 if (ecom1
->unit_size
!= ecom2
->unit_size
)
370 return (ecom1
->size
== ecom2
->size
371 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
*
372 ecom1
->unit_size
) == 0);
375 /* Initialize Extended Comminities related hash. */
376 void ecommunity_init(void)
378 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
379 "BGP ecommunity hash");
382 void ecommunity_finish(void)
384 hash_clean(ecomhash
, (void (*)(void *))ecommunity_hash_free
);
389 /* Extended Communities token enum. */
390 enum ecommunity_token
{
391 ecommunity_token_unknown
= 0,
393 ecommunity_token_soo
,
394 ecommunity_token_val
,
395 ecommunity_token_rt6
,
396 ecommunity_token_val6
,
399 static int ecommunity_encode_internal(uint8_t type
, uint8_t sub_type
,
402 struct in6_addr
*ip6
,
406 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
407 struct ecommunity_val_ipv6
*eval6
=
408 (struct ecommunity_val_ipv6
*)eval_ptr
;
411 if (type
== ECOMMUNITY_ENCODE_AS
) {
414 } else if (type
== ECOMMUNITY_ENCODE_IP
415 || type
== ECOMMUNITY_ENCODE_AS4
) {
416 if (val
> UINT16_MAX
)
418 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
419 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
&&
420 (!ip6
|| val
> UINT16_MAX
)) {
424 /* Fill in the values. */
427 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
428 eval
->val
[1] = sub_type
;
429 if (type
== ECOMMUNITY_ENCODE_AS
) {
430 eval
->val
[2] = (as
>> 8) & 0xff;
431 eval
->val
[3] = as
& 0xff;
432 eval
->val
[4] = (val
>> 24) & 0xff;
433 eval
->val
[5] = (val
>> 16) & 0xff;
434 eval
->val
[6] = (val
>> 8) & 0xff;
435 eval
->val
[7] = val
& 0xff;
436 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
437 memcpy(&eval
->val
[2], ip
, sizeof(struct in_addr
));
438 eval
->val
[6] = (val
>> 8) & 0xff;
439 eval
->val
[7] = val
& 0xff;
440 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
441 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
442 memcpy(&eval6
->val
[2], ip6
, sizeof(struct in6_addr
));
443 eval6
->val
[18] = (val
>> 8) & 0xff;
444 eval6
->val
[19] = val
& 0xff;
446 eval
->val
[2] = (as
>> 24) & 0xff;
447 eval
->val
[3] = (as
>> 16) & 0xff;
448 eval
->val
[4] = (as
>> 8) & 0xff;
449 eval
->val
[5] = as
& 0xff;
450 eval
->val
[6] = (val
>> 8) & 0xff;
451 eval
->val
[7] = val
& 0xff;
458 * Encode BGP extended community from passed values. Supports types
459 * defined in RFC 4360 and well-known sub-types.
461 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
462 struct in_addr ip
, uint32_t val
,
463 struct ecommunity_val
*eval
)
465 return ecommunity_encode_internal(type
, sub_type
, trans
, as
,
466 &ip
, NULL
, val
, (void *)eval
);
469 /* Get next Extended Communities token from the string. */
470 static const char *ecommunity_gettoken(const char *str
,
472 enum ecommunity_token
*token
)
485 char buf
[INET_ADDRSTRLEN
+ 1];
486 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
487 /* Skip white space. */
488 while (isspace((unsigned char)*p
)) {
493 /* Check the end of the line. */
497 /* "rt" and "soo" keyword parse. */
498 if (!isdigit((unsigned char)*p
)) {
499 /* "rt" match check. */
500 if (tolower((unsigned char)*p
) == 'r') {
502 if (tolower((unsigned char)*p
) == 't') {
504 if (*p
!= '\0' && tolower((int)*p
) == '6')
505 *token
= ecommunity_token_rt6
;
507 *token
= ecommunity_token_rt
;
510 if (isspace((unsigned char)*p
) || *p
== '\0') {
511 *token
= ecommunity_token_rt
;
516 /* "soo" match check. */
517 else if (tolower((unsigned char)*p
) == 's') {
519 if (tolower((unsigned char)*p
) == 'o') {
521 if (tolower((unsigned char)*p
) == 'o') {
523 *token
= ecommunity_token_soo
;
526 if (isspace((unsigned char)*p
) || *p
== '\0') {
527 *token
= ecommunity_token_soo
;
532 if (isspace((unsigned char)*p
) || *p
== '\0') {
533 *token
= ecommunity_token_soo
;
541 /* What a mess, there are several possibilities:
546 * d) <IPV6>:MN (only with rt6)
548 * A.B.C.D: Four Byte IP
550 * GHJK: Four-byte ASN
552 * OPQR: Four byte value
555 /* IPv6 case : look for last ':' */
556 if (*token
== ecommunity_token_rt6
||
557 *token
== ecommunity_token_val6
) {
560 limit
= endptr
= strrchr(p
, ':');
565 as
= strtoul(endptr
, &endptr
, 10);
566 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
569 memcpy(buf
, p
, (limit
- p
));
570 buf
[limit
- p
] = '\0';
571 ret
= inet_pton(AF_INET6
, buf
, &ip6
);
575 ecomm_type
= ECOMMUNITY_ENCODE_TRANS_EXP
;
576 if (ecommunity_encode_internal(ecomm_type
,
577 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
,
578 1, 0, NULL
, &ip6
, as
, eval_ptr
))
581 *token
= ecommunity_token_val6
;
582 while (isdigit((int)*p
) || *p
== ':' || *p
== '.') {
587 while (isdigit((unsigned char)*p
) || *p
== ':' || *p
== '.') {
595 if ((p
- str
) > INET_ADDRSTRLEN
)
597 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
598 memcpy(buf
, str
, p
- str
);
601 /* Parsing A.B.C.D in:
604 ret
= inet_aton(buf
, &ip
);
609 as
= strtoul(buf
, &endptr
, 10);
610 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
613 } else if (*p
== '.') {
622 /* We're past the IP/ASN part */
631 /* Low digit part must be there. */
632 if (!digit
|| !separator
)
635 /* Encode result into extended community. */
637 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
638 else if (as
> BGP_AS_MAX
)
639 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
641 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
642 if (ecommunity_encode(ecomm_type
, 0, 1, as
, ip
, val
, eval
))
644 *token
= ecommunity_token_val
;
648 *token
= ecommunity_token_unknown
;
652 static struct ecommunity
*ecommunity_str2com_internal(const char *str
, int type
,
653 int keyword_included
,
654 bool is_ipv6_extcomm
)
656 struct ecommunity
*ecom
= NULL
;
657 enum ecommunity_token token
= ecommunity_token_unknown
;
658 struct ecommunity_val_ipv6 eval
;
662 token
= ecommunity_token_rt6
;
663 while ((str
= ecommunity_gettoken(str
, (void *)&eval
, &token
))) {
665 case ecommunity_token_rt
:
666 case ecommunity_token_soo
:
667 if (!keyword_included
|| keyword
) {
669 ecommunity_free(&ecom
);
674 if (token
== ecommunity_token_rt
||
675 token
== ecommunity_token_rt6
) {
676 type
= ECOMMUNITY_ROUTE_TARGET
;
678 if (token
== ecommunity_token_soo
) {
679 type
= ECOMMUNITY_SITE_ORIGIN
;
682 case ecommunity_token_val
:
683 if (keyword_included
) {
685 ecommunity_free(&ecom
);
691 ecom
= ecommunity_new();
693 ecommunity_add_val_internal(ecom
, (void *)&eval
,
697 case ecommunity_token_val6
:
698 if (keyword_included
) {
700 ecommunity_free(&ecom
);
706 ecom
= ecommunity_new();
707 ecom
->unit_size
= IPV6_ECOMMUNITY_SIZE
;
709 ecommunity_add_val_internal(ecom
, (void *)&eval
, false, false,
712 case ecommunity_token_unknown
:
715 ecommunity_free(&ecom
);
722 /* Convert string to extended community attribute.
724 * When type is already known, please specify both str and type. str
725 * should not include keyword such as "rt" and "soo". Type is
726 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
727 * keyword_included should be zero.
729 * For example route-map's "set extcommunity" command case:
731 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
732 * type = ECOMMUNITY_ROUTE_TARGET
733 * keyword_included = 0
735 * "soo 100:1" -> str = "100:1"
736 * type = ECOMMUNITY_SITE_ORIGIN
737 * keyword_included = 0
739 * When string includes keyword for each extended community value.
740 * Please specify keyword_included as non-zero value.
742 * For example standard extcommunity-list case:
744 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
746 * keyword_include = 1
748 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
749 int keyword_included
)
751 return ecommunity_str2com_internal(str
, type
,
752 keyword_included
, false);
755 struct ecommunity
*ecommunity_str2com_ipv6(const char *str
, int type
,
756 int keyword_included
)
758 return ecommunity_str2com_internal(str
, type
,
759 keyword_included
, true);
762 static int ecommunity_rt_soo_str_internal(char *buf
, size_t bufsz
,
763 const uint8_t *pnt
, int type
,
764 int sub_type
, int format
,
765 unsigned short ecom_size
)
769 char buf_local
[INET6_ADDRSTRLEN
];
771 /* For parse Extended Community attribute tupple. */
772 struct ecommunity_as eas
;
773 struct ecommunity_ip eip
;
774 struct ecommunity_ip6 eip6
;
776 /* Determine prefix for string, if any. */
778 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
779 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
781 case ECOMMUNITY_FORMAT_DISPLAY
:
782 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
784 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
792 /* Put string into buffer. */
793 if (type
== ECOMMUNITY_ENCODE_AS4
) {
794 pnt
= ptr_get_be32(pnt
, &eas
.as
);
795 eas
.val
= (*pnt
++ << 8);
798 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
799 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
800 if (ecom_size
== ECOMMUNITY_SIZE
) {
801 eas
.as
= (*pnt
++ << 8);
803 pnt
= ptr_get_be32(pnt
, &eas
.val
);
805 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
,
808 /* this is an IPv6 ext community
809 * first 16 bytes stands for IPv6 addres
811 memcpy(&eip6
.ip
, pnt
, 16);
813 eip6
.val
= (*pnt
++ << 8);
814 eip6
.val
|= (*pnt
++);
816 inet_ntop(AF_INET6
, &eip6
.ip
, buf_local
,
818 len
= snprintf(buf
, bufsz
, "%s%s:%u", prefix
,
819 buf_local
, eip6
.val
);
821 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
822 memcpy(&eip
.ip
, pnt
, 4);
824 eip
.val
= (*pnt
++ << 8);
827 len
= snprintfrr(buf
, bufsz
, "%s%pI4:%u", prefix
, &eip
.ip
,
837 static int ecommunity_rt_soo_str(char *buf
, size_t bufsz
, const uint8_t *pnt
,
838 int type
, int sub_type
, int format
)
840 return ecommunity_rt_soo_str_internal(buf
, bufsz
, pnt
, type
,
845 static int ecommunity_lb_str(char *buf
, size_t bufsz
, const uint8_t *pnt
)
850 char bps_buf
[20] = {0};
852 #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
853 #define ONE_MBPS_BYTES (1000 * 1000 / 8)
854 #define ONE_KBPS_BYTES (1000 / 8)
858 (void)ptr_get_be32(pnt
, &bw
);
859 if (bw
>= ONE_GBPS_BYTES
)
860 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Gbps",
861 (float)(bw
/ ONE_GBPS_BYTES
));
862 else if (bw
>= ONE_MBPS_BYTES
)
863 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Mbps",
864 (float)(bw
/ ONE_MBPS_BYTES
));
865 else if (bw
>= ONE_KBPS_BYTES
)
866 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Kbps",
867 (float)(bw
/ ONE_KBPS_BYTES
));
869 snprintf(bps_buf
, sizeof(bps_buf
), "%u bps", bw
* 8);
871 len
= snprintf(buf
, bufsz
, "LB:%u:%u (%s)", as
, bw
, bps_buf
);
875 /* Convert extended community attribute to string.
877 Due to historical reason of industry standard implementation, there
878 are three types of format.
880 route-map set extcommunity format
881 "rt 100:1 100:2soo 100:3"
884 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
885 "RT:100:1 RT:100:2 SoO:100:3"
887 For each formath please use below definition for format:
889 ECOMMUNITY_FORMAT_ROUTE_MAP
890 ECOMMUNITY_FORMAT_COMMUNITY_LIST
891 ECOMMUNITY_FORMAT_DISPLAY
893 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
896 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
901 uint8_t sub_type
= 0;
905 if (!ecom
|| ecom
->size
== 0)
906 return XCALLOC(MTYPE_ECOMMUNITY_STR
, 1);
908 /* ecom strlen + space + null term */
909 str_size
= (ecom
->size
* (ECOMMUNITY_STRLEN
+ 1)) + 1;
910 str_buf
= XCALLOC(MTYPE_ECOMMUNITY_STR
, str_size
);
914 for (i
= 0; i
< ecom
->size
; i
++) {
916 memset(encbuf
, 0x00, sizeof(encbuf
));
918 /* Space between each value. */
920 strlcat(str_buf
, " ", str_size
);
922 /* Retrieve value field */
923 pnt
= ecom
->val
+ (i
* 8);
925 /* High-order octet is the type */
928 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
929 || type
== ECOMMUNITY_ENCODE_AS4
) {
930 /* Low-order octet of type. */
932 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
933 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
) {
935 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4
&&
936 type
== ECOMMUNITY_ENCODE_IP
) {
937 struct in_addr
*ipv4
=
938 (struct in_addr
*)pnt
;
939 char ipv4str
[INET_ADDRSTRLEN
];
941 inet_ntop(AF_INET
, ipv4
,
944 snprintf(encbuf
, sizeof(encbuf
),
945 "NH:%s:%d", ipv4str
, pnt
[5]);
946 } else if (sub_type
==
947 ECOMMUNITY_LINK_BANDWIDTH
&&
948 type
== ECOMMUNITY_ENCODE_AS
) {
949 ecommunity_lb_str(encbuf
,
950 sizeof(encbuf
), pnt
);
954 ecommunity_rt_soo_str(encbuf
, sizeof(encbuf
),
958 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
959 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
961 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
963 memcpy(&tunneltype
, pnt
+ 5, 2);
964 tunneltype
= ntohs(tunneltype
);
966 snprintf(encbuf
, sizeof(encbuf
), "ET:%d",
968 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
969 strlcpy(encbuf
, "Default Gateway",
974 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
975 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
977 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
980 memcpy(&rmac
, pnt
, ETH_ALEN
);
982 snprintf(encbuf
, sizeof(encbuf
),
983 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
984 (uint8_t)rmac
.octet
[0],
985 (uint8_t)rmac
.octet
[1],
986 (uint8_t)rmac
.octet
[2],
987 (uint8_t)rmac
.octet
[3],
988 (uint8_t)rmac
.octet
[4],
989 (uint8_t)rmac
.octet
[5]);
991 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
993 uint8_t flags
= *++pnt
;
995 memcpy(&seqnum
, pnt
+ 2, 4);
996 seqnum
= ntohl(seqnum
);
998 snprintf(encbuf
, sizeof(encbuf
), "MM:%u",
1003 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
))
1004 strlcat(encbuf
, ", sticky MAC",
1006 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ND
) {
1007 uint8_t flags
= *++pnt
;
1011 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG
))
1012 strlcpy(encbuf
, "ND:Router Flag",
1016 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG
))
1017 strlcpy(encbuf
, "ND:Proxy",
1020 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT
) {
1024 memcpy(&mac
, pnt
, ETH_ALEN
);
1027 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1028 (uint8_t)mac
.octet
[0],
1029 (uint8_t)mac
.octet
[1],
1030 (uint8_t)mac
.octet
[2],
1031 (uint8_t)mac
.octet
[3],
1032 (uint8_t)mac
.octet
[4],
1033 (uint8_t)mac
.octet
[5]);
1035 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL
) {
1036 uint8_t flags
= *++pnt
;
1039 sizeof(encbuf
), "ESI-label-Rt:%s",
1041 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG
) ?
1044 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION
) {
1050 memcpy(&bmap
, pnt
+ 2, 2);
1052 memcpy(&pref
, pnt
+ 5, 2);
1057 encbuf
, sizeof(encbuf
),
1058 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1061 snprintf(encbuf
, sizeof(encbuf
),
1062 "DF: (alg: %u, pref: %u)", alg
,
1066 } else if (type
== ECOMMUNITY_ENCODE_REDIRECT_IP_NH
) {
1068 if (sub_type
== ECOMMUNITY_REDIRECT_IP_NH
) {
1069 snprintf(encbuf
, sizeof(encbuf
),
1070 "FS:redirect IP 0x%x", *(pnt
+ 5));
1073 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
||
1074 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_2
||
1075 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_3
) {
1078 if (sub_type
== ECOMMUNITY_ROUTE_TARGET
) {
1079 char buf
[ECOMMUNITY_STRLEN
];
1081 memset(buf
, 0, sizeof(buf
));
1082 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1083 (const uint8_t *)pnt
,
1085 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1086 ECOMMUNITY_ROUTE_TARGET
,
1089 snprintf(encbuf
, sizeof(encbuf
), "%s", buf
);
1090 } else if (sub_type
==
1091 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
1094 memset(buf
, 0, sizeof(buf
));
1095 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1096 (const uint8_t *)pnt
,
1098 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1099 ECOMMUNITY_ROUTE_TARGET
,
1100 ECOMMUNITY_FORMAT_DISPLAY
,
1102 snprintf(encbuf
, sizeof(encbuf
),
1103 "FS:redirect VRF %s", buf
);
1104 } else if (sub_type
== ECOMMUNITY_REDIRECT_VRF
) {
1107 memset(buf
, 0, sizeof(buf
));
1108 ecommunity_rt_soo_str(buf
, sizeof(buf
),
1109 (const uint8_t *)pnt
,
1111 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1112 ECOMMUNITY_ROUTE_TARGET
,
1113 ECOMMUNITY_FORMAT_DISPLAY
);
1114 snprintf(encbuf
, sizeof(encbuf
),
1115 "FS:redirect VRF %s", buf
);
1116 snprintf(encbuf
, sizeof(encbuf
),
1117 "FS:redirect VRF %s", buf
);
1118 } else if (type
!= ECOMMUNITY_ENCODE_TRANS_EXP
)
1120 else if (sub_type
== ECOMMUNITY_TRAFFIC_ACTION
) {
1124 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
)
1125 strlcpy(action
, "terminate (apply)",
1128 strlcpy(action
, "eval stops",
1132 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1133 strlcat(action
, ", sample",
1137 snprintf(encbuf
, sizeof(encbuf
), "FS:action %s",
1139 } else if (sub_type
== ECOMMUNITY_TRAFFIC_RATE
) {
1140 union traffic_rate data
;
1142 data
.rate_byte
[3] = *(pnt
+2);
1143 data
.rate_byte
[2] = *(pnt
+3);
1144 data
.rate_byte
[1] = *(pnt
+4);
1145 data
.rate_byte
[0] = *(pnt
+5);
1146 snprintf(encbuf
, sizeof(encbuf
), "FS:rate %f",
1148 } else if (sub_type
== ECOMMUNITY_TRAFFIC_MARKING
) {
1149 snprintf(encbuf
, sizeof(encbuf
),
1150 "FS:marking %u", *(pnt
+ 5));
1153 } else if (type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) {
1155 if (sub_type
== ECOMMUNITY_LINK_BANDWIDTH
)
1156 ecommunity_lb_str(encbuf
, sizeof(encbuf
), pnt
);
1165 snprintf(encbuf
, sizeof(encbuf
), "UNK:%d, %d", type
,
1168 int r
= strlcat(str_buf
, encbuf
, str_size
);
1169 assert(r
< str_size
);
1175 bool ecommunity_match(const struct ecommunity
*ecom1
,
1176 const struct ecommunity
*ecom2
)
1181 if (ecom1
== NULL
&& ecom2
== NULL
)
1184 if (ecom1
== NULL
|| ecom2
== NULL
)
1187 if (ecom1
->size
< ecom2
->size
)
1190 /* Every community on com2 needs to be on com1 for this to match */
1191 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
1192 if (memcmp(ecom1
->val
+ i
* ecom1
->unit_size
,
1193 ecom2
->val
+ j
* ecom2
->unit_size
,
1200 if (j
== ecom2
->size
)
1206 /* return first occurence of type */
1207 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
1208 uint8_t type
, uint8_t subtype
)
1213 /* If the value already exists in the structure return 0. */
1215 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1219 if (p
[0] == type
&& p
[1] == subtype
)
1220 return (struct ecommunity_val
*)p
;
1225 /* remove ext. community matching type and subtype
1226 * return 1 on success ( removed ), 0 otherwise (not present)
1228 bool ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
1231 uint8_t *p
, *q
, *new;
1232 uint32_t c
, found
= 0;
1233 /* When this is fist value, just add it. */
1234 if (ecom
== NULL
|| ecom
->val
== NULL
)
1237 /* Check if any existing ext community matches. */
1238 /* Certain extended communities like the Route Target can be present
1239 * multiple times, handle that.
1242 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1243 if (p
[0] == type
&& p
[1] == subtype
)
1246 /* If no matching ext community exists, return. */
1250 /* Handle the case where everything needs to be stripped. */
1251 if (found
== ecom
->size
) {
1252 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1257 /* Strip matching ext community(ies). */
1258 new = XMALLOC(MTYPE_ECOMMUNITY_VAL
,
1259 (ecom
->size
- found
) * ecom
->unit_size
);
1261 for (c
= 0, p
= ecom
->val
; c
< ecom
->size
; c
++, p
+= ecom
->unit_size
) {
1262 if (!(p
[0] == type
&& p
[1] == subtype
)) {
1263 memcpy(q
, p
, ecom
->unit_size
);
1264 q
+= ecom
->unit_size
;
1267 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1269 ecom
->size
-= found
;
1274 * Remove specified extended community value from extended community.
1275 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1277 bool ecommunity_del_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
1280 uint32_t c
, found
= 0;
1282 /* Make sure specified value exists. */
1283 if (ecom
== NULL
|| ecom
->val
== NULL
)
1286 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1287 if (!memcmp(p
, eval
->val
, ecom
->unit_size
)) {
1295 /* Delete the selected value */
1298 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ecom
->unit_size
);
1300 memcpy(p
, ecom
->val
, c
* ecom
->unit_size
);
1301 if ((ecom
->size
- c
) != 0)
1302 memcpy(p
+ (c
)*ecom
->unit_size
,
1303 ecom
->val
+ (c
+ 1) * ecom
->unit_size
,
1304 (ecom
->size
- c
) * ecom
->unit_size
);
1305 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1313 int ecommunity_fill_pbr_action(struct ecommunity_val
*ecom_eval
,
1314 struct bgp_pbr_entry_action
*api
,
1317 if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_RATE
) {
1318 api
->action
= ACTION_TRAFFICRATE
;
1319 api
->u
.r
.rate_info
[3] = ecom_eval
->val
[4];
1320 api
->u
.r
.rate_info
[2] = ecom_eval
->val
[5];
1321 api
->u
.r
.rate_info
[1] = ecom_eval
->val
[6];
1322 api
->u
.r
.rate_info
[0] = ecom_eval
->val
[7];
1323 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_ACTION
) {
1324 api
->action
= ACTION_TRAFFIC_ACTION
;
1325 /* else distribute code is set by default */
1326 if (ecom_eval
->val
[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
))
1327 api
->u
.za
.filter
|= TRAFFIC_ACTION_TERMINATE
;
1329 api
->u
.za
.filter
|= TRAFFIC_ACTION_DISTRIBUTE
;
1330 if (ecom_eval
->val
[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1331 api
->u
.za
.filter
|= TRAFFIC_ACTION_SAMPLE
;
1333 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_MARKING
) {
1334 api
->action
= ACTION_MARKING
;
1335 api
->u
.marking_dscp
= ecom_eval
->val
[7];
1336 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_VRF
) {
1337 /* must use external function */
1339 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_IP_NH
&&
1341 /* see draft-ietf-idr-flowspec-redirect-ip-02
1342 * Q1: how come a ext. community can host ipv6 address
1343 * Q2 : from cisco documentation:
1344 * Announces the reachability of one or more flowspec NLRI.
1345 * When a BGP speaker receives an UPDATE message with the
1346 * redirect-to-IP extended community, it is expected to
1347 * create a traffic filtering rule for every flow-spec
1348 * NLRI in the message that has this path as its best
1349 * path. The filter entry matches the IP packets
1350 * described in the NLRI field and redirects them or
1351 * copies them towards the IPv4 or IPv6 address specified
1352 * in the 'Network Address of Next- Hop'
1353 * field of the associated MP_REACH_NLRI.
1355 struct ecommunity_ip
*ip_ecom
= (struct ecommunity_ip
*)
1358 api
->u
.zr
.redirect_ip_v4
= ip_ecom
->ip
;
1364 static struct ecommunity
*bgp_aggr_ecommunity_lookup(
1365 struct bgp_aggregate
*aggregate
,
1366 struct ecommunity
*ecommunity
)
1368 return hash_lookup(aggregate
->ecommunity_hash
, ecommunity
);
1371 static void *bgp_aggr_ecommunty_hash_alloc(void *p
)
1373 struct ecommunity
*ref
= (struct ecommunity
*)p
;
1374 struct ecommunity
*ecommunity
= NULL
;
1376 ecommunity
= ecommunity_dup(ref
);
1380 static void bgp_aggr_ecommunity_prepare(struct hash_bucket
*hb
, void *arg
)
1382 struct ecommunity
*hb_ecommunity
= hb
->data
;
1383 struct ecommunity
**aggr_ecommunity
= arg
;
1385 if (*aggr_ecommunity
)
1386 *aggr_ecommunity
= ecommunity_merge(*aggr_ecommunity
,
1389 *aggr_ecommunity
= ecommunity_dup(hb_ecommunity
);
1392 void bgp_aggr_ecommunity_remove(void *arg
)
1394 struct ecommunity
*ecommunity
= arg
;
1396 ecommunity_free(&ecommunity
);
1399 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate
*aggregate
,
1400 struct ecommunity
*ecommunity
)
1402 bgp_compute_aggregate_ecommunity_hash(aggregate
, ecommunity
);
1403 bgp_compute_aggregate_ecommunity_val(aggregate
);
1407 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate
*aggregate
,
1408 struct ecommunity
*ecommunity
)
1410 struct ecommunity
*aggr_ecommunity
= NULL
;
1412 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1415 /* Create hash if not already created.
1417 if (aggregate
->ecommunity_hash
== NULL
)
1418 aggregate
->ecommunity_hash
= hash_create(
1419 ecommunity_hash_make
, ecommunity_cmp
,
1420 "BGP Aggregator ecommunity hash");
1422 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1423 if (aggr_ecommunity
== NULL
) {
1424 /* Insert ecommunity into hash.
1426 aggr_ecommunity
= hash_get(aggregate
->ecommunity_hash
,
1428 bgp_aggr_ecommunty_hash_alloc
);
1431 /* Increment reference counter.
1433 aggr_ecommunity
->refcnt
++;
1436 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate
*aggregate
)
1438 struct ecommunity
*ecommerge
= NULL
;
1440 if (aggregate
== NULL
)
1443 /* Re-compute aggregate's ecommunity.
1445 if (aggregate
->ecommunity
)
1446 ecommunity_free(&aggregate
->ecommunity
);
1447 if (aggregate
->ecommunity_hash
1448 && aggregate
->ecommunity_hash
->count
) {
1449 hash_iterate(aggregate
->ecommunity_hash
,
1450 bgp_aggr_ecommunity_prepare
,
1451 &aggregate
->ecommunity
);
1452 ecommerge
= aggregate
->ecommunity
;
1453 aggregate
->ecommunity
= ecommunity_uniq_sort(ecommerge
);
1455 ecommunity_free(&ecommerge
);
1459 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
1460 struct ecommunity
*ecommunity
)
1462 struct ecommunity
*aggr_ecommunity
= NULL
;
1463 struct ecommunity
*ret_ecomm
= NULL
;
1466 || (!aggregate
->ecommunity_hash
)
1470 /* Look-up the ecommunity in the hash.
1472 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1473 if (aggr_ecommunity
) {
1474 aggr_ecommunity
->refcnt
--;
1476 if (aggr_ecommunity
->refcnt
== 0) {
1477 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1479 ecommunity_free(&ret_ecomm
);
1480 bgp_compute_aggregate_ecommunity_val(aggregate
);
1485 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1486 struct ecommunity
*ecommunity
)
1489 struct ecommunity
*aggr_ecommunity
= NULL
;
1490 struct ecommunity
*ret_ecomm
= NULL
;
1493 || (!aggregate
->ecommunity_hash
)
1497 /* Look-up the ecommunity in the hash.
1499 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1500 if (aggr_ecommunity
) {
1501 aggr_ecommunity
->refcnt
--;
1503 if (aggr_ecommunity
->refcnt
== 0) {
1504 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1506 ecommunity_free(&ret_ecomm
);
1512 * return the BGP link bandwidth extended community, if present;
1513 * the actual bandwidth is returned via param
1515 const uint8_t *ecommunity_linkbw_present(struct ecommunity
*ecom
, uint32_t *bw
)
1517 const uint8_t *eval
;
1523 if (!ecom
|| !ecom
->size
)
1526 for (i
= 0; i
< ecom
->size
; i
++) {
1528 uint8_t type
, sub_type
;
1531 eval
= pnt
= (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
1535 if ((type
== ECOMMUNITY_ENCODE_AS
||
1536 type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) &&
1537 sub_type
== ECOMMUNITY_LINK_BANDWIDTH
) {
1538 pnt
+= 2; /* bandwidth is encoded as AS:val */
1539 pnt
= ptr_get_be32(pnt
, &bwval
);
1540 (void)pnt
; /* consume value */
1551 struct ecommunity
*ecommunity_replace_linkbw(as_t as
,
1552 struct ecommunity
*ecom
,
1555 struct ecommunity
*new;
1556 struct ecommunity_val lb_eval
;
1557 const uint8_t *eval
;
1561 /* Nothing to replace if link-bandwidth doesn't exist or
1562 * is non-transitive - just return existing extcommunity.
1565 if (!ecom
|| !ecom
->size
)
1568 eval
= ecommunity_linkbw_present(ecom
, &cur_bw
);
1573 if (type
& ECOMMUNITY_FLAG_NON_TRANSITIVE
)
1576 /* Transitive link-bandwidth exists, replace with the passed
1577 * (cumulative) bandwidth value. We need to create a new
1578 * extcommunity for this - refer to AS-Path replace function
1581 if (cum_bw
> 0xFFFFFFFF)
1582 cum_bw
= 0xFFFFFFFF;
1583 encode_lb_extcomm(as
> BGP_AS_MAX
? BGP_AS_TRANS
: as
, cum_bw
,
1585 new = ecommunity_dup(ecom
);
1586 ecommunity_add_val(new, &lb_eval
, true, true);