]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #9649 from proelbtn/add-support-for-end-dt4
[mirror_frr.git] / bgpd / bgp_ecommunity.c
1 /* BGP Extended Communities Attribute
2 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
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
9 * later version.
10 *
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.
15 *
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
19 */
20
21 #include <zebra.h>
22
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
27 #include "queue.h"
28 #include "filter.h"
29 #include "jhash.h"
30 #include "stream.h"
31
32 #include "lib/printfrr.h"
33
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"
40
41 /* struct used to dump the rate contained in FS set traffic-rate EC */
42 union traffic_rate {
43 float rate_float;
44 uint8_t rate_byte[4];
45 };
46
47 /* Hash of community attribute. */
48 static struct hash *ecomhash;
49
50 /* Allocate a new ecommunities. */
51 struct ecommunity *ecommunity_new(void)
52 {
53 struct ecommunity *ecom;
54
55 ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
56 sizeof(struct ecommunity));
57 ecom->unit_size = ECOMMUNITY_SIZE;
58 return ecom;
59 }
60
61 void ecommunity_strfree(char **s)
62 {
63 XFREE(MTYPE_ECOMMUNITY_STR, *s);
64 }
65
66 /* Allocate ecommunities. */
67 void ecommunity_free(struct ecommunity **ecom)
68 {
69 if (!(*ecom))
70 return;
71
72 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
73 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
74 XFREE(MTYPE_ECOMMUNITY, *ecom);
75 }
76
77 static void ecommunity_hash_free(struct ecommunity *ecom)
78 {
79 ecommunity_free(&ecom);
80 }
81
82
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
87 else return 0.
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
91 not.
92 */
93 static bool ecommunity_add_val_internal(struct ecommunity *ecom,
94 const void *eval,
95 bool unique, bool overwrite,
96 uint8_t ecom_size)
97 {
98 uint32_t c, ins_idx;
99 const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
100 const struct ecommunity_val_ipv6 *eval6 =
101 (struct ecommunity_val_ipv6 *)eval;
102
103 /* When this is fist value, just add it. */
104 if (ecom->val == NULL) {
105 ecom->size = 1;
106 ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
107 ecom_length_size(ecom, ecom_size));
108 memcpy(ecom->val, eval, ecom_size);
109 return true;
110 }
111
112 /* If the value already exists in the structure return 0. */
113 /* check also if the extended community itself exists. */
114 c = 0;
115
116 ins_idx = UINT32_MAX;
117 for (uint8_t *p = ecom->val; c < ecom->size;
118 p += ecom_size, c++) {
119 if (unique) {
120 if (ecom_size == ECOMMUNITY_SIZE) {
121 if (p[0] == eval4->val[0] &&
122 p[1] == eval4->val[1]) {
123 if (overwrite) {
124 memcpy(p, eval4->val,
125 ecom_size);
126 return true;
127 }
128 return false;
129 }
130 } else {
131 if (p[0] == eval6->val[0] &&
132 p[1] == eval6->val[1]) {
133 if (overwrite) {
134 memcpy(p, eval6->val,
135 ecom_size);
136 return true;
137 }
138 return false;
139 }
140 }
141 }
142 int ret = memcmp(p, eval, ecom_size);
143 if (ret == 0)
144 return false;
145 if (ret > 0) {
146 if (!unique)
147 break;
148 if (ins_idx == UINT32_MAX)
149 ins_idx = c;
150 }
151 }
152
153 if (ins_idx == UINT32_MAX)
154 ins_idx = c;
155
156 /* Add the value to the structure with numerical sorting. */
157 ecom->size++;
158 ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
159 ecom_length_size(ecom, ecom_size));
160
161 memmove(ecom->val + ((ins_idx + 1) * ecom_size),
162 ecom->val + (ins_idx * ecom_size),
163 (ecom->size - 1 - ins_idx) * ecom_size);
164 memcpy(ecom->val + (ins_idx * ecom_size),
165 eval, ecom_size);
166
167 return true;
168 }
169
170 /* Add a new Extended Communities value to Extended Communities
171 * Attribute structure. When the value is already exists in the
172 * structure, we don't add the value. Newly added value is sorted by
173 * numerical order. When the value is added to the structure return 1
174 * else return 0.
175 */
176 bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
177 bool unique, bool overwrite)
178 {
179 return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
180 overwrite, ECOMMUNITY_SIZE);
181 }
182
183 bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
184 struct ecommunity_val_ipv6 *eval,
185 bool unique, bool overwrite)
186 {
187 return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
188 overwrite, IPV6_ECOMMUNITY_SIZE);
189 }
190
191 static struct ecommunity *
192 ecommunity_uniq_sort_internal(struct ecommunity *ecom,
193 unsigned short ecom_size)
194 {
195 uint32_t i;
196 struct ecommunity *new;
197 const void *eval;
198
199 if (!ecom)
200 return NULL;
201
202 new = ecommunity_new();
203 new->unit_size = ecom_size;
204 new->disable_ieee_floating = ecom->disable_ieee_floating;
205
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);
209 }
210 return new;
211 }
212
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.
216 */
217 struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
218 {
219 return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
220 }
221
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,
226 bool disable_ieee_floating)
227 {
228 struct ecommunity tmp;
229 struct ecommunity *new;
230
231 /* Length check. */
232 if (length % size_ecom)
233 return NULL;
234
235 /* Prepare tmporary structure for making a new Extended Communities
236 Attribute. */
237 tmp.size = length / size_ecom;
238 tmp.val = pnt;
239 tmp.disable_ieee_floating = disable_ieee_floating;
240
241 /* Create a new Extended Communities Attribute by uniq and sort each
242 Extended Communities value */
243 new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
244
245 return ecommunity_intern(new);
246 }
247
248 struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length,
249 bool disable_ieee_floating)
250 {
251 return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE,
252 disable_ieee_floating);
253 }
254
255 struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, unsigned short length,
256 bool disable_ieee_floating)
257 {
258 return ecommunity_parse_internal(pnt, length, IPV6_ECOMMUNITY_SIZE,
259 disable_ieee_floating);
260 }
261
262 /* Duplicate the Extended Communities Attribute structure. */
263 struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
264 {
265 struct ecommunity *new;
266
267 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
268 new->size = ecom->size;
269 new->unit_size = ecom->unit_size;
270 if (new->size) {
271 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
272 ecom->size * ecom->unit_size);
273 memcpy(new->val, ecom->val,
274 (size_t)ecom->size * (size_t)ecom->unit_size);
275 } else
276 new->val = NULL;
277 return new;
278 }
279
280 /* Return string representation of ecommunities attribute. */
281 char *ecommunity_str(struct ecommunity *ecom)
282 {
283 if (!ecom->str)
284 ecom->str =
285 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
286 return ecom->str;
287 }
288
289 /* Merge two Extended Communities Attribute structure. */
290 struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
291 struct ecommunity *ecom2)
292 {
293 ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
294 (size_t)(ecom1->size + ecom2->size)
295 * (size_t)ecom1->unit_size);
296
297 memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
298 (size_t)ecom2->size * (size_t)ecom1->unit_size);
299 ecom1->size += ecom2->size;
300
301 return ecom1;
302 }
303
304 /* Intern Extended Communities Attribute. */
305 struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
306 {
307 struct ecommunity *find;
308
309 assert(ecom->refcnt == 0);
310 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
311 if (find != ecom)
312 ecommunity_free(&ecom);
313
314 find->refcnt++;
315
316 if (!find->str)
317 find->str =
318 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
319
320 return find;
321 }
322
323 /* Unintern Extended Communities Attribute. */
324 void ecommunity_unintern(struct ecommunity **ecom)
325 {
326 struct ecommunity *ret;
327
328 if (!*ecom)
329 return;
330
331 if ((*ecom)->refcnt)
332 (*ecom)->refcnt--;
333
334 /* Pull off from hash. */
335 if ((*ecom)->refcnt == 0) {
336 /* Extended community must be in the hash. */
337 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
338 assert(ret != NULL);
339
340 ecommunity_free(ecom);
341 }
342 }
343
344 /* Utinity function to make hash key. */
345 unsigned int ecommunity_hash_make(const void *arg)
346 {
347 const struct ecommunity *ecom = arg;
348 int size = ecom->size * ecom->unit_size;
349
350 return jhash(ecom->val, size, 0x564321ab);
351 }
352
353 /* Compare two Extended Communities Attribute structure. */
354 bool ecommunity_cmp(const void *arg1, const void *arg2)
355 {
356 const struct ecommunity *ecom1 = arg1;
357 const struct ecommunity *ecom2 = arg2;
358
359 if (ecom1 == NULL && ecom2 == NULL)
360 return true;
361
362 if (ecom1 == NULL || ecom2 == NULL)
363 return false;
364
365 if (ecom1->unit_size != ecom2->unit_size)
366 return false;
367
368 return (ecom1->size == ecom2->size
369 && memcmp(ecom1->val, ecom2->val, ecom1->size *
370 ecom1->unit_size) == 0);
371 }
372
373 /* Initialize Extended Comminities related hash. */
374 void ecommunity_init(void)
375 {
376 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
377 "BGP ecommunity hash");
378 }
379
380 void ecommunity_finish(void)
381 {
382 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
383 hash_free(ecomhash);
384 ecomhash = NULL;
385 }
386
387 /* Extended Communities token enum. */
388 enum ecommunity_token {
389 ecommunity_token_unknown = 0,
390 ecommunity_token_rt,
391 ecommunity_token_soo,
392 ecommunity_token_val,
393 ecommunity_token_rt6,
394 ecommunity_token_val6,
395 };
396
397 static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
398 int trans, as_t as,
399 struct in_addr *ip,
400 struct in6_addr *ip6,
401 uint32_t val,
402 void *eval_ptr)
403 {
404 struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
405 struct ecommunity_val_ipv6 *eval6 =
406 (struct ecommunity_val_ipv6 *)eval_ptr;
407
408 assert(eval);
409 if (type == ECOMMUNITY_ENCODE_AS) {
410 if (as > BGP_AS_MAX)
411 return -1;
412 } else if (type == ECOMMUNITY_ENCODE_IP
413 || type == ECOMMUNITY_ENCODE_AS4) {
414 if (val > UINT16_MAX)
415 return -1;
416 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
417 sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
418 (!ip6 || val > UINT16_MAX)) {
419 return -1;
420 }
421
422 /* Fill in the values. */
423 eval->val[0] = type;
424 if (!trans)
425 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
426 eval->val[1] = sub_type;
427 if (type == ECOMMUNITY_ENCODE_AS) {
428 eval->val[2] = (as >> 8) & 0xff;
429 eval->val[3] = as & 0xff;
430 eval->val[4] = (val >> 24) & 0xff;
431 eval->val[5] = (val >> 16) & 0xff;
432 eval->val[6] = (val >> 8) & 0xff;
433 eval->val[7] = val & 0xff;
434 } else if (type == ECOMMUNITY_ENCODE_IP) {
435 memcpy(&eval->val[2], ip, sizeof(struct in_addr));
436 eval->val[6] = (val >> 8) & 0xff;
437 eval->val[7] = val & 0xff;
438 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
439 sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
440 memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
441 eval6->val[18] = (val >> 8) & 0xff;
442 eval6->val[19] = val & 0xff;
443 } else {
444 eval->val[2] = (as >> 24) & 0xff;
445 eval->val[3] = (as >> 16) & 0xff;
446 eval->val[4] = (as >> 8) & 0xff;
447 eval->val[5] = as & 0xff;
448 eval->val[6] = (val >> 8) & 0xff;
449 eval->val[7] = val & 0xff;
450 }
451
452 return 0;
453 }
454
455 /*
456 * Encode BGP extended community from passed values. Supports types
457 * defined in RFC 4360 and well-known sub-types.
458 */
459 static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
460 struct in_addr ip, uint32_t val,
461 struct ecommunity_val *eval)
462 {
463 return ecommunity_encode_internal(type, sub_type, trans, as,
464 &ip, NULL, val, (void *)eval);
465 }
466
467 /* Get next Extended Communities token from the string. */
468 static const char *ecommunity_gettoken(const char *str,
469 void *eval_ptr,
470 enum ecommunity_token *token)
471 {
472 int ret;
473 int dot = 0;
474 int digit = 0;
475 int separator = 0;
476 const char *p = str;
477 char *endptr;
478 struct in_addr ip;
479 struct in6_addr ip6;
480 as_t as = 0;
481 uint32_t val = 0;
482 uint8_t ecomm_type;
483 char buf[INET_ADDRSTRLEN + 1];
484 struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
485 /* Skip white space. */
486 while (isspace((unsigned char)*p)) {
487 p++;
488 str++;
489 }
490
491 /* Check the end of the line. */
492 if (*p == '\0')
493 return NULL;
494
495 /* "rt" and "soo" keyword parse. */
496 if (!isdigit((unsigned char)*p)) {
497 /* "rt" match check. */
498 if (tolower((unsigned char)*p) == 'r') {
499 p++;
500 if (tolower((unsigned char)*p) == 't') {
501 p++;
502 if (*p != '\0' && tolower((int)*p) == '6')
503 *token = ecommunity_token_rt6;
504 else
505 *token = ecommunity_token_rt;
506 return p;
507 }
508 if (isspace((unsigned char)*p) || *p == '\0') {
509 *token = ecommunity_token_rt;
510 return p;
511 }
512 goto error;
513 }
514 /* "soo" match check. */
515 else if (tolower((unsigned char)*p) == 's') {
516 p++;
517 if (tolower((unsigned char)*p) == 'o') {
518 p++;
519 if (tolower((unsigned char)*p) == 'o') {
520 p++;
521 *token = ecommunity_token_soo;
522 return p;
523 }
524 if (isspace((unsigned char)*p) || *p == '\0') {
525 *token = ecommunity_token_soo;
526 return p;
527 }
528 goto error;
529 }
530 if (isspace((unsigned char)*p) || *p == '\0') {
531 *token = ecommunity_token_soo;
532 return p;
533 }
534 goto error;
535 }
536 goto error;
537 }
538
539 /* What a mess, there are several possibilities:
540 *
541 * a) A.B.C.D:MN
542 * b) EF:OPQR
543 * c) GHJK:MN
544 * d) <IPV6>:MN (only with rt6)
545 *
546 * A.B.C.D: Four Byte IP
547 * EF: Two byte ASN
548 * GHJK: Four-byte ASN
549 * MN: Two byte value
550 * OPQR: Four byte value
551 *
552 */
553 /* IPv6 case : look for last ':' */
554 if (*token == ecommunity_token_rt6 ||
555 *token == ecommunity_token_val6) {
556 char *limit;
557
558 limit = endptr = strrchr(p, ':');
559 if (!endptr)
560 goto error;
561
562 endptr++;
563 as = strtoul(endptr, &endptr, 10);
564 if (*endptr != '\0' || as == BGP_AS4_MAX)
565 goto error;
566
567 memcpy(buf, p, (limit - p));
568 buf[limit - p] = '\0';
569 ret = inet_pton(AF_INET6, buf, &ip6);
570 if (ret == 0)
571 goto error;
572
573 ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
574 if (ecommunity_encode_internal(ecomm_type,
575 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
576 1, 0, NULL, &ip6, as, eval_ptr))
577 goto error;
578
579 *token = ecommunity_token_val6;
580 while (isdigit((int)*p) || *p == ':' || *p == '.') {
581 p++;
582 }
583 return p;
584 }
585 while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
586 if (*p == ':') {
587 if (separator)
588 goto error;
589
590 separator = 1;
591 digit = 0;
592
593 if ((p - str) > INET_ADDRSTRLEN)
594 goto error;
595 memset(buf, 0, INET_ADDRSTRLEN + 1);
596 memcpy(buf, str, p - str);
597
598 if (dot) {
599 /* Parsing A.B.C.D in:
600 * A.B.C.D:MN
601 */
602 ret = inet_aton(buf, &ip);
603 if (ret == 0)
604 goto error;
605 } else {
606 /* ASN */
607 as = strtoul(buf, &endptr, 10);
608 if (*endptr != '\0' || as == BGP_AS4_MAX)
609 goto error;
610 }
611 } else if (*p == '.') {
612 if (separator)
613 goto error;
614 dot++;
615 if (dot > 4)
616 goto error;
617 } else {
618 digit = 1;
619
620 /* We're past the IP/ASN part */
621 if (separator) {
622 val *= 10;
623 val += (*p - '0');
624 }
625 }
626 p++;
627 }
628
629 /* Low digit part must be there. */
630 if (!digit || !separator)
631 goto error;
632
633 /* Encode result into extended community. */
634 if (dot)
635 ecomm_type = ECOMMUNITY_ENCODE_IP;
636 else if (as > BGP_AS_MAX)
637 ecomm_type = ECOMMUNITY_ENCODE_AS4;
638 else
639 ecomm_type = ECOMMUNITY_ENCODE_AS;
640 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
641 goto error;
642 *token = ecommunity_token_val;
643 return p;
644
645 error:
646 *token = ecommunity_token_unknown;
647 return p;
648 }
649
650 static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
651 int keyword_included,
652 bool is_ipv6_extcomm)
653 {
654 struct ecommunity *ecom = NULL;
655 enum ecommunity_token token = ecommunity_token_unknown;
656 struct ecommunity_val_ipv6 eval;
657 int keyword = 0;
658
659 if (is_ipv6_extcomm)
660 token = ecommunity_token_rt6;
661 while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
662 switch (token) {
663 case ecommunity_token_rt:
664 case ecommunity_token_soo:
665 if (!keyword_included || keyword) {
666 if (ecom)
667 ecommunity_free(&ecom);
668 return NULL;
669 }
670 keyword = 1;
671
672 if (token == ecommunity_token_rt ||
673 token == ecommunity_token_rt6) {
674 type = ECOMMUNITY_ROUTE_TARGET;
675 }
676 if (token == ecommunity_token_soo) {
677 type = ECOMMUNITY_SITE_ORIGIN;
678 }
679 break;
680 case ecommunity_token_val:
681 if (keyword_included) {
682 if (!keyword) {
683 ecommunity_free(&ecom);
684 return NULL;
685 }
686 keyword = 0;
687 }
688 if (ecom == NULL)
689 ecom = ecommunity_new();
690 eval.val[1] = type;
691 ecommunity_add_val_internal(ecom, (void *)&eval,
692 false, false,
693 ecom->unit_size);
694 break;
695 case ecommunity_token_val6:
696 if (keyword_included) {
697 if (!keyword) {
698 ecommunity_free(&ecom);
699 return NULL;
700 }
701 keyword = 0;
702 }
703 if (ecom == NULL)
704 ecom = ecommunity_new();
705 ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
706 eval.val[1] = type;
707 ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
708 ecom->unit_size);
709 break;
710 case ecommunity_token_unknown:
711 default:
712 if (ecom)
713 ecommunity_free(&ecom);
714 return NULL;
715 }
716 }
717 return ecom;
718 }
719
720 /* Convert string to extended community attribute.
721 *
722 * When type is already known, please specify both str and type. str
723 * should not include keyword such as "rt" and "soo". Type is
724 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
725 * keyword_included should be zero.
726 *
727 * For example route-map's "set extcommunity" command case:
728 *
729 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
730 * type = ECOMMUNITY_ROUTE_TARGET
731 * keyword_included = 0
732 *
733 * "soo 100:1" -> str = "100:1"
734 * type = ECOMMUNITY_SITE_ORIGIN
735 * keyword_included = 0
736 *
737 * When string includes keyword for each extended community value.
738 * Please specify keyword_included as non-zero value.
739 *
740 * For example standard extcommunity-list case:
741 *
742 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
743 * type = 0
744 * keyword_include = 1
745 */
746 struct ecommunity *ecommunity_str2com(const char *str, int type,
747 int keyword_included)
748 {
749 return ecommunity_str2com_internal(str, type,
750 keyword_included, false);
751 }
752
753 struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
754 int keyword_included)
755 {
756 return ecommunity_str2com_internal(str, type,
757 keyword_included, true);
758 }
759
760 static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
761 const uint8_t *pnt, int type,
762 int sub_type, int format,
763 unsigned short ecom_size)
764 {
765 int len = 0;
766 const char *prefix;
767 char buf_local[INET6_ADDRSTRLEN];
768
769 /* For parse Extended Community attribute tupple. */
770 struct ecommunity_as eas;
771 struct ecommunity_ip eip;
772 struct ecommunity_ip6 eip6;
773
774 /* Determine prefix for string, if any. */
775 switch (format) {
776 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
777 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
778 break;
779 case ECOMMUNITY_FORMAT_DISPLAY:
780 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
781 break;
782 case ECOMMUNITY_FORMAT_ROUTE_MAP:
783 prefix = "";
784 break;
785 default:
786 prefix = "";
787 break;
788 }
789
790 /* Put string into buffer. */
791 if (type == ECOMMUNITY_ENCODE_AS4) {
792 pnt = ptr_get_be32(pnt, &eas.as);
793 eas.val = (*pnt++ << 8);
794 eas.val |= (*pnt++);
795
796 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
797 } else if (type == ECOMMUNITY_ENCODE_AS) {
798 if (ecom_size == ECOMMUNITY_SIZE) {
799 eas.as = (*pnt++ << 8);
800 eas.as |= (*pnt++);
801 pnt = ptr_get_be32(pnt, &eas.val);
802
803 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
804 eas.val);
805 } else {
806 /* this is an IPv6 ext community
807 * first 16 bytes stands for IPv6 addres
808 */
809 memcpy(&eip6.ip, pnt, 16);
810 pnt += 16;
811 eip6.val = (*pnt++ << 8);
812 eip6.val |= (*pnt++);
813
814 inet_ntop(AF_INET6, &eip6.ip, buf_local,
815 sizeof(buf_local));
816 len = snprintf(buf, bufsz, "%s%s:%u", prefix,
817 buf_local, eip6.val);
818 }
819 } else if (type == ECOMMUNITY_ENCODE_IP) {
820 memcpy(&eip.ip, pnt, 4);
821 pnt += 4;
822 eip.val = (*pnt++ << 8);
823 eip.val |= (*pnt++);
824
825 len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip,
826 eip.val);
827 }
828
829 /* consume value */
830 (void)pnt;
831
832 return len;
833 }
834
835 static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
836 int type, int sub_type, int format)
837 {
838 return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
839 sub_type, format,
840 ECOMMUNITY_SIZE);
841 }
842
843 /* Helper function to convert IEEE-754 Floating Point to uint32 */
844 static uint32_t ieee_float_uint32_to_uint32(uint32_t u)
845 {
846 union {
847 float r;
848 uint32_t d;
849 } f = {.d = u};
850
851 return (uint32_t)f.r;
852 }
853
854 static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
855 bool disable_ieee_floating)
856 {
857 int len = 0;
858 as_t as;
859 uint32_t bw_tmp, bw;
860 char bps_buf[20] = {0};
861
862 #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
863 #define ONE_MBPS_BYTES (1000 * 1000 / 8)
864 #define ONE_KBPS_BYTES (1000 / 8)
865
866 as = (*pnt++ << 8);
867 as |= (*pnt++);
868 (void)ptr_get_be32(pnt, &bw_tmp);
869
870 bw = disable_ieee_floating ? bw_tmp
871 : ieee_float_uint32_to_uint32(bw_tmp);
872
873 if (bw >= ONE_GBPS_BYTES)
874 snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps",
875 (float)(bw / ONE_GBPS_BYTES));
876 else if (bw >= ONE_MBPS_BYTES)
877 snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps",
878 (float)(bw / ONE_MBPS_BYTES));
879 else if (bw >= ONE_KBPS_BYTES)
880 snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps",
881 (float)(bw / ONE_KBPS_BYTES));
882 else
883 snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8);
884
885 len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
886 return len;
887 }
888
889 /* Convert extended community attribute to string.
890
891 Due to historical reason of industry standard implementation, there
892 are three types of format.
893
894 route-map set extcommunity format
895 "rt 100:1 100:2soo 100:3"
896
897 extcommunity-list
898 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
899 "RT:100:1 RT:100:2 SoO:100:3"
900
901 For each formath please use below definition for format:
902
903 ECOMMUNITY_FORMAT_ROUTE_MAP
904 ECOMMUNITY_FORMAT_COMMUNITY_LIST
905 ECOMMUNITY_FORMAT_DISPLAY
906
907 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
908 0 value displays all
909 */
910 char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
911 {
912 uint32_t i;
913 uint8_t *pnt;
914 uint8_t type = 0;
915 uint8_t sub_type = 0;
916 int str_size;
917 char *str_buf;
918
919 if (!ecom || ecom->size == 0)
920 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
921
922 /* ecom strlen + space + null term */
923 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
924 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
925
926 char encbuf[128];
927
928 for (i = 0; i < ecom->size; i++) {
929 int unk_ecom = 0;
930 memset(encbuf, 0x00, sizeof(encbuf));
931
932 /* Space between each value. */
933 if (i > 0)
934 strlcat(str_buf, " ", str_size);
935
936 /* Retrieve value field */
937 pnt = ecom->val + (i * ecom->unit_size);
938
939 /* High-order octet is the type */
940 type = *pnt++;
941
942 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
943 || type == ECOMMUNITY_ENCODE_AS4) {
944 /* Low-order octet of type. */
945 sub_type = *pnt++;
946 if (sub_type != ECOMMUNITY_ROUTE_TARGET
947 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
948 if (sub_type ==
949 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
950 type == ECOMMUNITY_ENCODE_IP) {
951 struct in_addr *ipv4 =
952 (struct in_addr *)pnt;
953 char ipv4str[INET_ADDRSTRLEN];
954
955 inet_ntop(AF_INET, ipv4,
956 ipv4str,
957 INET_ADDRSTRLEN);
958 snprintf(encbuf, sizeof(encbuf),
959 "NH:%s:%d", ipv4str, pnt[5]);
960 } else if (sub_type ==
961 ECOMMUNITY_LINK_BANDWIDTH &&
962 type == ECOMMUNITY_ENCODE_AS) {
963 ecommunity_lb_str(
964 encbuf, sizeof(encbuf), pnt,
965 ecom->disable_ieee_floating);
966 } else
967 unk_ecom = 1;
968 } else {
969 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
970 pnt, type, sub_type,
971 format);
972 }
973 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
974 if (filter == ECOMMUNITY_ROUTE_TARGET)
975 continue;
976 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
977 uint16_t tunneltype;
978 memcpy(&tunneltype, pnt + 5, 2);
979 tunneltype = ntohs(tunneltype);
980
981 snprintf(encbuf, sizeof(encbuf), "ET:%d",
982 tunneltype);
983 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
984 strlcpy(encbuf, "Default Gateway",
985 sizeof(encbuf));
986 } else {
987 unk_ecom = 1;
988 }
989 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
990 if (filter == ECOMMUNITY_ROUTE_TARGET)
991 continue;
992 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
993 struct ethaddr rmac;
994 pnt++;
995 memcpy(&rmac, pnt, ETH_ALEN);
996
997 snprintf(encbuf, sizeof(encbuf),
998 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
999 (uint8_t)rmac.octet[0],
1000 (uint8_t)rmac.octet[1],
1001 (uint8_t)rmac.octet[2],
1002 (uint8_t)rmac.octet[3],
1003 (uint8_t)rmac.octet[4],
1004 (uint8_t)rmac.octet[5]);
1005 } else if (*pnt
1006 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
1007 uint32_t seqnum;
1008 uint8_t flags = *++pnt;
1009
1010 memcpy(&seqnum, pnt + 2, 4);
1011 seqnum = ntohl(seqnum);
1012
1013 snprintf(encbuf, sizeof(encbuf), "MM:%u",
1014 seqnum);
1015
1016 if (CHECK_FLAG(
1017 flags,
1018 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
1019 strlcat(encbuf, ", sticky MAC",
1020 sizeof(encbuf));
1021 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1022 uint8_t flags = *++pnt;
1023
1024 if (CHECK_FLAG(
1025 flags,
1026 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1027 strlcpy(encbuf, "ND:Router Flag",
1028 sizeof(encbuf));
1029 if (CHECK_FLAG(
1030 flags,
1031 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1032 strlcpy(encbuf, "ND:Proxy",
1033 sizeof(encbuf));
1034 } else if (*pnt
1035 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1036 struct ethaddr mac;
1037
1038 pnt++;
1039 memcpy(&mac, pnt, ETH_ALEN);
1040 snprintf(encbuf,
1041 sizeof(encbuf),
1042 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1043 (uint8_t)mac.octet[0],
1044 (uint8_t)mac.octet[1],
1045 (uint8_t)mac.octet[2],
1046 (uint8_t)mac.octet[3],
1047 (uint8_t)mac.octet[4],
1048 (uint8_t)mac.octet[5]);
1049 } else if (*pnt
1050 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1051 uint8_t flags = *++pnt;
1052
1053 snprintf(encbuf,
1054 sizeof(encbuf), "ESI-label-Rt:%s",
1055 (flags &
1056 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1057 "SA":"AA");
1058 } else if (*pnt
1059 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1060 uint8_t alg;
1061 uint16_t pref;
1062 uint16_t bmap;
1063
1064 alg = *(pnt + 1);
1065 memcpy(&bmap, pnt + 2, 2);
1066 bmap = ntohs(bmap);
1067 memcpy(&pref, pnt + 5, 2);
1068 pref = ntohs(pref);
1069
1070 if (bmap)
1071 snprintf(
1072 encbuf, sizeof(encbuf),
1073 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1074 alg, bmap, pref);
1075 else
1076 snprintf(encbuf, sizeof(encbuf),
1077 "DF: (alg: %u, pref: %u)", alg,
1078 pref);
1079 } else
1080 unk_ecom = 1;
1081 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1082 sub_type = *pnt++;
1083 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
1084 snprintf(encbuf, sizeof(encbuf),
1085 "FS:redirect IP 0x%x", *(pnt + 5));
1086 } else
1087 unk_ecom = 1;
1088 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1089 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1090 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
1091 sub_type = *pnt++;
1092
1093 if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
1094 char buf[ECOMMUNITY_STRLEN];
1095
1096 memset(buf, 0, sizeof(buf));
1097 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1098 (const uint8_t *)pnt,
1099 type &
1100 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1101 ECOMMUNITY_ROUTE_TARGET,
1102 format,
1103 ecom->unit_size);
1104 snprintf(encbuf, sizeof(encbuf), "%s", buf);
1105 } else if (sub_type ==
1106 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
1107 char buf[64];
1108
1109 memset(buf, 0, sizeof(buf));
1110 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1111 (const uint8_t *)pnt,
1112 type &
1113 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1114 ECOMMUNITY_ROUTE_TARGET,
1115 ECOMMUNITY_FORMAT_DISPLAY,
1116 ecom->unit_size);
1117 snprintf(encbuf, sizeof(encbuf),
1118 "FS:redirect VRF %s", buf);
1119 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1120 char buf[16];
1121
1122 memset(buf, 0, sizeof(buf));
1123 ecommunity_rt_soo_str(buf, sizeof(buf),
1124 (const uint8_t *)pnt,
1125 type &
1126 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1127 ECOMMUNITY_ROUTE_TARGET,
1128 ECOMMUNITY_FORMAT_DISPLAY);
1129 snprintf(encbuf, sizeof(encbuf),
1130 "FS:redirect VRF %s", buf);
1131 snprintf(encbuf, sizeof(encbuf),
1132 "FS:redirect VRF %s", buf);
1133 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1134 unk_ecom = 1;
1135 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
1136 char action[64];
1137
1138 if (*(pnt+3) ==
1139 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
1140 strlcpy(action, "terminate (apply)",
1141 sizeof(action));
1142 else
1143 strlcpy(action, "eval stops",
1144 sizeof(action));
1145
1146 if (*(pnt+3) ==
1147 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1148 strlcat(action, ", sample",
1149 sizeof(action));
1150
1151
1152 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1153 action);
1154 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1155 union traffic_rate data;
1156
1157 data.rate_byte[3] = *(pnt+2);
1158 data.rate_byte[2] = *(pnt+3);
1159 data.rate_byte[1] = *(pnt+4);
1160 data.rate_byte[0] = *(pnt+5);
1161 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1162 data.rate_float);
1163 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
1164 snprintf(encbuf, sizeof(encbuf),
1165 "FS:marking %u", *(pnt + 5));
1166 } else
1167 unk_ecom = 1;
1168 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1169 sub_type = *pnt++;
1170 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
1171 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt,
1172 ecom->disable_ieee_floating);
1173 else
1174 unk_ecom = 1;
1175 } else {
1176 sub_type = *pnt++;
1177 unk_ecom = 1;
1178 }
1179
1180 if (unk_ecom)
1181 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1182 sub_type);
1183
1184 int r = strlcat(str_buf, encbuf, str_size);
1185 assert(r < str_size);
1186 }
1187
1188 return str_buf;
1189 }
1190
1191 bool ecommunity_match(const struct ecommunity *ecom1,
1192 const struct ecommunity *ecom2)
1193 {
1194 uint32_t i = 0;
1195 uint32_t j = 0;
1196
1197 if (ecom1 == NULL && ecom2 == NULL)
1198 return true;
1199
1200 if (ecom1 == NULL || ecom2 == NULL)
1201 return false;
1202
1203 if (ecom1->size < ecom2->size)
1204 return false;
1205
1206 /* Every community on com2 needs to be on com1 for this to match */
1207 while (i < ecom1->size && j < ecom2->size) {
1208 if (memcmp(ecom1->val + i * ecom1->unit_size,
1209 ecom2->val + j * ecom2->unit_size,
1210 ecom2->unit_size)
1211 == 0)
1212 j++;
1213 i++;
1214 }
1215
1216 if (j == ecom2->size)
1217 return true;
1218 else
1219 return false;
1220 }
1221
1222 /* return first occurence of type */
1223 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1224 uint8_t type, uint8_t subtype)
1225 {
1226 uint8_t *p;
1227 uint32_t c;
1228
1229 /* If the value already exists in the structure return 0. */
1230 c = 0;
1231 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1232 if (p == NULL) {
1233 continue;
1234 }
1235 if (p[0] == type && p[1] == subtype)
1236 return (struct ecommunity_val *)p;
1237 }
1238 return NULL;
1239 }
1240
1241 /* remove ext. community matching type and subtype
1242 * return 1 on success ( removed ), 0 otherwise (not present)
1243 */
1244 bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1245 uint8_t subtype)
1246 {
1247 uint8_t *p, *q, *new;
1248 uint32_t c, found = 0;
1249 /* When this is fist value, just add it. */
1250 if (ecom == NULL || ecom->val == NULL)
1251 return false;
1252
1253 /* Check if any existing ext community matches. */
1254 /* Certain extended communities like the Route Target can be present
1255 * multiple times, handle that.
1256 */
1257 c = 0;
1258 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1259 if (p[0] == type && p[1] == subtype)
1260 found++;
1261 }
1262 /* If no matching ext community exists, return. */
1263 if (found == 0)
1264 return false;
1265
1266 /* Handle the case where everything needs to be stripped. */
1267 if (found == ecom->size) {
1268 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1269 ecom->size = 0;
1270 return true;
1271 }
1272
1273 /* Strip matching ext community(ies). */
1274 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1275 (ecom->size - found) * ecom->unit_size);
1276 q = new;
1277 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
1278 if (!(p[0] == type && p[1] == subtype)) {
1279 memcpy(q, p, ecom->unit_size);
1280 q += ecom->unit_size;
1281 }
1282 }
1283 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1284 ecom->val = new;
1285 ecom->size -= found;
1286 return true;
1287 }
1288
1289 /*
1290 * Remove specified extended community value from extended community.
1291 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1292 */
1293 bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
1294 {
1295 uint8_t *p;
1296 uint32_t c, found = 0;
1297
1298 /* Make sure specified value exists. */
1299 if (ecom == NULL || ecom->val == NULL)
1300 return false;
1301 c = 0;
1302 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1303 if (!memcmp(p, eval->val, ecom->unit_size)) {
1304 found = 1;
1305 break;
1306 }
1307 }
1308 if (found == 0)
1309 return false;
1310
1311 /* Delete the selected value */
1312 ecom->size--;
1313 if (ecom->size) {
1314 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1315 if (c != 0)
1316 memcpy(p, ecom->val, c * ecom->unit_size);
1317 if ((ecom->size - c) != 0)
1318 memcpy(p + (c)*ecom->unit_size,
1319 ecom->val + (c + 1) * ecom->unit_size,
1320 (ecom->size - c) * ecom->unit_size);
1321 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1322 ecom->val = p;
1323 } else
1324 ecom->val = NULL;
1325
1326 return true;
1327 }
1328
1329 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1330 struct bgp_pbr_entry_action *api,
1331 afi_t afi)
1332 {
1333 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1334 api->action = ACTION_TRAFFICRATE;
1335 api->u.r.rate_info[3] = ecom_eval->val[4];
1336 api->u.r.rate_info[2] = ecom_eval->val[5];
1337 api->u.r.rate_info[1] = ecom_eval->val[6];
1338 api->u.r.rate_info[0] = ecom_eval->val[7];
1339 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1340 api->action = ACTION_TRAFFIC_ACTION;
1341 /* else distribute code is set by default */
1342 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1343 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1344 else
1345 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1346 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1347 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1348
1349 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1350 api->action = ACTION_MARKING;
1351 api->u.marking_dscp = ecom_eval->val[7];
1352 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1353 /* must use external function */
1354 return 0;
1355 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1356 afi == AFI_IP) {
1357 /* see draft-ietf-idr-flowspec-redirect-ip-02
1358 * Q1: how come a ext. community can host ipv6 address
1359 * Q2 : from cisco documentation:
1360 * Announces the reachability of one or more flowspec NLRI.
1361 * When a BGP speaker receives an UPDATE message with the
1362 * redirect-to-IP extended community, it is expected to
1363 * create a traffic filtering rule for every flow-spec
1364 * NLRI in the message that has this path as its best
1365 * path. The filter entry matches the IP packets
1366 * described in the NLRI field and redirects them or
1367 * copies them towards the IPv4 or IPv6 address specified
1368 * in the 'Network Address of Next- Hop'
1369 * field of the associated MP_REACH_NLRI.
1370 */
1371 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1372 ecom_eval + 2;
1373
1374 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1375 } else
1376 return -1;
1377 return 0;
1378 }
1379
1380 static struct ecommunity *bgp_aggr_ecommunity_lookup(
1381 struct bgp_aggregate *aggregate,
1382 struct ecommunity *ecommunity)
1383 {
1384 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1385 }
1386
1387 static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1388 {
1389 struct ecommunity *ref = (struct ecommunity *)p;
1390 struct ecommunity *ecommunity = NULL;
1391
1392 ecommunity = ecommunity_dup(ref);
1393 return ecommunity;
1394 }
1395
1396 static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
1397 {
1398 struct ecommunity *hb_ecommunity = hb->data;
1399 struct ecommunity **aggr_ecommunity = arg;
1400
1401 if (*aggr_ecommunity)
1402 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1403 hb_ecommunity);
1404 else
1405 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1406 }
1407
1408 void bgp_aggr_ecommunity_remove(void *arg)
1409 {
1410 struct ecommunity *ecommunity = arg;
1411
1412 ecommunity_free(&ecommunity);
1413 }
1414
1415 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1416 struct ecommunity *ecommunity)
1417 {
1418 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1419 bgp_compute_aggregate_ecommunity_val(aggregate);
1420 }
1421
1422
1423 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1424 struct ecommunity *ecommunity)
1425 {
1426 struct ecommunity *aggr_ecommunity = NULL;
1427
1428 if ((aggregate == NULL) || (ecommunity == NULL))
1429 return;
1430
1431 /* Create hash if not already created.
1432 */
1433 if (aggregate->ecommunity_hash == NULL)
1434 aggregate->ecommunity_hash = hash_create(
1435 ecommunity_hash_make, ecommunity_cmp,
1436 "BGP Aggregator ecommunity hash");
1437
1438 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1439 if (aggr_ecommunity == NULL) {
1440 /* Insert ecommunity into hash.
1441 */
1442 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1443 ecommunity,
1444 bgp_aggr_ecommunty_hash_alloc);
1445 }
1446
1447 /* Increment reference counter.
1448 */
1449 aggr_ecommunity->refcnt++;
1450 }
1451
1452 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1453 {
1454 struct ecommunity *ecommerge = NULL;
1455
1456 if (aggregate == NULL)
1457 return;
1458
1459 /* Re-compute aggregate's ecommunity.
1460 */
1461 if (aggregate->ecommunity)
1462 ecommunity_free(&aggregate->ecommunity);
1463 if (aggregate->ecommunity_hash
1464 && aggregate->ecommunity_hash->count) {
1465 hash_iterate(aggregate->ecommunity_hash,
1466 bgp_aggr_ecommunity_prepare,
1467 &aggregate->ecommunity);
1468 ecommerge = aggregate->ecommunity;
1469 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1470 if (ecommerge)
1471 ecommunity_free(&ecommerge);
1472 }
1473 }
1474
1475 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1476 struct ecommunity *ecommunity)
1477 {
1478 struct ecommunity *aggr_ecommunity = NULL;
1479 struct ecommunity *ret_ecomm = NULL;
1480
1481 if ((!aggregate)
1482 || (!aggregate->ecommunity_hash)
1483 || (!ecommunity))
1484 return;
1485
1486 /* Look-up the ecommunity in the hash.
1487 */
1488 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1489 if (aggr_ecommunity) {
1490 aggr_ecommunity->refcnt--;
1491
1492 if (aggr_ecommunity->refcnt == 0) {
1493 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1494 aggr_ecommunity);
1495 ecommunity_free(&ret_ecomm);
1496 bgp_compute_aggregate_ecommunity_val(aggregate);
1497 }
1498 }
1499 }
1500
1501 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1502 struct ecommunity *ecommunity)
1503 {
1504
1505 struct ecommunity *aggr_ecommunity = NULL;
1506 struct ecommunity *ret_ecomm = NULL;
1507
1508 if ((!aggregate)
1509 || (!aggregate->ecommunity_hash)
1510 || (!ecommunity))
1511 return;
1512
1513 /* Look-up the ecommunity in the hash.
1514 */
1515 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1516 if (aggr_ecommunity) {
1517 aggr_ecommunity->refcnt--;
1518
1519 if (aggr_ecommunity->refcnt == 0) {
1520 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1521 aggr_ecommunity);
1522 ecommunity_free(&ret_ecomm);
1523 }
1524 }
1525 }
1526
1527 /*
1528 * return the BGP link bandwidth extended community, if present;
1529 * the actual bandwidth is returned via param
1530 */
1531 const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1532 {
1533 const uint8_t *eval;
1534 uint32_t i;
1535
1536 if (bw)
1537 *bw = 0;
1538
1539 if (!ecom || !ecom->size)
1540 return NULL;
1541
1542 for (i = 0; i < ecom->size; i++) {
1543 const uint8_t *pnt;
1544 uint8_t type, sub_type;
1545 uint32_t bwval;
1546
1547 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1548 type = *pnt++;
1549 sub_type = *pnt++;
1550
1551 if ((type == ECOMMUNITY_ENCODE_AS ||
1552 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1553 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1554 pnt += 2; /* bandwidth is encoded as AS:val */
1555 pnt = ptr_get_be32(pnt, &bwval);
1556 (void)pnt; /* consume value */
1557 if (bw)
1558 *bw = ecom->disable_ieee_floating
1559 ? bwval
1560 : ieee_float_uint32_to_uint32(
1561 bwval);
1562 return eval;
1563 }
1564 }
1565
1566 return NULL;
1567 }
1568
1569
1570 struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
1571 uint64_t cum_bw,
1572 bool disable_ieee_floating)
1573 {
1574 struct ecommunity *new;
1575 struct ecommunity_val lb_eval;
1576 const uint8_t *eval;
1577 uint8_t type;
1578 uint32_t cur_bw;
1579
1580 /* Nothing to replace if link-bandwidth doesn't exist or
1581 * is non-transitive - just return existing extcommunity.
1582 */
1583 new = ecom;
1584 if (!ecom || !ecom->size)
1585 return new;
1586
1587 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1588 if (!eval)
1589 return new;
1590
1591 type = *eval;
1592 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1593 return new;
1594
1595 /* Transitive link-bandwidth exists, replace with the passed
1596 * (cumulative) bandwidth value. We need to create a new
1597 * extcommunity for this - refer to AS-Path replace function
1598 * for reference.
1599 */
1600 if (cum_bw > 0xFFFFFFFF)
1601 cum_bw = 0xFFFFFFFF;
1602 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
1603 &lb_eval, disable_ieee_floating);
1604 new = ecommunity_dup(ecom);
1605 ecommunity_add_val(new, &lb_eval, true, true);
1606
1607 return new;
1608 }