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