1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* BGP Extended Communities Attribute
3 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
17 #include "lib/printfrr.h"
19 #include "bgpd/bgpd.h"
20 #include "bgpd/bgp_ecommunity.h"
21 #include "bgpd/bgp_lcommunity.h"
22 #include "bgpd/bgp_aspath.h"
23 #include "bgpd/bgp_flowspec_private.h"
24 #include "bgpd/bgp_pbr.h"
26 /* struct used to dump the rate contained in FS set traffic-rate EC */
32 /* Hash of community attribute. */
33 static struct hash
*ecomhash
;
35 /* Allocate a new ecommunities. */
36 struct ecommunity
*ecommunity_new(void)
38 struct ecommunity
*ecom
;
40 ecom
= (struct ecommunity
*)XCALLOC(MTYPE_ECOMMUNITY
,
41 sizeof(struct ecommunity
));
42 ecom
->unit_size
= ECOMMUNITY_SIZE
;
46 void ecommunity_strfree(char **s
)
48 XFREE(MTYPE_ECOMMUNITY_STR
, *s
);
51 /* Free ecommunities. */
52 void ecommunity_free(struct ecommunity
**ecom
)
57 XFREE(MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
58 XFREE(MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
59 XFREE(MTYPE_ECOMMUNITY
, *ecom
);
62 static void ecommunity_hash_free(struct ecommunity
*ecom
)
64 ecommunity_free(&ecom
);
68 /* Add a new Extended Communities value to Extended Communities
69 Attribute structure. When the value is already exists in the
70 structure, we don't add the value. Newly added value is sorted by
71 numerical order. When the value is added to the structure return 1
73 The additional parameters 'unique' and 'overwrite' ensure a particular
74 extended community (based on type and sub-type) is present only
75 once and whether the new value should replace what is existing or
78 static bool ecommunity_add_val_internal(struct ecommunity
*ecom
,
80 bool unique
, bool overwrite
,
84 const struct ecommunity_val
*eval4
= (struct ecommunity_val
*)eval
;
85 const struct ecommunity_val_ipv6
*eval6
=
86 (struct ecommunity_val_ipv6
*)eval
;
88 /* When this is fist value, just add it. */
89 if (ecom
->val
== NULL
) {
91 ecom
->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
92 ecom_length_size(ecom
, ecom_size
));
93 memcpy(ecom
->val
, eval
, ecom_size
);
97 /* If the value already exists in the structure return 0. */
98 /* check also if the extended community itself exists. */
101 ins_idx
= UINT32_MAX
;
102 for (uint8_t *p
= ecom
->val
; c
< ecom
->size
;
103 p
+= ecom_size
, c
++) {
105 if (ecom_size
== ECOMMUNITY_SIZE
) {
106 if (p
[0] == eval4
->val
[0] &&
107 p
[1] == eval4
->val
[1]) {
109 memcpy(p
, eval4
->val
,
116 if (p
[0] == eval6
->val
[0] &&
117 p
[1] == eval6
->val
[1]) {
119 memcpy(p
, eval6
->val
,
127 int ret
= memcmp(p
, eval
, ecom_size
);
133 if (ins_idx
== UINT32_MAX
)
138 if (ins_idx
== UINT32_MAX
)
141 /* Add the value to the structure with numerical sorting. */
143 ecom
->val
= XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->val
,
144 ecom_length_size(ecom
, ecom_size
));
146 memmove(ecom
->val
+ ((ins_idx
+ 1) * ecom_size
),
147 ecom
->val
+ (ins_idx
* ecom_size
),
148 (ecom
->size
- 1 - ins_idx
) * ecom_size
);
149 memcpy(ecom
->val
+ (ins_idx
* ecom_size
),
155 /* Add a new Extended Communities value to Extended Communities
156 * Attribute structure. When the value is already exists in the
157 * structure, we don't add the value. Newly added value is sorted by
158 * numerical order. When the value is added to the structure return 1
161 bool ecommunity_add_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
,
162 bool unique
, bool overwrite
)
164 return ecommunity_add_val_internal(ecom
, (const void *)eval
, unique
,
165 overwrite
, ECOMMUNITY_SIZE
);
168 bool ecommunity_add_val_ipv6(struct ecommunity
*ecom
,
169 struct ecommunity_val_ipv6
*eval
,
170 bool unique
, bool overwrite
)
172 return ecommunity_add_val_internal(ecom
, (const void *)eval
, unique
,
173 overwrite
, IPV6_ECOMMUNITY_SIZE
);
176 static struct ecommunity
*
177 ecommunity_uniq_sort_internal(struct ecommunity
*ecom
,
178 unsigned short ecom_size
)
181 struct ecommunity
*new;
187 new = ecommunity_new();
188 new->unit_size
= ecom_size
;
189 new->disable_ieee_floating
= ecom
->disable_ieee_floating
;
191 for (i
= 0; i
< ecom
->size
; i
++) {
192 eval
= (void *)(ecom
->val
+ (i
* ecom_size
));
193 ecommunity_add_val_internal(new, eval
, false, false, ecom_size
);
198 /* This function takes pointer to Extended Communites structure then
199 * create a new Extended Communities structure by uniq and sort each
200 * Extended Communities value.
202 struct ecommunity
*ecommunity_uniq_sort(struct ecommunity
*ecom
)
204 return ecommunity_uniq_sort_internal(ecom
, ECOMMUNITY_SIZE
);
207 /* Parse Extended Communites Attribute in BGP packet. */
208 static struct ecommunity
*ecommunity_parse_internal(uint8_t *pnt
,
209 unsigned short length
,
210 unsigned short size_ecom
,
211 bool disable_ieee_floating
)
213 struct ecommunity tmp
;
214 struct ecommunity
*new;
217 if (length
% size_ecom
)
220 /* Prepare tmporary structure for making a new Extended Communities
222 tmp
.size
= length
/ size_ecom
;
224 tmp
.disable_ieee_floating
= disable_ieee_floating
;
226 /* Create a new Extended Communities Attribute by uniq and sort each
227 Extended Communities value */
228 new = ecommunity_uniq_sort_internal(&tmp
, size_ecom
);
230 return ecommunity_intern(new);
233 struct ecommunity
*ecommunity_parse(uint8_t *pnt
, unsigned short length
,
234 bool disable_ieee_floating
)
236 return ecommunity_parse_internal(pnt
, length
, ECOMMUNITY_SIZE
,
237 disable_ieee_floating
);
240 struct ecommunity
*ecommunity_parse_ipv6(uint8_t *pnt
, unsigned short length
,
241 bool disable_ieee_floating
)
243 return ecommunity_parse_internal(pnt
, length
, IPV6_ECOMMUNITY_SIZE
,
244 disable_ieee_floating
);
247 /* Duplicate the Extended Communities Attribute structure. */
248 struct ecommunity
*ecommunity_dup(struct ecommunity
*ecom
)
250 struct ecommunity
*new;
252 new = XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
253 new->size
= ecom
->size
;
254 new->unit_size
= ecom
->unit_size
;
256 new->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
257 ecom
->size
* ecom
->unit_size
);
258 memcpy(new->val
, ecom
->val
,
259 (size_t)ecom
->size
* (size_t)ecom
->unit_size
);
265 /* Return string representation of ecommunities attribute. */
266 char *ecommunity_str(struct ecommunity
*ecom
)
270 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
274 /* Merge two Extended Communities Attribute structure. */
275 struct ecommunity
*ecommunity_merge(struct ecommunity
*ecom1
,
276 struct ecommunity
*ecom2
)
278 ecom1
->val
= XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
279 (size_t)(ecom1
->size
+ ecom2
->size
)
280 * (size_t)ecom1
->unit_size
);
282 memcpy(ecom1
->val
+ (ecom1
->size
* ecom1
->unit_size
), ecom2
->val
,
283 (size_t)ecom2
->size
* (size_t)ecom1
->unit_size
);
284 ecom1
->size
+= ecom2
->size
;
289 /* Intern Extended Communities Attribute. */
290 struct ecommunity
*ecommunity_intern(struct ecommunity
*ecom
)
292 struct ecommunity
*find
;
294 assert(ecom
->refcnt
== 0);
295 find
= (struct ecommunity
*)hash_get(ecomhash
, ecom
, hash_alloc_intern
);
297 ecommunity_free(&ecom
);
303 ecommunity_ecom2str(find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
308 /* Unintern Extended Communities Attribute. */
309 void ecommunity_unintern(struct ecommunity
**ecom
)
311 struct ecommunity
*ret
;
319 /* Pull off from hash. */
320 if ((*ecom
)->refcnt
== 0) {
321 /* Extended community must be in the hash. */
322 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
325 ecommunity_free(ecom
);
329 /* Utinity function to make hash key. */
330 unsigned int ecommunity_hash_make(const void *arg
)
332 const struct ecommunity
*ecom
= arg
;
333 int size
= ecom
->size
* ecom
->unit_size
;
335 return jhash(ecom
->val
, size
, 0x564321ab);
338 /* Compare two Extended Communities Attribute structure. */
339 bool ecommunity_cmp(const void *arg1
, const void *arg2
)
341 const struct ecommunity
*ecom1
= arg1
;
342 const struct ecommunity
*ecom2
= arg2
;
344 if (ecom1
== NULL
&& ecom2
== NULL
)
347 if (ecom1
== NULL
|| ecom2
== NULL
)
350 if (ecom1
->unit_size
!= ecom2
->unit_size
)
353 return (ecom1
->size
== ecom2
->size
354 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
*
355 ecom1
->unit_size
) == 0);
358 /* Initialize Extended Comminities related hash. */
359 void ecommunity_init(void)
361 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
362 "BGP ecommunity hash");
365 void ecommunity_finish(void)
367 hash_clean_and_free(&ecomhash
, (void (*)(void *))ecommunity_hash_free
);
370 /* Extended Communities token enum. */
371 enum ecommunity_token
{
372 ecommunity_token_unknown
= 0,
375 ecommunity_token_soo
,
376 ecommunity_token_val
,
377 ecommunity_token_rt6
,
378 ecommunity_token_val6
,
381 static const char *ecommunity_origin_validation_state2str(
382 enum ecommunity_origin_validation_states state
)
385 case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID
:
387 case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND
:
389 case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID
:
391 case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED
:
398 static void ecommunity_origin_validation_state_str(char *buf
, size_t bufsz
,
401 /* Origin Validation State is encoded in the last octet
404 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
405 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
406 * | 0x43 | 0x00 | Reserved |
407 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
408 * | Reserved |validationstate|
409 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411 uint8_t state
= *(ptr
+ ECOMMUNITY_SIZE
- 3);
413 snprintf(buf
, bufsz
, "OVS:%s",
414 ecommunity_origin_validation_state2str(state
));
416 (void)ptr
; /* consume value */
419 bool ecommunity_node_target_match(struct ecommunity
*ecom
,
420 struct in_addr
*local_id
)
425 if (!ecom
|| !ecom
->size
)
428 for (i
= 0; i
< ecom
->size
; i
++) {
430 uint8_t type
, sub_type
;
432 pnt
= (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
436 if (type
== ECOMMUNITY_ENCODE_IP
&&
437 sub_type
== ECOMMUNITY_NODE_TARGET
) {
438 /* Node Target ID is encoded as A.B.C.D:0 */
439 if (IPV4_ADDR_SAME((struct in_addr
*)pnt
, local_id
))
448 static void ecommunity_node_target_str(char *buf
, size_t bufsz
, uint8_t *ptr
,
452 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
453 * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier |
454 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
455 * | Target BGP Identifier (cont.) | Reserved |
456 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
458 struct in_addr node_id
= {};
460 IPV4_ADDR_COPY(&node_id
, (struct in_addr
*)ptr
);
463 snprintfrr(buf
, bufsz
, "%s%pI4%s",
464 format
== ECOMMUNITY_FORMAT_COMMUNITY_LIST
? "nt " : "NT:",
466 format
== ECOMMUNITY_FORMAT_COMMUNITY_LIST
? ":0" : "");
468 (void)ptr
; /* consume value */
471 static int ecommunity_encode_internal(uint8_t type
, uint8_t sub_type
,
474 struct in6_addr
*ip6
,
478 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
479 struct ecommunity_val_ipv6
*eval6
=
480 (struct ecommunity_val_ipv6
*)eval_ptr
;
483 if (type
== ECOMMUNITY_ENCODE_AS
) {
486 } else if (type
== ECOMMUNITY_ENCODE_IP
487 || type
== ECOMMUNITY_ENCODE_AS4
) {
488 if (val
> UINT16_MAX
)
490 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
491 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
&&
492 (!ip6
|| val
> UINT16_MAX
)) {
496 /* Fill in the values. */
499 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
500 eval
->val
[1] = sub_type
;
501 if (type
== ECOMMUNITY_ENCODE_AS
) {
502 encode_route_target_as(as
, val
, eval
, trans
);
503 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
504 if (sub_type
== ECOMMUNITY_NODE_TARGET
)
505 encode_node_target(ip
, eval
, trans
);
507 encode_route_target_ip(ip
, val
, eval
, trans
);
508 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
&&
509 sub_type
== ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
510 memcpy(&eval6
->val
[2], ip6
, sizeof(struct in6_addr
));
511 eval6
->val
[18] = (val
>> 8) & 0xff;
512 eval6
->val
[19] = val
& 0xff;
514 encode_route_target_as4(as
, val
, eval
, trans
);
521 * Encode BGP extended community from passed values. Supports types
522 * defined in RFC 4360 and well-known sub-types.
524 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
525 struct in_addr ip
, uint32_t val
,
526 struct ecommunity_val
*eval
)
528 return ecommunity_encode_internal(type
, sub_type
, trans
, as
,
529 &ip
, NULL
, val
, (void *)eval
);
532 /* Get next Extended Communities token from the string. */
533 static const char *ecommunity_gettoken(const char *str
, void *eval_ptr
,
534 enum ecommunity_token
*token
, int type
)
547 char buf
[INET_ADDRSTRLEN
+ 1];
548 struct ecommunity_val
*eval
= (struct ecommunity_val
*)eval_ptr
;
551 /* Skip white space. */
552 while (isspace((unsigned char)*p
)) {
557 /* Check the end of the line. */
561 /* "rt", "nt", and "soo" keyword parse. */
562 if (!isdigit((unsigned char)*p
)) {
563 /* "rt" match check. */
564 if (tolower((unsigned char)*p
) == 'r') {
566 if (tolower((unsigned char)*p
) == 't') {
568 if (*p
!= '\0' && tolower((int)*p
) == '6')
569 *token
= ecommunity_token_rt6
;
571 *token
= ecommunity_token_rt
;
574 if (isspace((unsigned char)*p
) || *p
== '\0') {
575 *token
= ecommunity_token_rt
;
580 /* "nt" match check. */
581 if (tolower((unsigned char)*p
) == 'n') {
583 if (tolower((unsigned char)*p
) == 't') {
585 *token
= ecommunity_token_nt
;
588 if (isspace((unsigned char)*p
) || *p
== '\0') {
589 *token
= ecommunity_token_nt
;
594 /* "soo" match check. */
595 else if (tolower((unsigned char)*p
) == 's') {
597 if (tolower((unsigned char)*p
) == 'o') {
599 if (tolower((unsigned char)*p
) == 'o') {
601 *token
= ecommunity_token_soo
;
604 if (isspace((unsigned char)*p
) || *p
== '\0') {
605 *token
= ecommunity_token_soo
;
610 if (isspace((unsigned char)*p
) || *p
== '\0') {
611 *token
= ecommunity_token_soo
;
619 /* What a mess, there are several possibilities:
624 * d) <IPV6>:MN (only with rt6)
626 * A.B.C.D: Four Byte IP
628 * GHJK: Four-byte ASN
630 * OPQR: Four byte value
633 /* IPv6 case : look for last ':' */
634 if (*token
== ecommunity_token_rt6
||
635 *token
== ecommunity_token_val6
) {
638 limit
= endptr
= strrchr(p
, ':');
644 tmp_as
= strtoul(endptr
, &endptr
, 10);
645 /* 'unsigned long' is a uint64 on 64-bit
646 * systems, and uint32 on 32-bit systems. So for
647 * 64-bit we can just directly check the value
648 * against BGP_AS4_MAX/UINT32_MAX, and for
649 * 32-bit we can check for errno (set to ERANGE
652 if (*endptr
!= '\0' || tmp_as
== BGP_AS4_MAX
|| errno
)
656 memcpy(buf
, p
, (limit
- p
));
657 buf
[limit
- p
] = '\0';
658 ret
= inet_pton(AF_INET6
, buf
, &ip6
);
662 ecomm_type
= ECOMMUNITY_ENCODE_TRANS_EXP
;
663 if (ecommunity_encode_internal(ecomm_type
,
664 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
,
665 1, 0, NULL
, &ip6
, as
, eval_ptr
))
668 *token
= ecommunity_token_val6
;
669 while (isdigit((int)*p
) || *p
== ':' || *p
== '.') {
674 while (isdigit((unsigned char)*p
) || *p
== ':' || *p
== '.') {
682 if ((p
- str
) > INET_ADDRSTRLEN
)
684 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
685 memcpy(buf
, str
, p
- str
);
688 /* Parsing A.B.C.D in:
691 ret
= inet_aton(buf
, &ip
);
697 tmp_as
= strtoul(buf
, &endptr
, 10);
698 /* 'unsigned long' is a uint64 on 64-bit
699 * systems, and uint32 on 32-bit systems. So for
700 * 64-bit we can just directly check the value
701 * against BGP_AS4_MAX/UINT32_MAX, and for
702 * 32-bit we can check for errno (set to ERANGE
705 if (*endptr
!= '\0' || tmp_as
> BGP_AS4_MAX
||
710 } else if (*p
== '.') {
719 /* We're past the IP/ASN part */
728 /* Low digit part must be there. */
729 if (!digit
|| !separator
)
732 /* Encode result into extended community. */
734 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
735 else if (as
> BGP_AS_MAX
)
736 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
738 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
739 if (ecommunity_encode(ecomm_type
, type
, 1, as
, ip
, val
, eval
))
741 *token
= ecommunity_token_val
;
745 *token
= ecommunity_token_unknown
;
749 static struct ecommunity
*ecommunity_str2com_internal(const char *str
, int type
,
750 int keyword_included
,
751 bool is_ipv6_extcomm
)
753 struct ecommunity
*ecom
= NULL
;
754 enum ecommunity_token token
= ecommunity_token_unknown
;
755 struct ecommunity_val_ipv6 eval
;
759 token
= ecommunity_token_rt6
;
760 while ((str
= ecommunity_gettoken(str
, (void *)&eval
, &token
, type
))) {
762 case ecommunity_token_rt
:
763 case ecommunity_token_nt
:
764 case ecommunity_token_rt6
:
765 case ecommunity_token_soo
:
766 if (!keyword_included
|| keyword
) {
768 ecommunity_free(&ecom
);
773 if (token
== ecommunity_token_rt
||
774 token
== ecommunity_token_rt6
) {
775 type
= ECOMMUNITY_ROUTE_TARGET
;
777 if (token
== ecommunity_token_soo
) {
778 type
= ECOMMUNITY_SITE_ORIGIN
;
780 if (token
== ecommunity_token_nt
) {
781 type
= ECOMMUNITY_NODE_TARGET
;
784 case ecommunity_token_val
:
785 if (keyword_included
) {
787 ecommunity_free(&ecom
);
793 ecom
= ecommunity_new();
795 ecommunity_add_val_internal(ecom
, (void *)&eval
,
799 case ecommunity_token_val6
:
800 if (keyword_included
) {
802 ecommunity_free(&ecom
);
808 ecom
= ecommunity_new();
809 ecom
->unit_size
= IPV6_ECOMMUNITY_SIZE
;
811 ecommunity_add_val_internal(ecom
, (void *)&eval
, false, false,
814 case ecommunity_token_unknown
:
816 ecommunity_free(&ecom
);
823 /* Convert string to extended community attribute.
825 * When type is already known, please specify both str and type. str
826 * should not include keyword such as "rt" and "soo". Type is
827 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
828 * keyword_included should be zero.
830 * For example route-map's "set extcommunity" command case:
832 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
833 * type = ECOMMUNITY_ROUTE_TARGET
834 * keyword_included = 0
836 * "soo 100:1" -> str = "100:1"
837 * type = ECOMMUNITY_SITE_ORIGIN
838 * keyword_included = 0
840 * When string includes keyword for each extended community value.
841 * Please specify keyword_included as non-zero value.
843 * For example standard extcommunity-list case:
845 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
847 * keyword_include = 1
849 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
850 int keyword_included
)
852 return ecommunity_str2com_internal(str
, type
,
853 keyword_included
, false);
856 struct ecommunity
*ecommunity_str2com_ipv6(const char *str
, int type
,
857 int keyword_included
)
859 return ecommunity_str2com_internal(str
, type
,
860 keyword_included
, true);
863 static int ecommunity_rt_soo_str_internal(char *buf
, size_t bufsz
,
864 const uint8_t *pnt
, int type
,
865 int sub_type
, int format
,
866 unsigned short ecom_size
)
870 char buf_local
[INET6_ADDRSTRLEN
];
872 /* For parse Extended Community attribute tupple. */
873 struct ecommunity_as eas
;
874 struct ecommunity_ip eip
;
875 struct ecommunity_ip6 eip6
;
877 /* Determine prefix for string, if any. */
879 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
880 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
882 case ECOMMUNITY_FORMAT_DISPLAY
:
883 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
885 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
893 /* Put string into buffer. */
894 if (type
== ECOMMUNITY_ENCODE_AS4
) {
895 pnt
= ptr_get_be32(pnt
, &eas
.as
);
896 eas
.val
= (*pnt
++ << 8);
899 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
900 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
901 if (ecom_size
== ECOMMUNITY_SIZE
) {
902 eas
.as
= (*pnt
++ << 8);
904 pnt
= ptr_get_be32(pnt
, &eas
.val
);
906 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
,
909 /* this is an IPv6 ext community
910 * first 16 bytes stands for IPv6 addres
912 memcpy(&eip6
.ip
, pnt
, 16);
914 eip6
.val
= (*pnt
++ << 8);
915 eip6
.val
|= (*pnt
++);
917 inet_ntop(AF_INET6
, &eip6
.ip
, buf_local
,
919 len
= snprintf(buf
, bufsz
, "%s%s:%u", prefix
,
920 buf_local
, eip6
.val
);
922 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
923 memcpy(&eip
.ip
, pnt
, 4);
925 eip
.val
= (*pnt
++ << 8);
928 len
= snprintfrr(buf
, bufsz
, "%s%pI4:%u", prefix
, &eip
.ip
,
938 static int ecommunity_rt_soo_str(char *buf
, size_t bufsz
, const uint8_t *pnt
,
939 int type
, int sub_type
, int format
)
941 return ecommunity_rt_soo_str_internal(buf
, bufsz
, pnt
, type
,
946 /* Helper function to convert IEEE-754 Floating Point to uint32 */
947 static uint32_t ieee_float_uint32_to_uint32(uint32_t u
)
954 return (uint32_t)f
.r
;
957 static int ecommunity_lb_str(char *buf
, size_t bufsz
, const uint8_t *pnt
,
958 bool disable_ieee_floating
)
963 char bps_buf
[20] = {0};
965 #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
966 #define ONE_MBPS_BYTES (1000 * 1000 / 8)
967 #define ONE_KBPS_BYTES (1000 / 8)
971 (void)ptr_get_be32(pnt
, &bw_tmp
);
973 bw
= disable_ieee_floating
? bw_tmp
974 : ieee_float_uint32_to_uint32(bw_tmp
);
976 if (bw
>= ONE_GBPS_BYTES
)
977 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Gbps",
978 (float)(bw
/ ONE_GBPS_BYTES
));
979 else if (bw
>= ONE_MBPS_BYTES
)
980 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Mbps",
981 (float)(bw
/ ONE_MBPS_BYTES
));
982 else if (bw
>= ONE_KBPS_BYTES
)
983 snprintf(bps_buf
, sizeof(bps_buf
), "%.3f Kbps",
984 (float)(bw
/ ONE_KBPS_BYTES
));
986 snprintf(bps_buf
, sizeof(bps_buf
), "%u bps", bw
* 8);
988 len
= snprintf(buf
, bufsz
, "LB:%u:%u (%s)", as
, bw
, bps_buf
);
992 /* Convert extended community attribute to string.
994 Due to historical reason of industry standard implementation, there
995 are three types of format.
997 route-map set extcommunity format
998 "rt 100:1 100:2soo 100:3"
1001 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
1002 "RT:100:1 RT:100:2 SoO:100:3"
1004 For each formath please use below definition for format:
1006 ECOMMUNITY_FORMAT_ROUTE_MAP
1007 ECOMMUNITY_FORMAT_COMMUNITY_LIST
1008 ECOMMUNITY_FORMAT_DISPLAY
1010 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
1011 0 value displays all
1013 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
1018 uint8_t sub_type
= 0;
1022 if (!ecom
|| ecom
->size
== 0)
1023 return XCALLOC(MTYPE_ECOMMUNITY_STR
, 1);
1025 /* ecom strlen + space + null term */
1026 str_size
= (ecom
->size
* (ECOMMUNITY_STRLEN
+ 1)) + 1;
1027 str_buf
= XCALLOC(MTYPE_ECOMMUNITY_STR
, str_size
);
1031 for (i
= 0; i
< ecom
->size
; i
++) {
1033 memset(encbuf
, 0x00, sizeof(encbuf
));
1035 /* Space between each value. */
1037 strlcat(str_buf
, " ", str_size
);
1039 /* Retrieve value field */
1040 pnt
= ecom
->val
+ (i
* ecom
->unit_size
);
1042 /* High-order octet is the type */
1045 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
1046 || type
== ECOMMUNITY_ENCODE_AS4
) {
1047 /* Low-order octet of type. */
1049 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
1050 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
) {
1052 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4
&&
1053 type
== ECOMMUNITY_ENCODE_IP
) {
1054 struct in_addr
*ipv4
=
1055 (struct in_addr
*)pnt
;
1056 snprintfrr(encbuf
, sizeof(encbuf
),
1057 "NH:%pI4:%d", ipv4
, pnt
[5]);
1058 } else if (sub_type
==
1059 ECOMMUNITY_LINK_BANDWIDTH
&&
1060 type
== ECOMMUNITY_ENCODE_AS
) {
1062 encbuf
, sizeof(encbuf
), pnt
,
1063 ecom
->disable_ieee_floating
);
1064 } else if (sub_type
== ECOMMUNITY_NODE_TARGET
&&
1065 type
== ECOMMUNITY_ENCODE_IP
) {
1066 ecommunity_node_target_str(
1067 encbuf
, sizeof(encbuf
), pnt
,
1072 ecommunity_rt_soo_str(encbuf
, sizeof(encbuf
),
1073 pnt
, type
, sub_type
,
1076 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
1077 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
1079 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
1080 uint16_t tunneltype
;
1081 memcpy(&tunneltype
, pnt
+ 5, 2);
1082 tunneltype
= ntohs(tunneltype
);
1084 snprintf(encbuf
, sizeof(encbuf
), "ET:%d",
1086 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
1087 strlcpy(encbuf
, "Default Gateway",
1092 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
1093 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
1095 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
1096 struct ethaddr rmac
;
1098 memcpy(&rmac
, pnt
, ETH_ALEN
);
1100 snprintf(encbuf
, sizeof(encbuf
),
1101 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
1102 (uint8_t)rmac
.octet
[0],
1103 (uint8_t)rmac
.octet
[1],
1104 (uint8_t)rmac
.octet
[2],
1105 (uint8_t)rmac
.octet
[3],
1106 (uint8_t)rmac
.octet
[4],
1107 (uint8_t)rmac
.octet
[5]);
1109 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
1111 uint8_t flags
= *++pnt
;
1113 memcpy(&seqnum
, pnt
+ 2, 4);
1114 seqnum
= ntohl(seqnum
);
1116 snprintf(encbuf
, sizeof(encbuf
), "MM:%u",
1121 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
))
1122 strlcat(encbuf
, ", sticky MAC",
1124 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ND
) {
1125 uint8_t flags
= *++pnt
;
1129 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG
))
1130 strlcpy(encbuf
, "ND:Router Flag",
1134 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG
))
1135 strlcpy(encbuf
, "ND:Proxy",
1138 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT
) {
1142 memcpy(&mac
, pnt
, ETH_ALEN
);
1145 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1146 (uint8_t)mac
.octet
[0],
1147 (uint8_t)mac
.octet
[1],
1148 (uint8_t)mac
.octet
[2],
1149 (uint8_t)mac
.octet
[3],
1150 (uint8_t)mac
.octet
[4],
1151 (uint8_t)mac
.octet
[5]);
1153 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL
) {
1154 uint8_t flags
= *++pnt
;
1157 sizeof(encbuf
), "ESI-label-Rt:%s",
1159 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG
) ?
1162 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION
) {
1168 memcpy(&bmap
, pnt
+ 2, 2);
1170 memcpy(&pref
, pnt
+ 5, 2);
1175 encbuf
, sizeof(encbuf
),
1176 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1179 snprintf(encbuf
, sizeof(encbuf
),
1180 "DF: (alg: %u, pref: %u)", alg
,
1184 } else if (type
== ECOMMUNITY_ENCODE_REDIRECT_IP_NH
) {
1186 if (sub_type
== ECOMMUNITY_REDIRECT_IP_NH
) {
1187 snprintf(encbuf
, sizeof(encbuf
),
1188 "FS:redirect IP 0x%x", *(pnt
+ 5));
1191 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
||
1192 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_2
||
1193 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_3
) {
1196 if (sub_type
== ECOMMUNITY_ROUTE_TARGET
) {
1197 char buf
[ECOMMUNITY_STRLEN
];
1199 memset(buf
, 0, sizeof(buf
));
1200 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1201 (const uint8_t *)pnt
,
1203 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1204 ECOMMUNITY_ROUTE_TARGET
,
1207 snprintf(encbuf
, sizeof(encbuf
), "%s", buf
);
1208 } else if (sub_type
==
1209 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6
) {
1212 memset(buf
, 0, sizeof(buf
));
1213 ecommunity_rt_soo_str_internal(buf
, sizeof(buf
),
1214 (const uint8_t *)pnt
,
1216 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1217 ECOMMUNITY_ROUTE_TARGET
,
1218 ECOMMUNITY_FORMAT_DISPLAY
,
1220 snprintf(encbuf
, sizeof(encbuf
),
1221 "FS:redirect VRF %s", buf
);
1222 } else if (sub_type
== ECOMMUNITY_REDIRECT_VRF
) {
1225 memset(buf
, 0, sizeof(buf
));
1226 ecommunity_rt_soo_str(buf
, sizeof(buf
),
1227 (const uint8_t *)pnt
,
1229 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
1230 ECOMMUNITY_ROUTE_TARGET
,
1231 ECOMMUNITY_FORMAT_DISPLAY
);
1232 snprintf(encbuf
, sizeof(encbuf
),
1233 "FS:redirect VRF %s", buf
);
1234 snprintf(encbuf
, sizeof(encbuf
),
1235 "FS:redirect VRF %s", buf
);
1236 } else if (type
!= ECOMMUNITY_ENCODE_TRANS_EXP
)
1238 else if (sub_type
== ECOMMUNITY_TRAFFIC_ACTION
) {
1242 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
)
1243 strlcpy(action
, "terminate (apply)",
1246 strlcpy(action
, "eval stops",
1250 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1251 strlcat(action
, ", sample",
1255 snprintf(encbuf
, sizeof(encbuf
), "FS:action %s",
1257 } else if (sub_type
== ECOMMUNITY_TRAFFIC_RATE
) {
1258 union traffic_rate data
;
1260 data
.rate_byte
[3] = *(pnt
+2);
1261 data
.rate_byte
[2] = *(pnt
+3);
1262 data
.rate_byte
[1] = *(pnt
+4);
1263 data
.rate_byte
[0] = *(pnt
+5);
1264 snprintf(encbuf
, sizeof(encbuf
), "FS:rate %f",
1266 } else if (sub_type
== ECOMMUNITY_TRAFFIC_MARKING
) {
1267 snprintf(encbuf
, sizeof(encbuf
),
1268 "FS:marking %u", *(pnt
+ 5));
1271 } else if (type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) {
1273 if (sub_type
== ECOMMUNITY_LINK_BANDWIDTH
)
1274 ecommunity_lb_str(encbuf
, sizeof(encbuf
), pnt
,
1275 ecom
->disable_ieee_floating
);
1278 } else if (type
== ECOMMUNITY_ENCODE_IP_NON_TRANS
) {
1280 if (sub_type
== ECOMMUNITY_NODE_TARGET
)
1281 ecommunity_node_target_str(
1282 encbuf
, sizeof(encbuf
), pnt
, format
);
1285 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS
) {
1287 if (sub_type
== ECOMMUNITY_ORIGIN_VALIDATION_STATE
)
1288 ecommunity_origin_validation_state_str(
1289 encbuf
, sizeof(encbuf
), pnt
);
1298 snprintf(encbuf
, sizeof(encbuf
), "UNK:%d, %d", type
,
1301 int r
= strlcat(str_buf
, encbuf
, str_size
);
1302 assert(r
< str_size
);
1308 bool ecommunity_include(struct ecommunity
*e1
, struct ecommunity
*e2
)
1314 for (i
= 0; i
< e1
->size
; ++i
) {
1315 for (j
= 0; j
< e2
->size
; ++j
) {
1316 if (!memcmp(e1
->val
+ (i
* e1
->unit_size
),
1317 e2
->val
+ (j
* e2
->unit_size
),
1325 bool ecommunity_match(const struct ecommunity
*ecom1
,
1326 const struct ecommunity
*ecom2
)
1331 if (ecom1
== NULL
&& ecom2
== NULL
)
1334 if (ecom1
== NULL
|| ecom2
== NULL
)
1337 if (ecom1
->size
< ecom2
->size
)
1340 /* Every community on com2 needs to be on com1 for this to match */
1341 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
1342 if (memcmp(ecom1
->val
+ i
* ecom1
->unit_size
,
1343 ecom2
->val
+ j
* ecom2
->unit_size
,
1350 if (j
== ecom2
->size
)
1356 /* return first occurence of type */
1357 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
1358 uint8_t type
, uint8_t subtype
)
1363 /* If the value already exists in the structure return 0. */
1365 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1369 if (p
[0] == type
&& p
[1] == subtype
)
1370 return (struct ecommunity_val
*)p
;
1375 /* remove ext. community matching type and subtype
1376 * return 1 on success ( removed ), 0 otherwise (not present)
1378 bool ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
1381 uint8_t *p
, *q
, *new;
1382 uint32_t c
, found
= 0;
1383 /* When this is fist value, just add it. */
1384 if (ecom
== NULL
|| ecom
->val
== NULL
)
1387 /* Check if any existing ext community matches. */
1388 /* Certain extended communities like the Route Target can be present
1389 * multiple times, handle that.
1392 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1393 if (p
[0] == type
&& p
[1] == subtype
)
1396 /* If no matching ext community exists, return. */
1400 /* Handle the case where everything needs to be stripped. */
1401 if (found
== ecom
->size
) {
1402 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1407 /* Strip matching ext community(ies). */
1408 new = XMALLOC(MTYPE_ECOMMUNITY_VAL
,
1409 (ecom
->size
- found
) * ecom
->unit_size
);
1411 for (c
= 0, p
= ecom
->val
; c
< ecom
->size
; c
++, p
+= ecom
->unit_size
) {
1412 if (!(p
[0] == type
&& p
[1] == subtype
)) {
1413 memcpy(q
, p
, ecom
->unit_size
);
1414 q
+= ecom
->unit_size
;
1417 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1419 ecom
->size
-= found
;
1424 * Remove specified extended community value from extended community.
1425 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1427 bool ecommunity_del_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
1430 uint32_t c
, found
= 0;
1432 /* Make sure specified value exists. */
1433 if (ecom
== NULL
|| ecom
->val
== NULL
)
1436 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ecom
->unit_size
, c
++) {
1437 if (!memcmp(p
, eval
->val
, ecom
->unit_size
)) {
1445 /* Delete the selected value */
1448 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ecom
->unit_size
);
1450 memcpy(p
, ecom
->val
, c
* ecom
->unit_size
);
1451 if ((ecom
->size
- c
) != 0)
1452 memcpy(p
+ (c
)*ecom
->unit_size
,
1453 ecom
->val
+ (c
+ 1) * ecom
->unit_size
,
1454 (ecom
->size
- c
) * ecom
->unit_size
);
1455 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1458 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
1463 int ecommunity_fill_pbr_action(struct ecommunity_val
*ecom_eval
,
1464 struct bgp_pbr_entry_action
*api
,
1467 if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_RATE
) {
1468 api
->action
= ACTION_TRAFFICRATE
;
1469 api
->u
.r
.rate_info
[3] = ecom_eval
->val
[4];
1470 api
->u
.r
.rate_info
[2] = ecom_eval
->val
[5];
1471 api
->u
.r
.rate_info
[1] = ecom_eval
->val
[6];
1472 api
->u
.r
.rate_info
[0] = ecom_eval
->val
[7];
1473 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_ACTION
) {
1474 api
->action
= ACTION_TRAFFIC_ACTION
;
1475 /* else distribute code is set by default */
1476 if (ecom_eval
->val
[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
))
1477 api
->u
.za
.filter
|= TRAFFIC_ACTION_TERMINATE
;
1479 api
->u
.za
.filter
|= TRAFFIC_ACTION_DISTRIBUTE
;
1480 if (ecom_eval
->val
[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
1481 api
->u
.za
.filter
|= TRAFFIC_ACTION_SAMPLE
;
1483 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_MARKING
) {
1484 api
->action
= ACTION_MARKING
;
1485 api
->u
.marking_dscp
= ecom_eval
->val
[7];
1486 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_VRF
) {
1487 /* must use external function */
1489 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_IP_NH
&&
1491 /* see draft-ietf-idr-flowspec-redirect-ip-02
1492 * Q1: how come a ext. community can host ipv6 address
1493 * Q2 : from cisco documentation:
1494 * Announces the reachability of one or more flowspec NLRI.
1495 * When a BGP speaker receives an UPDATE message with the
1496 * redirect-to-IP extended community, it is expected to
1497 * create a traffic filtering rule for every flow-spec
1498 * NLRI in the message that has this path as its best
1499 * path. The filter entry matches the IP packets
1500 * described in the NLRI field and redirects them or
1501 * copies them towards the IPv4 or IPv6 address specified
1502 * in the 'Network Address of Next- Hop'
1503 * field of the associated MP_REACH_NLRI.
1505 struct ecommunity_ip
*ip_ecom
= (struct ecommunity_ip
*)
1508 api
->u
.zr
.redirect_ip_v4
= ip_ecom
->ip
;
1514 static struct ecommunity
*bgp_aggr_ecommunity_lookup(
1515 struct bgp_aggregate
*aggregate
,
1516 struct ecommunity
*ecommunity
)
1518 return hash_lookup(aggregate
->ecommunity_hash
, ecommunity
);
1521 static void *bgp_aggr_ecommunty_hash_alloc(void *p
)
1523 struct ecommunity
*ref
= (struct ecommunity
*)p
;
1524 struct ecommunity
*ecommunity
= NULL
;
1526 ecommunity
= ecommunity_dup(ref
);
1530 static void bgp_aggr_ecommunity_prepare(struct hash_bucket
*hb
, void *arg
)
1532 struct ecommunity
*hb_ecommunity
= hb
->data
;
1533 struct ecommunity
**aggr_ecommunity
= arg
;
1535 if (*aggr_ecommunity
)
1536 *aggr_ecommunity
= ecommunity_merge(*aggr_ecommunity
,
1539 *aggr_ecommunity
= ecommunity_dup(hb_ecommunity
);
1542 void bgp_aggr_ecommunity_remove(void *arg
)
1544 struct ecommunity
*ecommunity
= arg
;
1546 ecommunity_free(&ecommunity
);
1549 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate
*aggregate
,
1550 struct ecommunity
*ecommunity
)
1552 bgp_compute_aggregate_ecommunity_hash(aggregate
, ecommunity
);
1553 bgp_compute_aggregate_ecommunity_val(aggregate
);
1557 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate
*aggregate
,
1558 struct ecommunity
*ecommunity
)
1560 struct ecommunity
*aggr_ecommunity
= NULL
;
1562 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1565 /* Create hash if not already created.
1567 if (aggregate
->ecommunity_hash
== NULL
)
1568 aggregate
->ecommunity_hash
= hash_create(
1569 ecommunity_hash_make
, ecommunity_cmp
,
1570 "BGP Aggregator ecommunity hash");
1572 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1573 if (aggr_ecommunity
== NULL
) {
1574 /* Insert ecommunity into hash.
1576 aggr_ecommunity
= hash_get(aggregate
->ecommunity_hash
,
1578 bgp_aggr_ecommunty_hash_alloc
);
1581 /* Increment reference counter.
1583 aggr_ecommunity
->refcnt
++;
1586 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate
*aggregate
)
1588 struct ecommunity
*ecommerge
= NULL
;
1590 if (aggregate
== NULL
)
1593 /* Re-compute aggregate's ecommunity.
1595 if (aggregate
->ecommunity
)
1596 ecommunity_free(&aggregate
->ecommunity
);
1597 if (aggregate
->ecommunity_hash
1598 && aggregate
->ecommunity_hash
->count
) {
1599 hash_iterate(aggregate
->ecommunity_hash
,
1600 bgp_aggr_ecommunity_prepare
,
1601 &aggregate
->ecommunity
);
1602 ecommerge
= aggregate
->ecommunity
;
1603 aggregate
->ecommunity
= ecommunity_uniq_sort(ecommerge
);
1605 ecommunity_free(&ecommerge
);
1609 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
1610 struct ecommunity
*ecommunity
)
1612 struct ecommunity
*aggr_ecommunity
= NULL
;
1613 struct ecommunity
*ret_ecomm
= NULL
;
1616 || (!aggregate
->ecommunity_hash
)
1620 /* Look-up the ecommunity in the hash.
1622 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1623 if (aggr_ecommunity
) {
1624 aggr_ecommunity
->refcnt
--;
1626 if (aggr_ecommunity
->refcnt
== 0) {
1627 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1629 ecommunity_free(&ret_ecomm
);
1630 bgp_compute_aggregate_ecommunity_val(aggregate
);
1635 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1636 struct ecommunity
*ecommunity
)
1639 struct ecommunity
*aggr_ecommunity
= NULL
;
1640 struct ecommunity
*ret_ecomm
= NULL
;
1643 || (!aggregate
->ecommunity_hash
)
1647 /* Look-up the ecommunity in the hash.
1649 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1650 if (aggr_ecommunity
) {
1651 aggr_ecommunity
->refcnt
--;
1653 if (aggr_ecommunity
->refcnt
== 0) {
1654 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1656 ecommunity_free(&ret_ecomm
);
1662 ecommunity_add_origin_validation_state(enum rpki_states rpki_state
,
1663 struct ecommunity
*old
)
1665 struct ecommunity
*new = NULL
;
1666 struct ecommunity ovs_ecomm
= {0};
1667 struct ecommunity_val ovs_eval
;
1669 encode_origin_validation_state(rpki_state
, &ovs_eval
);
1672 new = ecommunity_dup(old
);
1673 ecommunity_add_val(new, &ovs_eval
, true, true);
1675 ecommunity_free(&old
);
1678 ovs_ecomm
.unit_size
= ECOMMUNITY_SIZE
;
1679 ovs_ecomm
.val
= (uint8_t *)&ovs_eval
.val
;
1680 new = ecommunity_dup(&ovs_ecomm
);
1687 * return the BGP link bandwidth extended community, if present;
1688 * the actual bandwidth is returned via param
1690 const uint8_t *ecommunity_linkbw_present(struct ecommunity
*ecom
, uint32_t *bw
)
1692 const uint8_t *eval
;
1698 if (!ecom
|| !ecom
->size
)
1701 for (i
= 0; i
< ecom
->size
; i
++) {
1703 uint8_t type
, sub_type
;
1706 eval
= pnt
= (ecom
->val
+ (i
* ECOMMUNITY_SIZE
));
1710 if ((type
== ECOMMUNITY_ENCODE_AS
||
1711 type
== ECOMMUNITY_ENCODE_AS_NON_TRANS
) &&
1712 sub_type
== ECOMMUNITY_LINK_BANDWIDTH
) {
1713 pnt
+= 2; /* bandwidth is encoded as AS:val */
1714 pnt
= ptr_get_be32(pnt
, &bwval
);
1715 (void)pnt
; /* consume value */
1717 *bw
= ecom
->disable_ieee_floating
1719 : ieee_float_uint32_to_uint32(
1729 struct ecommunity
*ecommunity_replace_linkbw(as_t as
, struct ecommunity
*ecom
,
1731 bool disable_ieee_floating
)
1733 struct ecommunity
*new;
1734 struct ecommunity_val lb_eval
;
1735 const uint8_t *eval
;
1739 /* Nothing to replace if link-bandwidth doesn't exist or
1740 * is non-transitive - just return existing extcommunity.
1743 if (!ecom
|| !ecom
->size
)
1746 eval
= ecommunity_linkbw_present(ecom
, &cur_bw
);
1751 if (type
& ECOMMUNITY_FLAG_NON_TRANSITIVE
)
1754 /* Transitive link-bandwidth exists, replace with the passed
1755 * (cumulative) bandwidth value. We need to create a new
1756 * extcommunity for this - refer to AS-Path replace function
1759 if (cum_bw
> 0xFFFFFFFF)
1760 cum_bw
= 0xFFFFFFFF;
1761 encode_lb_extcomm(as
> BGP_AS_MAX
? BGP_AS_TRANS
: as
, cum_bw
, false,
1762 &lb_eval
, disable_ieee_floating
);
1763 new = ecommunity_dup(ecom
);
1764 ecommunity_add_val(new, &lb_eval
, true, true);