]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #11903 from sri-mohan1/sri-bfd-dbg1
[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 /* Free 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 structure 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_include(struct ecommunity *e1, struct ecommunity *e2)
1192 {
1193 uint32_t i, j;
1194
1195 if (!e1 || !e2)
1196 return false;
1197 for (i = 0; i < e1->size; ++i) {
1198 for (j = 0; j < e2->size; ++j) {
1199 if (!memcmp(e1->val + (i * e1->unit_size),
1200 e2->val + (j * e2->unit_size),
1201 e1->unit_size))
1202 return true;
1203 }
1204 }
1205 return false;
1206 }
1207
1208 bool ecommunity_match(const struct ecommunity *ecom1,
1209 const struct ecommunity *ecom2)
1210 {
1211 uint32_t i = 0;
1212 uint32_t j = 0;
1213
1214 if (ecom1 == NULL && ecom2 == NULL)
1215 return true;
1216
1217 if (ecom1 == NULL || ecom2 == NULL)
1218 return false;
1219
1220 if (ecom1->size < ecom2->size)
1221 return false;
1222
1223 /* Every community on com2 needs to be on com1 for this to match */
1224 while (i < ecom1->size && j < ecom2->size) {
1225 if (memcmp(ecom1->val + i * ecom1->unit_size,
1226 ecom2->val + j * ecom2->unit_size,
1227 ecom2->unit_size)
1228 == 0)
1229 j++;
1230 i++;
1231 }
1232
1233 if (j == ecom2->size)
1234 return true;
1235 else
1236 return false;
1237 }
1238
1239 /* return first occurence of type */
1240 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1241 uint8_t type, uint8_t subtype)
1242 {
1243 uint8_t *p;
1244 uint32_t c;
1245
1246 /* If the value already exists in the structure return 0. */
1247 c = 0;
1248 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1249 if (p == NULL) {
1250 continue;
1251 }
1252 if (p[0] == type && p[1] == subtype)
1253 return (struct ecommunity_val *)p;
1254 }
1255 return NULL;
1256 }
1257
1258 /* remove ext. community matching type and subtype
1259 * return 1 on success ( removed ), 0 otherwise (not present)
1260 */
1261 bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1262 uint8_t subtype)
1263 {
1264 uint8_t *p, *q, *new;
1265 uint32_t c, found = 0;
1266 /* When this is fist value, just add it. */
1267 if (ecom == NULL || ecom->val == NULL)
1268 return false;
1269
1270 /* Check if any existing ext community matches. */
1271 /* Certain extended communities like the Route Target can be present
1272 * multiple times, handle that.
1273 */
1274 c = 0;
1275 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1276 if (p[0] == type && p[1] == subtype)
1277 found++;
1278 }
1279 /* If no matching ext community exists, return. */
1280 if (found == 0)
1281 return false;
1282
1283 /* Handle the case where everything needs to be stripped. */
1284 if (found == ecom->size) {
1285 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1286 ecom->size = 0;
1287 return true;
1288 }
1289
1290 /* Strip matching ext community(ies). */
1291 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1292 (ecom->size - found) * ecom->unit_size);
1293 q = new;
1294 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
1295 if (!(p[0] == type && p[1] == subtype)) {
1296 memcpy(q, p, ecom->unit_size);
1297 q += ecom->unit_size;
1298 }
1299 }
1300 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1301 ecom->val = new;
1302 ecom->size -= found;
1303 return true;
1304 }
1305
1306 /*
1307 * Remove specified extended community value from extended community.
1308 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1309 */
1310 bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
1311 {
1312 uint8_t *p;
1313 uint32_t c, found = 0;
1314
1315 /* Make sure specified value exists. */
1316 if (ecom == NULL || ecom->val == NULL)
1317 return false;
1318 c = 0;
1319 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1320 if (!memcmp(p, eval->val, ecom->unit_size)) {
1321 found = 1;
1322 break;
1323 }
1324 }
1325 if (found == 0)
1326 return false;
1327
1328 /* Delete the selected value */
1329 ecom->size--;
1330 if (ecom->size) {
1331 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1332 if (c != 0)
1333 memcpy(p, ecom->val, c * ecom->unit_size);
1334 if ((ecom->size - c) != 0)
1335 memcpy(p + (c)*ecom->unit_size,
1336 ecom->val + (c + 1) * ecom->unit_size,
1337 (ecom->size - c) * ecom->unit_size);
1338 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1339 ecom->val = p;
1340 } else
1341 ecom->val = NULL;
1342
1343 return true;
1344 }
1345
1346 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1347 struct bgp_pbr_entry_action *api,
1348 afi_t afi)
1349 {
1350 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1351 api->action = ACTION_TRAFFICRATE;
1352 api->u.r.rate_info[3] = ecom_eval->val[4];
1353 api->u.r.rate_info[2] = ecom_eval->val[5];
1354 api->u.r.rate_info[1] = ecom_eval->val[6];
1355 api->u.r.rate_info[0] = ecom_eval->val[7];
1356 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1357 api->action = ACTION_TRAFFIC_ACTION;
1358 /* else distribute code is set by default */
1359 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1360 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1361 else
1362 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1363 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1364 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1365
1366 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1367 api->action = ACTION_MARKING;
1368 api->u.marking_dscp = ecom_eval->val[7];
1369 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1370 /* must use external function */
1371 return 0;
1372 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1373 afi == AFI_IP) {
1374 /* see draft-ietf-idr-flowspec-redirect-ip-02
1375 * Q1: how come a ext. community can host ipv6 address
1376 * Q2 : from cisco documentation:
1377 * Announces the reachability of one or more flowspec NLRI.
1378 * When a BGP speaker receives an UPDATE message with the
1379 * redirect-to-IP extended community, it is expected to
1380 * create a traffic filtering rule for every flow-spec
1381 * NLRI in the message that has this path as its best
1382 * path. The filter entry matches the IP packets
1383 * described in the NLRI field and redirects them or
1384 * copies them towards the IPv4 or IPv6 address specified
1385 * in the 'Network Address of Next- Hop'
1386 * field of the associated MP_REACH_NLRI.
1387 */
1388 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1389 ecom_eval + 2;
1390
1391 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1392 } else
1393 return -1;
1394 return 0;
1395 }
1396
1397 static struct ecommunity *bgp_aggr_ecommunity_lookup(
1398 struct bgp_aggregate *aggregate,
1399 struct ecommunity *ecommunity)
1400 {
1401 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1402 }
1403
1404 static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1405 {
1406 struct ecommunity *ref = (struct ecommunity *)p;
1407 struct ecommunity *ecommunity = NULL;
1408
1409 ecommunity = ecommunity_dup(ref);
1410 return ecommunity;
1411 }
1412
1413 static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
1414 {
1415 struct ecommunity *hb_ecommunity = hb->data;
1416 struct ecommunity **aggr_ecommunity = arg;
1417
1418 if (*aggr_ecommunity)
1419 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1420 hb_ecommunity);
1421 else
1422 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1423 }
1424
1425 void bgp_aggr_ecommunity_remove(void *arg)
1426 {
1427 struct ecommunity *ecommunity = arg;
1428
1429 ecommunity_free(&ecommunity);
1430 }
1431
1432 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1433 struct ecommunity *ecommunity)
1434 {
1435 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1436 bgp_compute_aggregate_ecommunity_val(aggregate);
1437 }
1438
1439
1440 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1441 struct ecommunity *ecommunity)
1442 {
1443 struct ecommunity *aggr_ecommunity = NULL;
1444
1445 if ((aggregate == NULL) || (ecommunity == NULL))
1446 return;
1447
1448 /* Create hash if not already created.
1449 */
1450 if (aggregate->ecommunity_hash == NULL)
1451 aggregate->ecommunity_hash = hash_create(
1452 ecommunity_hash_make, ecommunity_cmp,
1453 "BGP Aggregator ecommunity hash");
1454
1455 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1456 if (aggr_ecommunity == NULL) {
1457 /* Insert ecommunity into hash.
1458 */
1459 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1460 ecommunity,
1461 bgp_aggr_ecommunty_hash_alloc);
1462 }
1463
1464 /* Increment reference counter.
1465 */
1466 aggr_ecommunity->refcnt++;
1467 }
1468
1469 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1470 {
1471 struct ecommunity *ecommerge = NULL;
1472
1473 if (aggregate == NULL)
1474 return;
1475
1476 /* Re-compute aggregate's ecommunity.
1477 */
1478 if (aggregate->ecommunity)
1479 ecommunity_free(&aggregate->ecommunity);
1480 if (aggregate->ecommunity_hash
1481 && aggregate->ecommunity_hash->count) {
1482 hash_iterate(aggregate->ecommunity_hash,
1483 bgp_aggr_ecommunity_prepare,
1484 &aggregate->ecommunity);
1485 ecommerge = aggregate->ecommunity;
1486 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1487 if (ecommerge)
1488 ecommunity_free(&ecommerge);
1489 }
1490 }
1491
1492 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1493 struct ecommunity *ecommunity)
1494 {
1495 struct ecommunity *aggr_ecommunity = NULL;
1496 struct ecommunity *ret_ecomm = NULL;
1497
1498 if ((!aggregate)
1499 || (!aggregate->ecommunity_hash)
1500 || (!ecommunity))
1501 return;
1502
1503 /* Look-up the ecommunity in the hash.
1504 */
1505 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1506 if (aggr_ecommunity) {
1507 aggr_ecommunity->refcnt--;
1508
1509 if (aggr_ecommunity->refcnt == 0) {
1510 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1511 aggr_ecommunity);
1512 ecommunity_free(&ret_ecomm);
1513 bgp_compute_aggregate_ecommunity_val(aggregate);
1514 }
1515 }
1516 }
1517
1518 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1519 struct ecommunity *ecommunity)
1520 {
1521
1522 struct ecommunity *aggr_ecommunity = NULL;
1523 struct ecommunity *ret_ecomm = NULL;
1524
1525 if ((!aggregate)
1526 || (!aggregate->ecommunity_hash)
1527 || (!ecommunity))
1528 return;
1529
1530 /* Look-up the ecommunity in the hash.
1531 */
1532 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1533 if (aggr_ecommunity) {
1534 aggr_ecommunity->refcnt--;
1535
1536 if (aggr_ecommunity->refcnt == 0) {
1537 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1538 aggr_ecommunity);
1539 ecommunity_free(&ret_ecomm);
1540 }
1541 }
1542 }
1543
1544 /*
1545 * return the BGP link bandwidth extended community, if present;
1546 * the actual bandwidth is returned via param
1547 */
1548 const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1549 {
1550 const uint8_t *eval;
1551 uint32_t i;
1552
1553 if (bw)
1554 *bw = 0;
1555
1556 if (!ecom || !ecom->size)
1557 return NULL;
1558
1559 for (i = 0; i < ecom->size; i++) {
1560 const uint8_t *pnt;
1561 uint8_t type, sub_type;
1562 uint32_t bwval;
1563
1564 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1565 type = *pnt++;
1566 sub_type = *pnt++;
1567
1568 if ((type == ECOMMUNITY_ENCODE_AS ||
1569 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1570 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1571 pnt += 2; /* bandwidth is encoded as AS:val */
1572 pnt = ptr_get_be32(pnt, &bwval);
1573 (void)pnt; /* consume value */
1574 if (bw)
1575 *bw = ecom->disable_ieee_floating
1576 ? bwval
1577 : ieee_float_uint32_to_uint32(
1578 bwval);
1579 return eval;
1580 }
1581 }
1582
1583 return NULL;
1584 }
1585
1586
1587 struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
1588 uint64_t cum_bw,
1589 bool disable_ieee_floating)
1590 {
1591 struct ecommunity *new;
1592 struct ecommunity_val lb_eval;
1593 const uint8_t *eval;
1594 uint8_t type;
1595 uint32_t cur_bw;
1596
1597 /* Nothing to replace if link-bandwidth doesn't exist or
1598 * is non-transitive - just return existing extcommunity.
1599 */
1600 new = ecom;
1601 if (!ecom || !ecom->size)
1602 return new;
1603
1604 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1605 if (!eval)
1606 return new;
1607
1608 type = *eval;
1609 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1610 return new;
1611
1612 /* Transitive link-bandwidth exists, replace with the passed
1613 * (cumulative) bandwidth value. We need to create a new
1614 * extcommunity for this - refer to AS-Path replace function
1615 * for reference.
1616 */
1617 if (cum_bw > 0xFFFFFFFF)
1618 cum_bw = 0xFFFFFFFF;
1619 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
1620 &lb_eval, disable_ieee_floating);
1621 new = ecommunity_dup(ecom);
1622 ecommunity_add_val(new, &lb_eval, true, true);
1623
1624 return new;
1625 }