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