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
)
82 /* When this is fist value, just add it. */
83 if (ecom
->val
== NULL
) {
85 ecom
->val
= XCALLOC(MTYPE_ECOMMUNITY_VAL
, ECOMMUNITY_SIZE
);
86 memcpy(ecom
->val
, eval
->val
, ECOMMUNITY_SIZE
);
90 /* If the value already exists in the structure return 0. */
92 for (uint8_t *p
= ecom
->val
; c
< ecom
->size
;
93 p
+= ECOMMUNITY_SIZE
, c
++) {
94 int ret
= memcmp(p
, eval
->val
, ECOMMUNITY_SIZE
);
101 /* Add the value to the structure with numerical sorting. */
103 ecom
->val
= XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->val
,
104 ecom
->size
* ECOMMUNITY_SIZE
);
106 memmove(ecom
->val
+ ((c
+ 1) * ECOMMUNITY_SIZE
),
107 ecom
->val
+ (c
* ECOMMUNITY_SIZE
),
108 (ecom
->size
- 1 - c
) * ECOMMUNITY_SIZE
);
109 memcpy(ecom
->val
+ (c
* ECOMMUNITY_SIZE
), eval
->val
, ECOMMUNITY_SIZE
);
114 /* This function takes pointer to Extended Communites strucutre then
115 create a new Extended Communities structure by uniq and sort each
116 Extended Communities value. */
117 struct ecommunity
*ecommunity_uniq_sort(struct ecommunity
*ecom
)
120 struct ecommunity
*new;
121 struct ecommunity_val
*eval
;
126 new = ecommunity_new();
128 for (i
= 0; i
< ecom
->size
; i
++) {
129 eval
= (struct ecommunity_val
*)(ecom
->val
130 + (i
* ECOMMUNITY_SIZE
));
131 ecommunity_add_val(new, eval
);
136 /* Parse Extended Communites Attribute in BGP packet. */
137 struct ecommunity
*ecommunity_parse(uint8_t *pnt
, unsigned short length
)
139 struct ecommunity tmp
;
140 struct ecommunity
*new;
143 if (length
% ECOMMUNITY_SIZE
)
146 /* Prepare tmporary structure for making a new Extended Communities
148 tmp
.size
= length
/ ECOMMUNITY_SIZE
;
151 /* Create a new Extended Communities Attribute by uniq and sort each
152 Extended Communities value */
153 new = ecommunity_uniq_sort(&tmp
);
155 return ecommunity_intern(new);
158 /* Duplicate the Extended Communities Attribute structure. */
159 struct ecommunity
*ecommunity_dup(struct ecommunity
*ecom
)
161 struct ecommunity
*new;
163 new = XCALLOC(MTYPE_ECOMMUNITY
, sizeof(struct ecommunity
));
164 new->size
= ecom
->size
;
166 new->val
= XMALLOC(MTYPE_ECOMMUNITY_VAL
,
167 ecom
->size
* ECOMMUNITY_SIZE
);
168 memcpy(new->val
, ecom
->val
, ecom
->size
* ECOMMUNITY_SIZE
);
174 /* Retrun string representation of communities attribute. */
175 char *ecommunity_str(struct ecommunity
*ecom
)
179 ecommunity_ecom2str(ecom
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
183 /* Merge two Extended Communities Attribute structure. */
184 struct ecommunity
*ecommunity_merge(struct ecommunity
*ecom1
,
185 struct ecommunity
*ecom2
)
189 XREALLOC(MTYPE_ECOMMUNITY_VAL
, ecom1
->val
,
190 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
193 XMALLOC(MTYPE_ECOMMUNITY_VAL
,
194 (ecom1
->size
+ ecom2
->size
) * ECOMMUNITY_SIZE
);
196 memcpy(ecom1
->val
+ (ecom1
->size
* ECOMMUNITY_SIZE
), ecom2
->val
,
197 ecom2
->size
* ECOMMUNITY_SIZE
);
198 ecom1
->size
+= ecom2
->size
;
203 /* Intern Extended Communities Attribute. */
204 struct ecommunity
*ecommunity_intern(struct ecommunity
*ecom
)
206 struct ecommunity
*find
;
208 assert(ecom
->refcnt
== 0);
210 find
= (struct ecommunity
*)hash_get(ecomhash
, ecom
, hash_alloc_intern
);
213 ecommunity_free(&ecom
);
219 ecommunity_ecom2str(find
, ECOMMUNITY_FORMAT_DISPLAY
, 0);
224 /* Unintern Extended Communities Attribute. */
225 void ecommunity_unintern(struct ecommunity
**ecom
)
227 struct ecommunity
*ret
;
232 /* Pull off from hash. */
233 if ((*ecom
)->refcnt
== 0) {
234 /* Extended community must be in the hash. */
235 ret
= (struct ecommunity
*)hash_release(ecomhash
, *ecom
);
238 ecommunity_free(ecom
);
242 /* Utinity function to make hash key. */
243 unsigned int ecommunity_hash_make(const void *arg
)
245 const struct ecommunity
*ecom
= arg
;
246 int size
= ecom
->size
* ECOMMUNITY_SIZE
;
248 return jhash(ecom
->val
, size
, 0x564321ab);
251 /* Compare two Extended Communities Attribute structure. */
252 bool ecommunity_cmp(const void *arg1
, const void *arg2
)
254 const struct ecommunity
*ecom1
= arg1
;
255 const struct ecommunity
*ecom2
= arg2
;
257 if (ecom1
== NULL
&& ecom2
== NULL
)
260 if (ecom1
== NULL
|| ecom2
== NULL
)
263 return (ecom1
->size
== ecom2
->size
264 && memcmp(ecom1
->val
, ecom2
->val
, ecom1
->size
* ECOMMUNITY_SIZE
)
268 /* Initialize Extended Comminities related hash. */
269 void ecommunity_init(void)
271 ecomhash
= hash_create(ecommunity_hash_make
, ecommunity_cmp
,
272 "BGP ecommunity hash");
275 void ecommunity_finish(void)
277 hash_clean(ecomhash
, (void (*)(void *))ecommunity_hash_free
);
282 /* Extended Communities token enum. */
283 enum ecommunity_token
{
284 ecommunity_token_unknown
= 0,
286 ecommunity_token_soo
,
287 ecommunity_token_val
,
291 * Encode BGP extended community from passed values. Supports types
292 * defined in RFC 4360 and well-known sub-types.
294 static int ecommunity_encode(uint8_t type
, uint8_t sub_type
, int trans
, as_t as
,
295 struct in_addr ip
, uint32_t val
,
296 struct ecommunity_val
*eval
)
299 if (type
== ECOMMUNITY_ENCODE_AS
) {
302 } else if (type
== ECOMMUNITY_ENCODE_IP
303 || type
== ECOMMUNITY_ENCODE_AS4
) {
304 if (val
> UINT16_MAX
)
308 /* Fill in the values. */
311 eval
->val
[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE
;
312 eval
->val
[1] = sub_type
;
313 if (type
== ECOMMUNITY_ENCODE_AS
) {
314 eval
->val
[2] = (as
>> 8) & 0xff;
315 eval
->val
[3] = as
& 0xff;
316 eval
->val
[4] = (val
>> 24) & 0xff;
317 eval
->val
[5] = (val
>> 16) & 0xff;
318 eval
->val
[6] = (val
>> 8) & 0xff;
319 eval
->val
[7] = val
& 0xff;
320 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
321 memcpy(&eval
->val
[2], &ip
, sizeof(struct in_addr
));
322 eval
->val
[6] = (val
>> 8) & 0xff;
323 eval
->val
[7] = val
& 0xff;
325 eval
->val
[2] = (as
>> 24) & 0xff;
326 eval
->val
[3] = (as
>> 16) & 0xff;
327 eval
->val
[4] = (as
>> 8) & 0xff;
328 eval
->val
[5] = as
& 0xff;
329 eval
->val
[6] = (val
>> 8) & 0xff;
330 eval
->val
[7] = val
& 0xff;
336 /* Get next Extended Communities token from the string. */
337 static const char *ecommunity_gettoken(const char *str
,
338 struct ecommunity_val
*eval
,
339 enum ecommunity_token
*token
)
351 char buf
[INET_ADDRSTRLEN
+ 1];
353 /* Skip white space. */
354 while (isspace((unsigned char)*p
)) {
359 /* Check the end of the line. */
363 /* "rt" and "soo" keyword parse. */
364 if (!isdigit((unsigned char)*p
)) {
365 /* "rt" match check. */
366 if (tolower((unsigned char)*p
) == 'r') {
368 if (tolower((unsigned char)*p
) == 't') {
370 *token
= ecommunity_token_rt
;
373 if (isspace((unsigned char)*p
) || *p
== '\0') {
374 *token
= ecommunity_token_rt
;
379 /* "soo" match check. */
380 else if (tolower((unsigned char)*p
) == 's') {
382 if (tolower((unsigned char)*p
) == 'o') {
384 if (tolower((unsigned char)*p
) == 'o') {
386 *token
= ecommunity_token_soo
;
389 if (isspace((unsigned char)*p
) || *p
== '\0') {
390 *token
= ecommunity_token_soo
;
395 if (isspace((unsigned char)*p
) || *p
== '\0') {
396 *token
= ecommunity_token_soo
;
404 /* What a mess, there are several possibilities:
410 * A.B.C.D: Four Byte IP
412 * GHJK: Four-byte ASN
414 * OPQR: Four byte value
417 while (isdigit((unsigned char)*p
) || *p
== ':' || *p
== '.') {
425 if ((p
- str
) > INET_ADDRSTRLEN
)
427 memset(buf
, 0, INET_ADDRSTRLEN
+ 1);
428 memcpy(buf
, str
, p
- str
);
431 /* Parsing A.B.C.D in:
434 ret
= inet_aton(buf
, &ip
);
439 as
= strtoul(buf
, &endptr
, 10);
440 if (*endptr
!= '\0' || as
== BGP_AS4_MAX
)
443 } else if (*p
== '.') {
452 /* We're past the IP/ASN part */
461 /* Low digit part must be there. */
462 if (!digit
|| !separator
)
465 /* Encode result into extended community. */
467 ecomm_type
= ECOMMUNITY_ENCODE_IP
;
468 else if (as
> BGP_AS_MAX
)
469 ecomm_type
= ECOMMUNITY_ENCODE_AS4
;
471 ecomm_type
= ECOMMUNITY_ENCODE_AS
;
472 if (ecommunity_encode(ecomm_type
, 0, 1, as
, ip
, val
, eval
))
474 *token
= ecommunity_token_val
;
478 *token
= ecommunity_token_unknown
;
482 /* Convert string to extended community attribute.
484 When type is already known, please specify both str and type. str
485 should not include keyword such as "rt" and "soo". Type is
486 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
487 keyword_included should be zero.
489 For example route-map's "set extcommunity" command case:
491 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
492 type = ECOMMUNITY_ROUTE_TARGET
495 "soo 100:1" -> str = "100:1"
496 type = ECOMMUNITY_SITE_ORIGIN
499 When string includes keyword for each extended community value.
500 Please specify keyword_included as non-zero value.
502 For example standard extcommunity-list case:
504 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
508 struct ecommunity
*ecommunity_str2com(const char *str
, int type
,
509 int keyword_included
)
511 struct ecommunity
*ecom
= NULL
;
512 enum ecommunity_token token
= ecommunity_token_unknown
;
513 struct ecommunity_val eval
;
516 while ((str
= ecommunity_gettoken(str
, &eval
, &token
))) {
518 case ecommunity_token_rt
:
519 case ecommunity_token_soo
:
520 if (!keyword_included
|| keyword
) {
522 ecommunity_free(&ecom
);
527 if (token
== ecommunity_token_rt
) {
528 type
= ECOMMUNITY_ROUTE_TARGET
;
530 if (token
== ecommunity_token_soo
) {
531 type
= ECOMMUNITY_SITE_ORIGIN
;
534 case ecommunity_token_val
:
535 if (keyword_included
) {
538 ecommunity_free(&ecom
);
544 ecom
= ecommunity_new();
546 ecommunity_add_val(ecom
, &eval
);
548 case ecommunity_token_unknown
:
551 ecommunity_free(&ecom
);
558 static int ecommunity_rt_soo_str(char *buf
, size_t bufsz
, uint8_t *pnt
,
559 int type
, int sub_type
, int format
)
564 /* For parse Extended Community attribute tupple. */
565 struct ecommunity_as eas
;
566 struct ecommunity_ip eip
;
569 /* Determine prefix for string, if any. */
571 case ECOMMUNITY_FORMAT_COMMUNITY_LIST
:
572 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "rt " : "soo ");
574 case ECOMMUNITY_FORMAT_DISPLAY
:
575 prefix
= (sub_type
== ECOMMUNITY_ROUTE_TARGET
? "RT:" : "SoO:");
577 case ECOMMUNITY_FORMAT_ROUTE_MAP
:
585 /* Put string into buffer. */
586 if (type
== ECOMMUNITY_ENCODE_AS4
) {
587 pnt
= ptr_get_be32(pnt
, &eas
.as
);
588 eas
.val
= (*pnt
++ << 8);
591 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
592 } else if (type
== ECOMMUNITY_ENCODE_AS
) {
593 eas
.as
= (*pnt
++ << 8);
595 pnt
= ptr_get_be32(pnt
, &eas
.val
);
597 len
= snprintf(buf
, bufsz
, "%s%u:%u", prefix
, eas
.as
, eas
.val
);
598 } else if (type
== ECOMMUNITY_ENCODE_IP
) {
599 memcpy(&eip
.ip
, pnt
, 4);
601 eip
.val
= (*pnt
++ << 8);
604 len
= snprintf(buf
, bufsz
, "%s%s:%u", prefix
, inet_ntoa(eip
.ip
),
614 /* Convert extended community attribute to string.
616 Due to historical reason of industry standard implementation, there
617 are three types of format.
619 route-map set extcommunity format
624 "rt 100:1 rt 100:2 soo 100:3"
626 "show [ip] bgp" and extcommunity-list regular expression matching
627 "RT:100:1 RT:100:2 SoO:100:3"
629 For each formath please use below definition for format:
631 ECOMMUNITY_FORMAT_ROUTE_MAP
632 ECOMMUNITY_FORMAT_COMMUNITY_LIST
633 ECOMMUNITY_FORMAT_DISPLAY
635 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
638 char *ecommunity_ecom2str(struct ecommunity
*ecom
, int format
, int filter
)
643 uint8_t sub_type
= 0;
644 #define ECOMMUNITY_STRLEN 64
649 return XCALLOC(MTYPE_ECOMMUNITY_STR
, 1);
651 /* ecom strlen + space + null term */
652 str_size
= (ecom
->size
* (ECOMMUNITY_STRLEN
+ 1)) + 1;
653 str_buf
= XCALLOC(MTYPE_ECOMMUNITY_STR
, str_size
);
657 for (i
= 0; i
< ecom
->size
; i
++) {
659 memset(encbuf
, 0x00, sizeof(encbuf
));
661 /* Space between each value. */
663 strlcat(str_buf
, " ", str_size
);
665 /* Retrieve value field */
666 pnt
= ecom
->val
+ (i
* 8);
668 /* High-order octet is the type */
671 if (type
== ECOMMUNITY_ENCODE_AS
|| type
== ECOMMUNITY_ENCODE_IP
672 || type
== ECOMMUNITY_ENCODE_AS4
) {
673 /* Low-order octet of type. */
675 if (sub_type
!= ECOMMUNITY_ROUTE_TARGET
676 && sub_type
!= ECOMMUNITY_SITE_ORIGIN
) {
678 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4
&&
679 type
== ECOMMUNITY_ENCODE_IP
) {
680 struct in_addr
*ipv4
=
681 (struct in_addr
*)pnt
;
682 char ipv4str
[INET_ADDRSTRLEN
];
684 inet_ntop(AF_INET
, ipv4
,
687 snprintf(encbuf
, sizeof(encbuf
),
688 "NH:%s:%d", ipv4str
, pnt
[5]);
692 ecommunity_rt_soo_str(encbuf
, sizeof(encbuf
),
696 } else if (type
== ECOMMUNITY_ENCODE_OPAQUE
) {
697 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
699 if (*pnt
== ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
) {
701 memcpy(&tunneltype
, pnt
+ 5, 2);
702 tunneltype
= ntohs(tunneltype
);
704 snprintf(encbuf
, sizeof(encbuf
), "ET:%d",
706 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_DEF_GW
) {
707 strlcpy(encbuf
, "Default Gateway",
712 } else if (type
== ECOMMUNITY_ENCODE_EVPN
) {
713 if (filter
== ECOMMUNITY_ROUTE_TARGET
)
715 if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC
) {
718 memcpy(&rmac
, pnt
, ETH_ALEN
);
720 snprintf(encbuf
, sizeof(encbuf
),
721 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
722 (uint8_t)rmac
.octet
[0],
723 (uint8_t)rmac
.octet
[1],
724 (uint8_t)rmac
.octet
[2],
725 (uint8_t)rmac
.octet
[3],
726 (uint8_t)rmac
.octet
[4],
727 (uint8_t)rmac
.octet
[5]);
729 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY
) {
731 uint8_t flags
= *++pnt
;
733 memcpy(&seqnum
, pnt
+ 2, 4);
734 seqnum
= ntohl(seqnum
);
736 snprintf(encbuf
, sizeof(encbuf
), "MM:%u",
741 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY
))
742 strlcat(encbuf
, ", sticky MAC",
744 } else if (*pnt
== ECOMMUNITY_EVPN_SUBTYPE_ND
) {
745 uint8_t flags
= *++pnt
;
749 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG
))
750 strlcpy(encbuf
, "ND:Router Flag",
754 } else if (type
== ECOMMUNITY_ENCODE_REDIRECT_IP_NH
) {
756 if (sub_type
== ECOMMUNITY_REDIRECT_IP_NH
) {
757 snprintf(encbuf
, sizeof(encbuf
),
758 "FS:redirect IP 0x%x", *(pnt
+ 5));
761 } else if (type
== ECOMMUNITY_ENCODE_TRANS_EXP
||
762 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_2
||
763 type
== ECOMMUNITY_EXTENDED_COMMUNITY_PART_3
) {
765 if (sub_type
== ECOMMUNITY_REDIRECT_VRF
) {
767 ecommunity_rt_soo_str(
768 buf
, sizeof(buf
), (uint8_t *)pnt
,
769 type
& ~ECOMMUNITY_ENCODE_TRANS_EXP
,
770 ECOMMUNITY_ROUTE_TARGET
,
771 ECOMMUNITY_FORMAT_DISPLAY
);
772 snprintf(encbuf
, sizeof(encbuf
),
773 "FS:redirect VRF %s", buf
);
774 } else if (type
!= ECOMMUNITY_ENCODE_TRANS_EXP
)
776 else if (sub_type
== ECOMMUNITY_TRAFFIC_ACTION
) {
780 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
)
781 strlcpy(action
, "terminate (apply)",
784 strlcpy(action
, "eval stops",
788 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
789 strlcat(action
, ", sample",
793 snprintf(encbuf
, sizeof(encbuf
), "FS:action %s",
795 } else if (sub_type
== ECOMMUNITY_TRAFFIC_RATE
) {
796 union traffic_rate data
;
798 data
.rate_byte
[3] = *(pnt
+2);
799 data
.rate_byte
[2] = *(pnt
+3);
800 data
.rate_byte
[1] = *(pnt
+4);
801 data
.rate_byte
[0] = *(pnt
+5);
802 snprintf(encbuf
, sizeof(encbuf
), "FS:rate %f",
804 } else if (sub_type
== ECOMMUNITY_TRAFFIC_MARKING
) {
805 snprintf(encbuf
, sizeof(encbuf
),
806 "FS:marking %u", *(pnt
+ 5));
808 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT
) {
811 memcpy(&mac
, pnt
, ETH_ALEN
);
814 encbuf
, sizeof(encbuf
),
815 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
816 (uint8_t)mac
.octet
[0],
817 (uint8_t)mac
.octet
[1],
818 (uint8_t)mac
.octet
[2],
819 (uint8_t)mac
.octet
[3],
820 (uint8_t)mac
.octet
[4],
821 (uint8_t)mac
.octet
[5]);
830 snprintf(encbuf
, sizeof(encbuf
), "UNK:%d, %d", type
,
833 int r
= strlcat(str_buf
, encbuf
, str_size
);
834 assert(r
< str_size
);
840 int ecommunity_match(const struct ecommunity
*ecom1
,
841 const struct ecommunity
*ecom2
)
846 if (ecom1
== NULL
&& ecom2
== NULL
)
849 if (ecom1
== NULL
|| ecom2
== NULL
)
852 if (ecom1
->size
< ecom2
->size
)
855 /* Every community on com2 needs to be on com1 for this to match */
856 while (i
< ecom1
->size
&& j
< ecom2
->size
) {
857 if (memcmp(ecom1
->val
+ i
* ECOMMUNITY_SIZE
,
858 ecom2
->val
+ j
* ECOMMUNITY_SIZE
, ECOMMUNITY_SIZE
)
864 if (j
== ecom2
->size
)
870 /* return first occurence of type */
871 extern struct ecommunity_val
*ecommunity_lookup(const struct ecommunity
*ecom
,
872 uint8_t type
, uint8_t subtype
)
877 /* If the value already exists in the structure return 0. */
879 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
883 if (p
[0] == type
&& p
[1] == subtype
)
884 return (struct ecommunity_val
*)p
;
889 /* remove ext. community matching type and subtype
890 * return 1 on success ( removed ), 0 otherwise (not present)
892 extern int ecommunity_strip(struct ecommunity
*ecom
, uint8_t type
,
897 /* When this is fist value, just add it. */
898 if (ecom
== NULL
|| ecom
->val
== NULL
) {
902 /* If the value already exists in the structure return 0. */
904 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
905 if (p
[0] == type
&& p
[1] == subtype
) {
912 /* Strip The selected value */
914 /* size is reduced. no memmove to do */
915 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
917 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
918 if ((ecom
->size
- c
) != 0)
919 memcpy(p
+ (c
)*ECOMMUNITY_SIZE
,
920 ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
921 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
922 /* shift last ecommunities */
923 XFREE(MTYPE_ECOMMUNITY
, ecom
->val
);
929 * Remove specified extended community value from extended community.
930 * Returns 1 if value was present (and hence, removed), 0 otherwise.
932 int ecommunity_del_val(struct ecommunity
*ecom
, struct ecommunity_val
*eval
)
937 /* Make sure specified value exists. */
938 if (ecom
== NULL
|| ecom
->val
== NULL
)
941 for (p
= ecom
->val
; c
< ecom
->size
; p
+= ECOMMUNITY_SIZE
, c
++) {
942 if (!memcmp(p
, eval
->val
, ECOMMUNITY_SIZE
)) {
950 /* Delete the selected value */
952 p
= XMALLOC(MTYPE_ECOMMUNITY_VAL
, ecom
->size
* ECOMMUNITY_SIZE
);
954 memcpy(p
, ecom
->val
, c
* ECOMMUNITY_SIZE
);
955 if ((ecom
->size
- c
) != 0)
956 memcpy(p
+ (c
)*ECOMMUNITY_SIZE
,
957 ecom
->val
+ (c
+ 1) * ECOMMUNITY_SIZE
,
958 (ecom
->size
- c
) * ECOMMUNITY_SIZE
);
959 XFREE(MTYPE_ECOMMUNITY_VAL
, ecom
->val
);
964 int ecommunity_fill_pbr_action(struct ecommunity_val
*ecom_eval
,
965 struct bgp_pbr_entry_action
*api
)
967 if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_RATE
) {
968 api
->action
= ACTION_TRAFFICRATE
;
969 api
->u
.r
.rate_info
[3] = ecom_eval
->val
[4];
970 api
->u
.r
.rate_info
[2] = ecom_eval
->val
[5];
971 api
->u
.r
.rate_info
[1] = ecom_eval
->val
[6];
972 api
->u
.r
.rate_info
[0] = ecom_eval
->val
[7];
973 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_ACTION
) {
974 api
->action
= ACTION_TRAFFIC_ACTION
;
975 /* else distribute code is set by default */
976 if (ecom_eval
->val
[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL
))
977 api
->u
.za
.filter
|= TRAFFIC_ACTION_TERMINATE
;
979 api
->u
.za
.filter
|= TRAFFIC_ACTION_DISTRIBUTE
;
980 if (ecom_eval
->val
[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE
)
981 api
->u
.za
.filter
|= TRAFFIC_ACTION_SAMPLE
;
983 } else if (ecom_eval
->val
[1] == ECOMMUNITY_TRAFFIC_MARKING
) {
984 api
->action
= ACTION_MARKING
;
985 api
->u
.marking_dscp
= ecom_eval
->val
[7];
986 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_VRF
) {
987 /* must use external function */
989 } else if (ecom_eval
->val
[1] == ECOMMUNITY_REDIRECT_IP_NH
) {
990 /* see draft-ietf-idr-flowspec-redirect-ip-02
991 * Q1: how come a ext. community can host ipv6 address
992 * Q2 : from cisco documentation:
993 * Announces the reachability of one or more flowspec NLRI.
994 * When a BGP speaker receives an UPDATE message with the
995 * redirect-to-IP extended community, it is expected to
996 * create a traffic filtering rule for every flow-spec
997 * NLRI in the message that has this path as its best
998 * path. The filter entry matches the IP packets
999 * described in the NLRI field and redirects them or
1000 * copies them towards the IPv4 or IPv6 address specified
1001 * in the 'Network Address of Next- Hop'
1002 * field of the associated MP_REACH_NLRI.
1004 struct ecommunity_ip
*ip_ecom
= (struct ecommunity_ip
*)
1007 api
->u
.zr
.redirect_ip_v4
= ip_ecom
->ip
;
1013 static struct ecommunity
*bgp_aggr_ecommunity_lookup(
1014 struct bgp_aggregate
*aggregate
,
1015 struct ecommunity
*ecommunity
)
1017 return hash_lookup(aggregate
->ecommunity_hash
, ecommunity
);
1020 static void *bgp_aggr_ecommunty_hash_alloc(void *p
)
1022 struct ecommunity
*ref
= (struct ecommunity
*)p
;
1023 struct ecommunity
*ecommunity
= NULL
;
1025 ecommunity
= ecommunity_dup(ref
);
1029 static void bgp_aggr_ecommunity_prepare(struct hash_backet
*hb
, void *arg
)
1031 struct ecommunity
*hb_ecommunity
= hb
->data
;
1032 struct ecommunity
**aggr_ecommunity
= arg
;
1034 if (*aggr_ecommunity
)
1035 *aggr_ecommunity
= ecommunity_merge(*aggr_ecommunity
,
1038 *aggr_ecommunity
= ecommunity_dup(hb_ecommunity
);
1041 void bgp_aggr_ecommunity_remove(void *arg
)
1043 struct ecommunity
*ecommunity
= arg
;
1045 ecommunity_free(&ecommunity
);
1048 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate
*aggregate
,
1049 struct ecommunity
*ecommunity
)
1051 bgp_compute_aggregate_ecommunity_hash(aggregate
, ecommunity
);
1052 bgp_compute_aggregate_ecommunity_val(aggregate
);
1056 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate
*aggregate
,
1057 struct ecommunity
*ecommunity
)
1059 struct ecommunity
*aggr_ecommunity
= NULL
;
1061 if ((aggregate
== NULL
) || (ecommunity
== NULL
))
1064 /* Create hash if not already created.
1066 if (aggregate
->ecommunity_hash
== NULL
)
1067 aggregate
->ecommunity_hash
= hash_create(
1068 ecommunity_hash_make
, ecommunity_cmp
,
1069 "BGP Aggregator ecommunity hash");
1071 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1072 if (aggr_ecommunity
== NULL
) {
1073 /* Insert ecommunity into hash.
1075 aggr_ecommunity
= hash_get(aggregate
->ecommunity_hash
,
1077 bgp_aggr_ecommunty_hash_alloc
);
1080 /* Increment reference counter.
1082 aggr_ecommunity
->refcnt
++;
1085 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate
*aggregate
)
1087 struct ecommunity
*ecommerge
= NULL
;
1089 if (aggregate
== NULL
)
1092 /* Re-compute aggregate's ecommunity.
1094 if (aggregate
->ecommunity
)
1095 ecommunity_free(&aggregate
->ecommunity
);
1096 if (aggregate
->ecommunity_hash
1097 && aggregate
->ecommunity_hash
->count
) {
1098 hash_iterate(aggregate
->ecommunity_hash
,
1099 bgp_aggr_ecommunity_prepare
,
1100 &aggregate
->ecommunity
);
1101 ecommerge
= aggregate
->ecommunity
;
1102 aggregate
->ecommunity
= ecommunity_uniq_sort(ecommerge
);
1104 ecommunity_free(&ecommerge
);
1108 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate
*aggregate
,
1109 struct ecommunity
*ecommunity
)
1111 struct ecommunity
*aggr_ecommunity
= NULL
;
1112 struct ecommunity
*ret_ecomm
= NULL
;
1115 || (!aggregate
->ecommunity_hash
)
1119 /* Look-up the ecommunity in the hash.
1121 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1122 if (aggr_ecommunity
) {
1123 aggr_ecommunity
->refcnt
--;
1125 if (aggr_ecommunity
->refcnt
== 0) {
1126 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1128 ecommunity_free(&ret_ecomm
);
1129 bgp_compute_aggregate_ecommunity_val(aggregate
);
1134 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate
*aggregate
,
1135 struct ecommunity
*ecommunity
)
1138 struct ecommunity
*aggr_ecommunity
= NULL
;
1139 struct ecommunity
*ret_ecomm
= NULL
;
1142 || (!aggregate
->ecommunity_hash
)
1146 /* Look-up the ecommunity in the hash.
1148 aggr_ecommunity
= bgp_aggr_ecommunity_lookup(aggregate
, ecommunity
);
1149 if (aggr_ecommunity
) {
1150 aggr_ecommunity
->refcnt
--;
1152 if (aggr_ecommunity
->refcnt
== 0) {
1153 ret_ecomm
= hash_release(aggregate
->ecommunity_hash
,
1155 ecommunity_free(&ret_ecomm
);