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 "bgpd/bgpd.h"
33 #include "bgpd/bgp_ecommunity.h"
34 #include "bgpd/bgp_lcommunity.h"
35 #include "bgpd/bgp_aspath.h"
36 #include "bgpd/bgp_flowspec_private.h"
37 #include "bgpd/bgp_pbr.h"
39 /* struct used to dump the rate contained in FS set traffic-rate EC */
45 /* Hash of community attribute. */
46 static struct hash
*ecomhash
;
48 /* Allocate a new ecommunities. */
49 struct ecommunity
*ecommunity_new(void)
51 return XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
54 void ecommunity_strfree(char **s
)
56 XFREE(MTYPE_ECOMMUNITY_STR
, *s
);
59 /* Allocate ecommunities. */
60 void ecommunity_free(struct ecommunity
**ecom
)
62 XFREE(MTYPE_ECOMMUNITY_VAL
, (*ecom
)->val
);
63 XFREE(MTYPE_ECOMMUNITY_STR
, (*ecom
)->str
);
64 XFREE(MTYPE_ECOMMUNITY
, *ecom
);
67 static void ecommunity_hash_free(struct ecommunity
*ecom
)
69 ecommunity_free(&ecom
);
73 /* Add a new Extended Communities value to Extended Communities
74 Attribute structure. When the value is already exists in the
75 structure, we don't add the value. Newly added value is sorted by
76 numerical order. When the value is added to the structure return 1
78 int ecommunity_add_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
84 /* When this is fist value, just add it. */
85 if (ecom
->val
== NULL
) {
87 ecom
->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom_length(ecom
));
88 memcpy(ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
92 /* If the value already exists in the structure return 0. */
94 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
95 ret
= memcmp(p
, eval
->val
, ECOMMUNITY_SIZE
);
102 /* Add the value to the structure with numerical sorting. */
105 XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->val
, ecom_length(ecom
));
107 memmove(ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
108 ecom
->val
+ c
* ECOMMUNITY_SIZE
,
109 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
110 memcpy(ecom
->val
+ c
* ECOMMUNITY_SIZE
, eval
->val
, ECOMMUNITY_SIZE
);
115 /* This function takes pointer to Extended Communites strucutre then
116 create a new Extended Communities structure by uniq and sort each
117 Extended Communities value. */
118 struct ecommunity
*ecommunity_uniq_sort(struct ecommunity
*ecom
)
121 struct ecommunity
*new;
122 struct ecommunity_val
*eval
;
127 new = ecommunity_new();
129 for (i
= 0; i
< ecom
->size
; i
++) {
130 eval
= (struct ecommunity_val
*)(ecom
->val
131 + (i
* ECOMMUNITY_SIZE
));
132 ecommunity_add_val(new, eval
);
137 /* Parse Extended Communites Attribute in BGP packet. */
138 struct ecommunity
*ecommunity_parse(uint8_t *pnt
, unsigned short length
)
140 struct ecommunity tmp
;
141 struct ecommunity
*new;
144 if (length
% ECOMMUNITY_SIZE
)
147 /* Prepare tmporary structure for making a new Extended Communities
149 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
152 /* Create a new Extended Communities Attribute by uniq and sort each
153 Extended Communities value */
154 new = ecommunity_uniq_sort(&tmp
);
156 return ecommunity_intern(new);
159 /* Duplicate the Extended Communities Attribute structure. */
160 struct ecommunity
*ecommunity_dup(struct ecommunity
*ecom
)
162 struct ecommunity
*new;
164 new = XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
165 new->size
= ecom
->size
;
167 new->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
168 ecom
->size
* ECOMMUNITY_SIZE
);
169 memcpy(new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
175 /* Retrun string representation of communities attribute. */
176 char *ecommunity_str(struct ecommunity
*ecom
)
180 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
184 /* Merge two Extended Communities Attribute structure. */
185 struct ecommunity
*ecommunity_merge(struct ecommunity
*ecom1
,
186 struct ecommunity
*ecom2
)
190 XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
191 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
194 XMALLOC(MTYPE_ECOMMUNITY_VAL
,
195 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
197 memcpy(ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
), ecom2
->val
,
198 ecom2
->size
* ECOMMUNITY_SIZE
);
199 ecom1
->size
+= ecom2
->size
;
204 /* Intern Extended Communities Attribute. */
205 struct ecommunity
*ecommunity_intern(struct ecommunity
*ecom
)
207 struct ecommunity
*find
;
209 assert(ecom
->refcnt
== 0);
211 find
= (struct ecommunity
*)hash_get(ecomhash
, ecom
, hash_alloc_intern
);
214 ecommunity_free(&ecom
);
220 ecommunity_ecom2str(find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
225 /* Unintern Extended Communities Attribute. */
226 void ecommunity_unintern(struct ecommunity
**ecom
)
228 struct ecommunity
*ret
;
233 /* Pull off from hash. */
234 if ((*ecom
)->refcnt
== 0) {
235 /* Extended community must be in the hash. */
236 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
239 ecommunity_free(ecom
);
243 /* Utinity function to make hash key. */
244 unsigned int ecommunity_hash_make(void *arg
)
246 const struct ecommunity
*ecom
= arg
;
247 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
249 return jhash(ecom
->val
, size
, 0x564321ab);
252 /* Compare two Extended Communities Attribute structure. */
253 bool ecommunity_cmp(const void *arg1
, const void *arg2
)
255 const struct ecommunity
*ecom1
= arg1
;
256 const struct ecommunity
*ecom2
= arg2
;
258 if (ecom1
== NULL
&& ecom2
== NULL
)
261 if (ecom1
== NULL
|| ecom2
== NULL
)
264 return (ecom1
->size
== ecom2
->size
265 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
)
269 /* Initialize Extended Comminities related hash. */
270 void ecommunity_init(void)
272 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
273 "BGP ecommunity hash");
276 void ecommunity_finish(void)
278 hash_clean(ecomhash
, (void (*)(void *))ecommunity_hash_free
);
283 /* Extended Communities token enum. */
284 enum ecommunity_token
{
285 ecommunity_token_unknown
= 0,
287 ecommunity_token_soo
,
288 ecommunity_token_val
,
292 * Encode BGP extended community from passed values. Supports types
293 * defined in RFC 4360 and well-known sub-types.
295 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
296 struct in_addr ip
, uint32_t val
,
297 struct ecommunity_val
*eval
)
300 if (type
== ECOMMUNITY_ENCODE_AS
) {
303 } else if (type
== ECOMMUNITY_ENCODE_IP
304 || type
== ECOMMUNITY_ENCODE_AS4
) {
305 if (val
> UINT16_MAX
)
309 /* Fill in the values. */
312 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
313 eval
->val
[1] = sub_type
;
314 if (type
== ECOMMUNITY_ENCODE_AS
) {
315 eval
->val
[2] = (as
>> 8) & 0xff;
316 eval
->val
[3] = as
& 0xff;
317 eval
->val
[4] = (val
>> 24) & 0xff;
318 eval
->val
[5] = (val
>> 16) & 0xff;
319 eval
->val
[6] = (val
>> 8) & 0xff;
320 eval
->val
[7] = val
& 0xff;
321 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
322 memcpy(&eval
->val
[2], &ip
, sizeof(struct in_addr
));
323 eval
->val
[6] = (val
>> 8) & 0xff;
324 eval
->val
[7] = val
& 0xff;
326 eval
->val
[2] = (as
>> 24) & 0xff;
327 eval
->val
[3] = (as
>> 16) & 0xff;
328 eval
->val
[4] = (as
>> 8) & 0xff;
329 eval
->val
[5] = as
& 0xff;
330 eval
->val
[6] = (val
>> 8) & 0xff;
331 eval
->val
[7] = val
& 0xff;
337 /* Get next Extended Communities token from the string. */
338 static const char *ecommunity_gettoken(const char *str
,
339 struct ecommunity_val
*eval
,
340 enum ecommunity_token
*token
)
352 char buf
[INET_ADDRSTRLEN
+ 1];
354 /* Skip white space. */
355 while (isspace((int)*p
)) {
360 /* Check the end of the line. */
364 /* "rt" and "soo" keyword parse. */
365 if (!isdigit((int)*p
)) {
366 /* "rt" match check. */
367 if (tolower((int)*p
) == 'r') {
369 if (tolower((int)*p
) == 't') {
371 *token
= ecommunity_token_rt
;
374 if (isspace((int)*p
) || *p
== '\0') {
375 *token
= ecommunity_token_rt
;
380 /* "soo" match check. */
381 else if (tolower((int)*p
) == 's') {
383 if (tolower((int)*p
) == 'o') {
385 if (tolower((int)*p
) == 'o') {
387 *token
= ecommunity_token_soo
;
390 if (isspace((int)*p
) || *p
== '\0') {
391 *token
= ecommunity_token_soo
;
396 if (isspace((int)*p
) || *p
== '\0') {
397 *token
= ecommunity_token_soo
;
405 /* What a mess, there are several possibilities:
411 * A.B.C.D: Four Byte IP
413 * GHJK: Four-byte ASN
415 * OPQR: Four byte value
418 while (isdigit((int)*p
) || *p
== ':' || *p
== '.') {
426 if ((p
- str
) > INET_ADDRSTRLEN
)
428 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
429 memcpy(buf
, str
, p
- str
);
432 /* Parsing A.B.C.D in:
435 ret
= inet_aton(buf
, &ip
);
440 as
= strtoul(buf
, &endptr
, 10);
441 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
444 } else if (*p
== '.') {
453 /* We're past the IP/ASN part */
462 /* Low digit part must be there. */
463 if (!digit
|| !separator
)
466 /* Encode result into extended community. */
468 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
469 else if (as
> BGP_AS_MAX
)
470 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
472 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
473 if (ecommunity_encode(ecomm_type
, 0, 1, as
, ip
, val
, eval
))
475 *token
= ecommunity_token_val
;
479 *token
= ecommunity_token_unknown
;
483 /* Convert string to extended community attribute.
485 When type is already known, please specify both str and type. str
486 should not include keyword such as "rt" and "soo". Type is
487 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
488 keyword_included should be zero.
490 For example route-map's "set extcommunity" command case:
492 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
493 type = ECOMMUNITY_ROUTE_TARGET
496 "soo 100:1" -> str = "100:1"
497 type = ECOMMUNITY_SITE_ORIGIN
500 When string includes keyword for each extended community value.
501 Please specify keyword_included as non-zero value.
503 For example standard extcommunity-list case:
505 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
509 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
510 int keyword_included
)
512 struct ecommunity
*ecom
= NULL
;
513 enum ecommunity_token token
= ecommunity_token_unknown
;
514 struct ecommunity_val eval
;
517 while ((str
= ecommunity_gettoken(str
, &eval
, &token
))) {
519 case ecommunity_token_rt
:
520 case ecommunity_token_soo
:
521 if (!keyword_included
|| keyword
) {
523 ecommunity_free(&ecom
);
528 if (token
== ecommunity_token_rt
) {
529 type
= ECOMMUNITY_ROUTE_TARGET
;
531 if (token
== ecommunity_token_soo
) {
532 type
= ECOMMUNITY_SITE_ORIGIN
;
535 case ecommunity_token_val
:
536 if (keyword_included
) {
539 ecommunity_free(&ecom
);
545 ecom
= ecommunity_new();
547 ecommunity_add_val(ecom
, &eval
);
549 case ecommunity_token_unknown
:
552 ecommunity_free(&ecom
);
559 static int ecommunity_rt_soo_str(char *buf
, uint8_t *pnt
, int type
,
560 int sub_type
, int format
)
565 /* For parse Extended Community attribute tupple. */
566 struct ecommunity_as eas
;
567 struct ecommunity_ip eip
;
570 /* Determine prefix for string, if any. */
572 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
573 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
575 case ECOMMUNITY_FORMAT_DISPLAY
:
576 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
578 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
586 /* Put string into buffer. */
587 if (type
== ECOMMUNITY_ENCODE_AS4
) {
588 pnt
= ptr_get_be32(pnt
, &eas
.as
);
589 eas
.val
= (*pnt
++ << 8);
592 len
= sprintf(buf
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
593 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
594 eas
.as
= (*pnt
++ << 8);
596 pnt
= ptr_get_be32(pnt
, &eas
.val
);
598 len
= sprintf(buf
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
599 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
600 memcpy(&eip
.ip
, pnt
, 4);
602 eip
.val
= (*pnt
++ << 8);
605 len
= sprintf(buf
, "%s%s:%u", prefix
, inet_ntoa(eip
.ip
),
608 (void)pnt
; /* consume value */
613 /* Convert extended community attribute to string.
615 Due to historical reason of industry standard implementation, there
616 are three types of format.
618 route-map set extcommunity format
623 "rt 100:1 rt 100:2 soo 100:3"
625 "show [ip] bgp" and extcommunity-list regular expression matching
626 "RT:100:1 RT:100:2 SoO:100:3"
628 For each formath please use below definition for format:
630 ECOMMUNITY_FORMAT_ROUTE_MAP
631 ECOMMUNITY_FORMAT_COMMUNITY_LIST
632 ECOMMUNITY_FORMAT_DISPLAY
634 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
637 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
642 uint8_t sub_type
= 0;
643 #define ECOMMUNITY_STR_DEFAULT_LEN 64
650 if (ecom
->size
== 0) {
651 str_buf
= XMALLOC(MTYPE_ECOMMUNITY_STR
, 1);
656 /* Prepare buffer. */
657 str_buf
= XMALLOC(MTYPE_ECOMMUNITY_STR
, ECOMMUNITY_STR_DEFAULT_LEN
+ 1);
658 str_size
= ECOMMUNITY_STR_DEFAULT_LEN
+ 1;
662 for (i
= 0; i
< ecom
->size
; i
++) {
665 /* Make it sure size is enough. */
666 while (str_pnt
+ ECOMMUNITY_STR_DEFAULT_LEN
>= str_size
) {
668 str_buf
= XREALLOC(MTYPE_ECOMMUNITY_STR
, str_buf
,
672 /* Space between each value. */
674 str_buf
[str_pnt
++] = ' ';
678 pnt
= ecom
->val
+ (i
* 8);
680 /* High-order octet of type. */
683 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
684 || type
== ECOMMUNITY_ENCODE_AS4
) {
685 /* Low-order octet of type. */
687 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
688 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
) {
690 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4
&&
691 type
== ECOMMUNITY_ENCODE_IP
) {
692 struct in_addr
*ipv4
=
693 (struct in_addr
*)pnt
;
694 char ipv4str
[INET_ADDRSTRLEN
];
696 inet_ntop(AF_INET
, ipv4
,
699 len
= sprintf(str_buf
+ str_pnt
,
705 len
= ecommunity_rt_soo_str(str_buf
+ str_pnt
,
708 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
709 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
711 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
713 memcpy(&tunneltype
, pnt
+ 5, 2);
714 tunneltype
= ntohs(tunneltype
);
715 len
= sprintf(str_buf
+ str_pnt
, "ET:%d",
717 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
718 len
= sprintf(str_buf
+ str_pnt
,
722 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
723 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
725 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
728 memcpy(&rmac
, pnt
, ETH_ALEN
);
731 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
732 (uint8_t)rmac
.octet
[0],
733 (uint8_t)rmac
.octet
[1],
734 (uint8_t)rmac
.octet
[2],
735 (uint8_t)rmac
.octet
[3],
736 (uint8_t)rmac
.octet
[4],
737 (uint8_t)rmac
.octet
[5]);
739 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
741 uint8_t flags
= *++pnt
;
743 memcpy(&seqnum
, pnt
+ 2, 4);
744 seqnum
= ntohl(seqnum
);
746 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
)
747 len
= sprintf(str_buf
+ str_pnt
,
751 len
= sprintf(str_buf
+ str_pnt
,
753 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ND
) {
754 uint8_t flags
= *++pnt
;
757 & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG
)
758 len
= sprintf(str_buf
+ str_pnt
,
762 } else if (type
== ECOMMUNITY_ENCODE_REDIRECT_IP_NH
) {
764 if (sub_type
== ECOMMUNITY_REDIRECT_IP_NH
) {
767 "FS:redirect IP 0x%x", *(pnt
+5));
770 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
||
771 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_2
||
772 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_3
) {
774 if (sub_type
== ECOMMUNITY_REDIRECT_VRF
) {
777 memset(buf
, 0, sizeof(buf
));
778 ecommunity_rt_soo_str(buf
, (uint8_t *)pnt
,
780 ~ECOMMUNITY_ENCODE_TRANS_EXP
,
781 ECOMMUNITY_ROUTE_TARGET
,
782 ECOMMUNITY_FORMAT_DISPLAY
);
783 len
= snprintf(str_buf
+ str_pnt
,
785 "FS:redirect VRF %s", buf
);
786 } else if (type
!= ECOMMUNITY_ENCODE_TRANS_EXP
)
788 else if (sub_type
== ECOMMUNITY_TRAFFIC_ACTION
) {
793 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
)
794 ptr
+= snprintf(ptr
, sizeof(action
),
795 "terminate (apply)");
797 ptr
+= snprintf(ptr
, sizeof(action
),
800 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
801 snprintf(ptr
, sizeof(action
) -
802 (size_t)(ptr
-action
),
804 len
= snprintf(str_buf
+ str_pnt
,
806 "FS:action %s", action
);
807 } else if (sub_type
== ECOMMUNITY_TRAFFIC_RATE
) {
808 union traffic_rate data
;
810 data
.rate_byte
[3] = *(pnt
+2);
811 data
.rate_byte
[2] = *(pnt
+3);
812 data
.rate_byte
[1] = *(pnt
+4);
813 data
.rate_byte
[0] = *(pnt
+5);
816 "FS:rate %f", data
.rate_float
);
817 } else if (sub_type
== ECOMMUNITY_TRAFFIC_MARKING
) {
820 "FS:marking %u", *(pnt
+5));
822 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT
) {
826 memcpy(&mac
, pnt
, ETH_ALEN
);
829 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
830 (uint8_t)mac
.octet
[0],
831 (uint8_t)mac
.octet
[1],
832 (uint8_t)mac
.octet
[2],
833 (uint8_t)mac
.octet
[3],
834 (uint8_t)mac
.octet
[4],
835 (uint8_t)mac
.octet
[5]);
844 len
= sprintf(str_buf
+ str_pnt
, "UNK:%d, %d",
854 int ecommunity_match(const struct ecommunity
*ecom1
,
855 const struct ecommunity
*ecom2
)
860 if (ecom1
== NULL
&& ecom2
== NULL
)
863 if (ecom1
== NULL
|| ecom2
== NULL
)
866 if (ecom1
->size
< ecom2
->size
)
869 /* Every community on com2 needs to be on com1 for this to match */
870 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
871 if (memcmp(ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
872 ecom2
->val
+ j
* ECOMMUNITY_SIZE
, ECOMMUNITY_SIZE
)
878 if (j
== ecom2
->size
)
884 /* return first occurence of type */
885 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
886 uint8_t type
, uint8_t subtype
)
891 /* If the value already exists in the structure return 0. */
893 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
897 if (p
[0] == type
&& p
[1] == subtype
)
898 return (struct ecommunity_val
*)p
;
903 /* remove ext. community matching type and subtype
904 * return 1 on success ( removed ), 0 otherwise (not present)
906 extern int ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
911 /* When this is fist value, just add it. */
912 if (ecom
== NULL
|| ecom
->val
== NULL
) {
916 /* If the value already exists in the structure return 0. */
918 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
919 if (p
[0] == type
&& p
[1] == subtype
) {
926 /* Strip The selected value */
928 /* size is reduced. no memmove to do */
929 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
931 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
932 if ((ecom
->size
- c
) != 0)
933 memcpy(p
+ (c
)*ECOMMUNITY_SIZE
,
934 ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
935 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
936 /* shift last ecommunities */
937 XFREE(MTYPE_ECOMMUNITY
, ecom
->val
);
943 * Remove specified extended community value from extended community.
944 * Returns 1 if value was present (and hence, removed), 0 otherwise.
946 int ecommunity_del_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
951 /* Make sure specified value exists. */
952 if (ecom
== NULL
|| ecom
->val
== NULL
)
955 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
956 if (!memcmp(p
, eval
->val
, ECOMMUNITY_SIZE
)) {
964 /* Delete the selected value */
966 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
968 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
969 if ((ecom
->size
- c
) != 0)
970 memcpy(p
+ (c
)*ECOMMUNITY_SIZE
,
971 ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
972 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
973 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
978 int ecommunity_fill_pbr_action(struct ecommunity_val
*ecom_eval
,
979 struct bgp_pbr_entry_action
*api
)
981 if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_RATE
) {
982 api
->action
= ACTION_TRAFFICRATE
;
983 api
->u
.r
.rate_info
[3] = ecom_eval
->val
[4];
984 api
->u
.r
.rate_info
[2] = ecom_eval
->val
[5];
985 api
->u
.r
.rate_info
[1] = ecom_eval
->val
[6];
986 api
->u
.r
.rate_info
[0] = ecom_eval
->val
[7];
987 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_ACTION
) {
988 api
->action
= ACTION_TRAFFIC_ACTION
;
989 /* else distribute code is set by default */
990 if (ecom_eval
->val
[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
))
991 api
->u
.za
.filter
|= TRAFFIC_ACTION_TERMINATE
;
993 api
->u
.za
.filter
|= TRAFFIC_ACTION_DISTRIBUTE
;
994 if (ecom_eval
->val
[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
995 api
->u
.za
.filter
|= TRAFFIC_ACTION_SAMPLE
;
997 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_MARKING
) {
998 api
->action
= ACTION_MARKING
;
999 api
->u
.marking_dscp
= ecom_eval
->val
[7];
1000 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_VRF
) {
1001 /* must use external function */
1003 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_IP_NH
) {
1004 /* see draft-ietf-idr-flowspec-redirect-ip-02
1005 * Q1: how come a ext. community can host ipv6 address
1006 * Q2 : from cisco documentation:
1007 * Announces the reachability of one or more flowspec NLRI.
1008 * When a BGP speaker receives an UPDATE message with the
1009 * redirect-to-IP extended community, it is expected to
1010 * create a traffic filtering rule for every flow-spec
1011 * NLRI in the message that has this path as its best
1012 * path. The filter entry matches the IP packets
1013 * described in the NLRI field and redirects them or
1014 * copies them towards the IPv4 or IPv6 address specified
1015 * in the 'Network Address of Next- Hop'
1016 * field of the associated MP_REACH_NLRI.
1018 struct ecommunity_ip
*ip_ecom
= (struct ecommunity_ip
*)
1021 api
->u
.zr
.redirect_ip_v4
= ip_ecom
->ip
;
1027 static struct ecommunity
*bgp_aggr_ecommunity_lookup(
1028 struct bgp_aggregate
*aggregate
,
1029 struct ecommunity
*ecommunity
)
1031 return hash_lookup(aggregate
->ecommunity_hash
, ecommunity
);
1034 static void *bgp_aggr_ecommunty_hash_alloc(void *p
)
1036 struct ecommunity
*ref
= (struct ecommunity
*)p
;
1037 struct ecommunity
*ecommunity
= NULL
;
1039 ecommunity
= ecommunity_dup(ref
);
1043 static void bgp_aggr_ecommunity_prepare(struct hash_backet
*hb
, void *arg
)
1045 struct ecommunity
*ecommerge
= NULL
;
1046 struct ecommunity
*hb_ecommunity
= hb
->data
;
1047 struct ecommunity
**aggr_ecommunity
= arg
;
1049 if (*aggr_ecommunity
) {
1050 ecommerge
= ecommunity_merge(*aggr_ecommunity
, hb_ecommunity
);
1051 *aggr_ecommunity
= ecommunity_uniq_sort(ecommerge
);
1052 ecommunity_free(&ecommerge
);
1054 *aggr_ecommunity
= ecommunity_dup(hb_ecommunity
);
1057 void bgp_aggr_ecommunity_remove(void *arg
)
1059 struct ecommunity
*ecommunity
= arg
;
1061 ecommunity_free(&ecommunity
);
1064 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate
*aggregate
,
1065 struct ecommunity
*ecommunity
)
1067 struct ecommunity
*aggr_ecommunity
= NULL
;
1069 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1072 /* Create hash if not already created.
1074 if (aggregate
->ecommunity_hash
== NULL
)
1075 aggregate
->ecommunity_hash
= hash_create(
1076 ecommunity_hash_make
, ecommunity_cmp
,
1077 "BGP Aggregator ecommunity hash");
1079 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1080 if (aggr_ecommunity
== NULL
) {
1081 /* Insert ecommunity into hash.
1083 aggr_ecommunity
= hash_get(aggregate
->ecommunity_hash
,
1085 bgp_aggr_ecommunty_hash_alloc
);
1087 /* Re-compute aggregate's ecommunity.
1089 if (aggregate
->ecommunity
)
1090 ecommunity_free(&aggregate
->ecommunity
);
1092 hash_iterate(aggregate
->ecommunity_hash
,
1093 bgp_aggr_ecommunity_prepare
,
1094 &aggregate
->ecommunity
);
1097 /* Increment refernce counter.
1099 aggr_ecommunity
->refcnt
++;
1102 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
1103 struct ecommunity
*ecommunity
)
1105 struct ecommunity
*aggr_ecommunity
= NULL
;
1106 struct ecommunity
*ret_ecomm
= NULL
;
1108 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1111 if (aggregate
->ecommunity_hash
== NULL
)
1114 /* Look-up the ecommunity in the hash.
1116 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1117 if (aggr_ecommunity
) {
1118 aggr_ecommunity
->refcnt
--;
1120 if (aggr_ecommunity
->refcnt
== 0) {
1121 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1123 ecommunity_free(&ret_ecomm
);
1125 ecommunity_free(&aggregate
->ecommunity
);
1127 /* Compute aggregate's ecommunity.
1129 hash_iterate(aggregate
->ecommunity_hash
,
1130 bgp_aggr_ecommunity_prepare
,
1131 &aggregate
->ecommunity
);