]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #8870 from anlancs/master-fix-reload-service
[mirror_frr.git] / bgpd / bgp_ecommunity.c
1 /* BGP Extended Communities Attribute
2 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
27 #include "queue.h"
28 #include "filter.h"
29 #include "jhash.h"
30 #include "stream.h"
31
32 #include "lib/printfrr.h"
33
34 #include "bgpd/bgpd.h"
35 #include "bgpd/bgp_ecommunity.h"
36 #include "bgpd/bgp_lcommunity.h"
37 #include "bgpd/bgp_aspath.h"
38 #include "bgpd/bgp_flowspec_private.h"
39 #include "bgpd/bgp_pbr.h"
40
41 /* struct used to dump the rate contained in FS set traffic-rate EC */
42 union traffic_rate {
43 float rate_float;
44 uint8_t rate_byte[4];
45 };
46
47 /* Hash of community attribute. */
48 static struct hash *ecomhash;
49
50 /* Allocate a new ecommunities. */
51 struct ecommunity *ecommunity_new(void)
52 {
53 struct ecommunity *ecom;
54
55 ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
56 sizeof(struct ecommunity));
57 ecom->unit_size = ECOMMUNITY_SIZE;
58 return ecom;
59 }
60
61 void ecommunity_strfree(char **s)
62 {
63 XFREE(MTYPE_ECOMMUNITY_STR, *s);
64 }
65
66 /* Allocate ecommunities. */
67 void ecommunity_free(struct ecommunity **ecom)
68 {
69 if (!(*ecom))
70 return;
71
72 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
73 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
74 XFREE(MTYPE_ECOMMUNITY, *ecom);
75 }
76
77 static void ecommunity_hash_free(struct ecommunity *ecom)
78 {
79 ecommunity_free(&ecom);
80 }
81
82
83 /* Add a new Extended Communities value to Extended Communities
84 Attribute structure. When the value is already exists in the
85 structure, we don't add the value. Newly added value is sorted by
86 numerical order. When the value is added to the structure return 1
87 else return 0.
88 The additional parameters 'unique' and 'overwrite' ensure a particular
89 extended community (based on type and sub-type) is present only
90 once and whether the new value should replace what is existing or
91 not.
92 */
93 static bool ecommunity_add_val_internal(struct ecommunity *ecom,
94 const void *eval,
95 bool unique, bool overwrite,
96 uint8_t ecom_size)
97 {
98 uint32_t c, ins_idx;
99 const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
100 const struct ecommunity_val_ipv6 *eval6 =
101 (struct ecommunity_val_ipv6 *)eval;
102
103 /* When this is fist value, just add it. */
104 if (ecom->val == NULL) {
105 ecom->size = 1;
106 ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
107 ecom_length_size(ecom, ecom_size));
108 memcpy(ecom->val, eval, ecom_size);
109 return true;
110 }
111
112 /* If the value already exists in the structure return 0. */
113 /* check also if the extended community itself exists. */
114 c = 0;
115
116 ins_idx = UINT32_MAX;
117 for (uint8_t *p = ecom->val; c < ecom->size;
118 p += ecom_size, c++) {
119 if (unique) {
120 if (ecom_size == ECOMMUNITY_SIZE) {
121 if (p[0] == eval4->val[0] &&
122 p[1] == eval4->val[1]) {
123 if (overwrite) {
124 memcpy(p, eval4->val,
125 ecom_size);
126 return true;
127 }
128 return false;
129 }
130 } else {
131 if (p[0] == eval6->val[0] &&
132 p[1] == eval6->val[1]) {
133 if (overwrite) {
134 memcpy(p, eval6->val,
135 ecom_size);
136 return true;
137 }
138 return false;
139 }
140 }
141 }
142 int ret = memcmp(p, eval, ecom_size);
143 if (ret == 0)
144 return false;
145 if (ret > 0) {
146 if (!unique)
147 break;
148 if (ins_idx == UINT32_MAX)
149 ins_idx = c;
150 }
151 }
152
153 if (ins_idx == UINT32_MAX)
154 ins_idx = c;
155
156 /* Add the value to the structure with numerical sorting. */
157 ecom->size++;
158 ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
159 ecom_length_size(ecom, ecom_size));
160
161 memmove(ecom->val + ((ins_idx + 1) * ecom_size),
162 ecom->val + (ins_idx * ecom_size),
163 (ecom->size - 1 - ins_idx) * ecom_size);
164 memcpy(ecom->val + (ins_idx * ecom_size),
165 eval, ecom_size);
166
167 return true;
168 }
169
170 /* Add a new Extended Communities value to Extended Communities
171 * Attribute structure. When the value is already exists in the
172 * structure, we don't add the value. Newly added value is sorted by
173 * numerical order. When the value is added to the structure return 1
174 * else return 0.
175 */
176 bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
177 bool unique, bool overwrite)
178 {
179 return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
180 overwrite, ECOMMUNITY_SIZE);
181 }
182
183 bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
184 struct ecommunity_val_ipv6 *eval,
185 bool unique, bool overwrite)
186 {
187 return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
188 overwrite, IPV6_ECOMMUNITY_SIZE);
189 }
190
191 static struct ecommunity *
192 ecommunity_uniq_sort_internal(struct ecommunity *ecom,
193 unsigned short ecom_size)
194 {
195 uint32_t i;
196 struct ecommunity *new;
197 const void *eval;
198
199 if (!ecom)
200 return NULL;
201
202 new = ecommunity_new();
203 new->unit_size = ecom_size;
204
205 for (i = 0; i < ecom->size; i++) {
206 eval = (void *)(ecom->val + (i * ecom_size));
207 ecommunity_add_val_internal(new, eval, false, false, ecom_size);
208 }
209 return new;
210 }
211
212 /* This function takes pointer to Extended Communites strucutre then
213 * create a new Extended Communities structure by uniq and sort each
214 * Extended Communities value.
215 */
216 struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
217 {
218 return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
219 }
220
221 /* Parse Extended Communites Attribute in BGP packet. */
222 static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
223 unsigned short length,
224 unsigned short size_ecom)
225 {
226 struct ecommunity tmp;
227 struct ecommunity *new;
228
229 /* Length check. */
230 if (length % size_ecom)
231 return NULL;
232
233 /* Prepare tmporary structure for making a new Extended Communities
234 Attribute. */
235 tmp.size = length / size_ecom;
236 tmp.val = pnt;
237
238 /* Create a new Extended Communities Attribute by uniq and sort each
239 Extended Communities value */
240 new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
241
242 return ecommunity_intern(new);
243 }
244
245 struct ecommunity *ecommunity_parse(uint8_t *pnt,
246 unsigned short length)
247 {
248 return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE);
249 }
250
251 struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
252 unsigned short length)
253 {
254 return ecommunity_parse_internal(pnt, length,
255 IPV6_ECOMMUNITY_SIZE);
256 }
257
258 /* Duplicate the Extended Communities Attribute structure. */
259 struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
260 {
261 struct ecommunity *new;
262
263 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
264 new->size = ecom->size;
265 new->unit_size = ecom->unit_size;
266 if (new->size) {
267 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
268 ecom->size * ecom->unit_size);
269 memcpy(new->val, ecom->val,
270 (size_t)ecom->size * (size_t)ecom->unit_size);
271 } else
272 new->val = NULL;
273 return new;
274 }
275
276 /* Return string representation of ecommunities attribute. */
277 char *ecommunity_str(struct ecommunity *ecom)
278 {
279 if (!ecom->str)
280 ecom->str =
281 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
282 return ecom->str;
283 }
284
285 /* Merge two Extended Communities Attribute structure. */
286 struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
287 struct ecommunity *ecom2)
288 {
289 ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
290 (size_t)(ecom1->size + ecom2->size)
291 * (size_t)ecom1->unit_size);
292
293 memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
294 (size_t)ecom2->size * (size_t)ecom1->unit_size);
295 ecom1->size += ecom2->size;
296
297 return ecom1;
298 }
299
300 /* Intern Extended Communities Attribute. */
301 struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
302 {
303 struct ecommunity *find;
304
305 assert(ecom->refcnt == 0);
306 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
307 if (find != ecom)
308 ecommunity_free(&ecom);
309
310 find->refcnt++;
311
312 if (!find->str)
313 find->str =
314 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
315
316 return find;
317 }
318
319 /* Unintern Extended Communities Attribute. */
320 void ecommunity_unintern(struct ecommunity **ecom)
321 {
322 struct ecommunity *ret;
323
324 if (!*ecom)
325 return;
326
327 if ((*ecom)->refcnt)
328 (*ecom)->refcnt--;
329
330 /* Pull off from hash. */
331 if ((*ecom)->refcnt == 0) {
332 /* Extended community must be in the hash. */
333 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
334 assert(ret != NULL);
335
336 ecommunity_free(ecom);
337 }
338 }
339
340 /* Utinity function to make hash key. */
341 unsigned int ecommunity_hash_make(const void *arg)
342 {
343 const struct ecommunity *ecom = arg;
344 int size = ecom->size * ecom->unit_size;
345
346 return jhash(ecom->val, size, 0x564321ab);
347 }
348
349 /* Compare two Extended Communities Attribute structure. */
350 bool ecommunity_cmp(const void *arg1, const void *arg2)
351 {
352 const struct ecommunity *ecom1 = arg1;
353 const struct ecommunity *ecom2 = arg2;
354
355 if (ecom1 == NULL && ecom2 == NULL)
356 return true;
357
358 if (ecom1 == NULL || ecom2 == NULL)
359 return false;
360
361 if (ecom1->unit_size != ecom2->unit_size)
362 return false;
363
364 return (ecom1->size == ecom2->size
365 && memcmp(ecom1->val, ecom2->val, ecom1->size *
366 ecom1->unit_size) == 0);
367 }
368
369 /* Initialize Extended Comminities related hash. */
370 void ecommunity_init(void)
371 {
372 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
373 "BGP ecommunity hash");
374 }
375
376 void ecommunity_finish(void)
377 {
378 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
379 hash_free(ecomhash);
380 ecomhash = NULL;
381 }
382
383 /* Extended Communities token enum. */
384 enum ecommunity_token {
385 ecommunity_token_unknown = 0,
386 ecommunity_token_rt,
387 ecommunity_token_soo,
388 ecommunity_token_val,
389 ecommunity_token_rt6,
390 ecommunity_token_val6,
391 };
392
393 static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
394 int trans, as_t as,
395 struct in_addr *ip,
396 struct in6_addr *ip6,
397 uint32_t val,
398 void *eval_ptr)
399 {
400 struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
401 struct ecommunity_val_ipv6 *eval6 =
402 (struct ecommunity_val_ipv6 *)eval_ptr;
403
404 assert(eval);
405 if (type == ECOMMUNITY_ENCODE_AS) {
406 if (as > BGP_AS_MAX)
407 return -1;
408 } else if (type == ECOMMUNITY_ENCODE_IP
409 || type == ECOMMUNITY_ENCODE_AS4) {
410 if (val > UINT16_MAX)
411 return -1;
412 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
413 sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
414 (!ip6 || val > UINT16_MAX)) {
415 return -1;
416 }
417
418 /* Fill in the values. */
419 eval->val[0] = type;
420 if (!trans)
421 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
422 eval->val[1] = sub_type;
423 if (type == ECOMMUNITY_ENCODE_AS) {
424 eval->val[2] = (as >> 8) & 0xff;
425 eval->val[3] = as & 0xff;
426 eval->val[4] = (val >> 24) & 0xff;
427 eval->val[5] = (val >> 16) & 0xff;
428 eval->val[6] = (val >> 8) & 0xff;
429 eval->val[7] = val & 0xff;
430 } else if (type == ECOMMUNITY_ENCODE_IP) {
431 memcpy(&eval->val[2], ip, sizeof(struct in_addr));
432 eval->val[6] = (val >> 8) & 0xff;
433 eval->val[7] = val & 0xff;
434 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
435 sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
436 memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
437 eval6->val[18] = (val >> 8) & 0xff;
438 eval6->val[19] = val & 0xff;
439 } else {
440 eval->val[2] = (as >> 24) & 0xff;
441 eval->val[3] = (as >> 16) & 0xff;
442 eval->val[4] = (as >> 8) & 0xff;
443 eval->val[5] = as & 0xff;
444 eval->val[6] = (val >> 8) & 0xff;
445 eval->val[7] = val & 0xff;
446 }
447
448 return 0;
449 }
450
451 /*
452 * Encode BGP extended community from passed values. Supports types
453 * defined in RFC 4360 and well-known sub-types.
454 */
455 static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
456 struct in_addr ip, uint32_t val,
457 struct ecommunity_val *eval)
458 {
459 return ecommunity_encode_internal(type, sub_type, trans, as,
460 &ip, NULL, val, (void *)eval);
461 }
462
463 /* Get next Extended Communities token from the string. */
464 static const char *ecommunity_gettoken(const char *str,
465 void *eval_ptr,
466 enum ecommunity_token *token)
467 {
468 int ret;
469 int dot = 0;
470 int digit = 0;
471 int separator = 0;
472 const char *p = str;
473 char *endptr;
474 struct in_addr ip;
475 struct in6_addr ip6;
476 as_t as = 0;
477 uint32_t val = 0;
478 uint8_t ecomm_type;
479 char buf[INET_ADDRSTRLEN + 1];
480 struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
481 /* Skip white space. */
482 while (isspace((unsigned char)*p)) {
483 p++;
484 str++;
485 }
486
487 /* Check the end of the line. */
488 if (*p == '\0')
489 return NULL;
490
491 /* "rt" and "soo" keyword parse. */
492 if (!isdigit((unsigned char)*p)) {
493 /* "rt" match check. */
494 if (tolower((unsigned char)*p) == 'r') {
495 p++;
496 if (tolower((unsigned char)*p) == 't') {
497 p++;
498 if (*p != '\0' && tolower((int)*p) == '6')
499 *token = ecommunity_token_rt6;
500 else
501 *token = ecommunity_token_rt;
502 return p;
503 }
504 if (isspace((unsigned char)*p) || *p == '\0') {
505 *token = ecommunity_token_rt;
506 return p;
507 }
508 goto error;
509 }
510 /* "soo" match check. */
511 else if (tolower((unsigned char)*p) == 's') {
512 p++;
513 if (tolower((unsigned char)*p) == 'o') {
514 p++;
515 if (tolower((unsigned char)*p) == 'o') {
516 p++;
517 *token = ecommunity_token_soo;
518 return p;
519 }
520 if (isspace((unsigned char)*p) || *p == '\0') {
521 *token = ecommunity_token_soo;
522 return p;
523 }
524 goto error;
525 }
526 if (isspace((unsigned char)*p) || *p == '\0') {
527 *token = ecommunity_token_soo;
528 return p;
529 }
530 goto error;
531 }
532 goto error;
533 }
534
535 /* What a mess, there are several possibilities:
536 *
537 * a) A.B.C.D:MN
538 * b) EF:OPQR
539 * c) GHJK:MN
540 * d) <IPV6>:MN (only with rt6)
541 *
542 * A.B.C.D: Four Byte IP
543 * EF: Two byte ASN
544 * GHJK: Four-byte ASN
545 * MN: Two byte value
546 * OPQR: Four byte value
547 *
548 */
549 /* IPv6 case : look for last ':' */
550 if (*token == ecommunity_token_rt6 ||
551 *token == ecommunity_token_val6) {
552 char *limit;
553
554 limit = endptr = strrchr(p, ':');
555 if (!endptr)
556 goto error;
557
558 endptr++;
559 as = strtoul(endptr, &endptr, 10);
560 if (*endptr != '\0' || as == BGP_AS4_MAX)
561 goto error;
562
563 memcpy(buf, p, (limit - p));
564 buf[limit - p] = '\0';
565 ret = inet_pton(AF_INET6, buf, &ip6);
566 if (ret == 0)
567 goto error;
568
569 ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
570 if (ecommunity_encode_internal(ecomm_type,
571 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
572 1, 0, NULL, &ip6, as, eval_ptr))
573 goto error;
574
575 *token = ecommunity_token_val6;
576 while (isdigit((int)*p) || *p == ':' || *p == '.') {
577 p++;
578 }
579 return p;
580 }
581 while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
582 if (*p == ':') {
583 if (separator)
584 goto error;
585
586 separator = 1;
587 digit = 0;
588
589 if ((p - str) > INET_ADDRSTRLEN)
590 goto error;
591 memset(buf, 0, INET_ADDRSTRLEN + 1);
592 memcpy(buf, str, p - str);
593
594 if (dot) {
595 /* Parsing A.B.C.D in:
596 * A.B.C.D:MN
597 */
598 ret = inet_aton(buf, &ip);
599 if (ret == 0)
600 goto error;
601 } else {
602 /* ASN */
603 as = strtoul(buf, &endptr, 10);
604 if (*endptr != '\0' || as == BGP_AS4_MAX)
605 goto error;
606 }
607 } else if (*p == '.') {
608 if (separator)
609 goto error;
610 dot++;
611 if (dot > 4)
612 goto error;
613 } else {
614 digit = 1;
615
616 /* We're past the IP/ASN part */
617 if (separator) {
618 val *= 10;
619 val += (*p - '0');
620 }
621 }
622 p++;
623 }
624
625 /* Low digit part must be there. */
626 if (!digit || !separator)
627 goto error;
628
629 /* Encode result into extended community. */
630 if (dot)
631 ecomm_type = ECOMMUNITY_ENCODE_IP;
632 else if (as > BGP_AS_MAX)
633 ecomm_type = ECOMMUNITY_ENCODE_AS4;
634 else
635 ecomm_type = ECOMMUNITY_ENCODE_AS;
636 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
637 goto error;
638 *token = ecommunity_token_val;
639 return p;
640
641 error:
642 *token = ecommunity_token_unknown;
643 return p;
644 }
645
646 static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
647 int keyword_included,
648 bool is_ipv6_extcomm)
649 {
650 struct ecommunity *ecom = NULL;
651 enum ecommunity_token token = ecommunity_token_unknown;
652 struct ecommunity_val_ipv6 eval;
653 int keyword = 0;
654
655 if (is_ipv6_extcomm)
656 token = ecommunity_token_rt6;
657 while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
658 switch (token) {
659 case ecommunity_token_rt:
660 case ecommunity_token_soo:
661 if (!keyword_included || keyword) {
662 if (ecom)
663 ecommunity_free(&ecom);
664 return NULL;
665 }
666 keyword = 1;
667
668 if (token == ecommunity_token_rt ||
669 token == ecommunity_token_rt6) {
670 type = ECOMMUNITY_ROUTE_TARGET;
671 }
672 if (token == ecommunity_token_soo) {
673 type = ECOMMUNITY_SITE_ORIGIN;
674 }
675 break;
676 case ecommunity_token_val:
677 if (keyword_included) {
678 if (!keyword) {
679 ecommunity_free(&ecom);
680 return NULL;
681 }
682 keyword = 0;
683 }
684 if (ecom == NULL)
685 ecom = ecommunity_new();
686 eval.val[1] = type;
687 ecommunity_add_val_internal(ecom, (void *)&eval,
688 false, false,
689 ecom->unit_size);
690 break;
691 case ecommunity_token_val6:
692 if (keyword_included) {
693 if (!keyword) {
694 ecommunity_free(&ecom);
695 return NULL;
696 }
697 keyword = 0;
698 }
699 if (ecom == NULL)
700 ecom = ecommunity_new();
701 ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
702 eval.val[1] = type;
703 ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
704 ecom->unit_size);
705 break;
706 case ecommunity_token_unknown:
707 default:
708 if (ecom)
709 ecommunity_free(&ecom);
710 return NULL;
711 }
712 }
713 return ecom;
714 }
715
716 /* Convert string to extended community attribute.
717 *
718 * When type is already known, please specify both str and type. str
719 * should not include keyword such as "rt" and "soo". Type is
720 * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
721 * keyword_included should be zero.
722 *
723 * For example route-map's "set extcommunity" command case:
724 *
725 * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
726 * type = ECOMMUNITY_ROUTE_TARGET
727 * keyword_included = 0
728 *
729 * "soo 100:1" -> str = "100:1"
730 * type = ECOMMUNITY_SITE_ORIGIN
731 * keyword_included = 0
732 *
733 * When string includes keyword for each extended community value.
734 * Please specify keyword_included as non-zero value.
735 *
736 * For example standard extcommunity-list case:
737 *
738 * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
739 * type = 0
740 * keyword_include = 1
741 */
742 struct ecommunity *ecommunity_str2com(const char *str, int type,
743 int keyword_included)
744 {
745 return ecommunity_str2com_internal(str, type,
746 keyword_included, false);
747 }
748
749 struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
750 int keyword_included)
751 {
752 return ecommunity_str2com_internal(str, type,
753 keyword_included, true);
754 }
755
756 static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
757 const uint8_t *pnt, int type,
758 int sub_type, int format,
759 unsigned short ecom_size)
760 {
761 int len = 0;
762 const char *prefix;
763 char buf_local[INET6_ADDRSTRLEN];
764
765 /* For parse Extended Community attribute tupple. */
766 struct ecommunity_as eas;
767 struct ecommunity_ip eip;
768 struct ecommunity_ip6 eip6;
769
770 /* Determine prefix for string, if any. */
771 switch (format) {
772 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
773 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
774 break;
775 case ECOMMUNITY_FORMAT_DISPLAY:
776 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
777 break;
778 case ECOMMUNITY_FORMAT_ROUTE_MAP:
779 prefix = "";
780 break;
781 default:
782 prefix = "";
783 break;
784 }
785
786 /* Put string into buffer. */
787 if (type == ECOMMUNITY_ENCODE_AS4) {
788 pnt = ptr_get_be32(pnt, &eas.as);
789 eas.val = (*pnt++ << 8);
790 eas.val |= (*pnt++);
791
792 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
793 } else if (type == ECOMMUNITY_ENCODE_AS) {
794 if (ecom_size == ECOMMUNITY_SIZE) {
795 eas.as = (*pnt++ << 8);
796 eas.as |= (*pnt++);
797 pnt = ptr_get_be32(pnt, &eas.val);
798
799 len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
800 eas.val);
801 } else {
802 /* this is an IPv6 ext community
803 * first 16 bytes stands for IPv6 addres
804 */
805 memcpy(&eip6.ip, pnt, 16);
806 pnt += 16;
807 eip6.val = (*pnt++ << 8);
808 eip6.val |= (*pnt++);
809
810 inet_ntop(AF_INET6, &eip6.ip, buf_local,
811 sizeof(buf_local));
812 len = snprintf(buf, bufsz, "%s%s:%u", prefix,
813 buf_local, eip6.val);
814 }
815 } else if (type == ECOMMUNITY_ENCODE_IP) {
816 memcpy(&eip.ip, pnt, 4);
817 pnt += 4;
818 eip.val = (*pnt++ << 8);
819 eip.val |= (*pnt++);
820
821 len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip,
822 eip.val);
823 }
824
825 /* consume value */
826 (void)pnt;
827
828 return len;
829 }
830
831 static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
832 int type, int sub_type, int format)
833 {
834 return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
835 sub_type, format,
836 ECOMMUNITY_SIZE);
837 }
838
839 static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
840 {
841 int len = 0;
842 as_t as;
843 uint32_t bw;
844 char bps_buf[20] = {0};
845
846 #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
847 #define ONE_MBPS_BYTES (1000 * 1000 / 8)
848 #define ONE_KBPS_BYTES (1000 / 8)
849
850 as = (*pnt++ << 8);
851 as |= (*pnt++);
852 (void)ptr_get_be32(pnt, &bw);
853 if (bw >= ONE_GBPS_BYTES)
854 snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps",
855 (float)(bw / ONE_GBPS_BYTES));
856 else if (bw >= ONE_MBPS_BYTES)
857 snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps",
858 (float)(bw / ONE_MBPS_BYTES));
859 else if (bw >= ONE_KBPS_BYTES)
860 snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps",
861 (float)(bw / ONE_KBPS_BYTES));
862 else
863 snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8);
864
865 len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
866 return len;
867 }
868
869 /* Convert extended community attribute to string.
870
871 Due to historical reason of industry standard implementation, there
872 are three types of format.
873
874 route-map set extcommunity format
875 "rt 100:1 100:2soo 100:3"
876
877 extcommunity-list
878 "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
879 "RT:100:1 RT:100:2 SoO:100:3"
880
881 For each formath please use below definition for format:
882
883 ECOMMUNITY_FORMAT_ROUTE_MAP
884 ECOMMUNITY_FORMAT_COMMUNITY_LIST
885 ECOMMUNITY_FORMAT_DISPLAY
886
887 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
888 0 value displays all
889 */
890 char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
891 {
892 uint32_t i;
893 uint8_t *pnt;
894 uint8_t type = 0;
895 uint8_t sub_type = 0;
896 int str_size;
897 char *str_buf;
898
899 if (!ecom || ecom->size == 0)
900 return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
901
902 /* ecom strlen + space + null term */
903 str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
904 str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
905
906 char encbuf[128];
907
908 for (i = 0; i < ecom->size; i++) {
909 int unk_ecom = 0;
910 memset(encbuf, 0x00, sizeof(encbuf));
911
912 /* Space between each value. */
913 if (i > 0)
914 strlcat(str_buf, " ", str_size);
915
916 /* Retrieve value field */
917 pnt = ecom->val + (i * 8);
918
919 /* High-order octet is the type */
920 type = *pnt++;
921
922 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
923 || type == ECOMMUNITY_ENCODE_AS4) {
924 /* Low-order octet of type. */
925 sub_type = *pnt++;
926 if (sub_type != ECOMMUNITY_ROUTE_TARGET
927 && sub_type != ECOMMUNITY_SITE_ORIGIN) {
928 if (sub_type ==
929 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
930 type == ECOMMUNITY_ENCODE_IP) {
931 struct in_addr *ipv4 =
932 (struct in_addr *)pnt;
933 char ipv4str[INET_ADDRSTRLEN];
934
935 inet_ntop(AF_INET, ipv4,
936 ipv4str,
937 INET_ADDRSTRLEN);
938 snprintf(encbuf, sizeof(encbuf),
939 "NH:%s:%d", ipv4str, pnt[5]);
940 } else if (sub_type ==
941 ECOMMUNITY_LINK_BANDWIDTH &&
942 type == ECOMMUNITY_ENCODE_AS) {
943 ecommunity_lb_str(encbuf,
944 sizeof(encbuf), pnt);
945 } else
946 unk_ecom = 1;
947 } else {
948 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
949 pnt, type, sub_type,
950 format);
951 }
952 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
953 if (filter == ECOMMUNITY_ROUTE_TARGET)
954 continue;
955 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
956 uint16_t tunneltype;
957 memcpy(&tunneltype, pnt + 5, 2);
958 tunneltype = ntohs(tunneltype);
959
960 snprintf(encbuf, sizeof(encbuf), "ET:%d",
961 tunneltype);
962 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
963 strlcpy(encbuf, "Default Gateway",
964 sizeof(encbuf));
965 } else {
966 unk_ecom = 1;
967 }
968 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
969 if (filter == ECOMMUNITY_ROUTE_TARGET)
970 continue;
971 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
972 struct ethaddr rmac;
973 pnt++;
974 memcpy(&rmac, pnt, ETH_ALEN);
975
976 snprintf(encbuf, sizeof(encbuf),
977 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
978 (uint8_t)rmac.octet[0],
979 (uint8_t)rmac.octet[1],
980 (uint8_t)rmac.octet[2],
981 (uint8_t)rmac.octet[3],
982 (uint8_t)rmac.octet[4],
983 (uint8_t)rmac.octet[5]);
984 } else if (*pnt
985 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
986 uint32_t seqnum;
987 uint8_t flags = *++pnt;
988
989 memcpy(&seqnum, pnt + 2, 4);
990 seqnum = ntohl(seqnum);
991
992 snprintf(encbuf, sizeof(encbuf), "MM:%u",
993 seqnum);
994
995 if (CHECK_FLAG(
996 flags,
997 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
998 strlcat(encbuf, ", sticky MAC",
999 sizeof(encbuf));
1000 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1001 uint8_t flags = *++pnt;
1002
1003 if (CHECK_FLAG(
1004 flags,
1005 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1006 strlcpy(encbuf, "ND:Router Flag",
1007 sizeof(encbuf));
1008 if (CHECK_FLAG(
1009 flags,
1010 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1011 strlcpy(encbuf, "ND:Proxy",
1012 sizeof(encbuf));
1013 } else if (*pnt
1014 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1015 struct ethaddr mac;
1016
1017 pnt++;
1018 memcpy(&mac, pnt, ETH_ALEN);
1019 snprintf(encbuf,
1020 sizeof(encbuf),
1021 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1022 (uint8_t)mac.octet[0],
1023 (uint8_t)mac.octet[1],
1024 (uint8_t)mac.octet[2],
1025 (uint8_t)mac.octet[3],
1026 (uint8_t)mac.octet[4],
1027 (uint8_t)mac.octet[5]);
1028 } else if (*pnt
1029 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1030 uint8_t flags = *++pnt;
1031
1032 snprintf(encbuf,
1033 sizeof(encbuf), "ESI-label-Rt:%s",
1034 (flags &
1035 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1036 "SA":"AA");
1037 } else if (*pnt
1038 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1039 uint8_t alg;
1040 uint16_t pref;
1041 uint16_t bmap;
1042
1043 alg = *(pnt + 1);
1044 memcpy(&bmap, pnt + 2, 2);
1045 bmap = ntohs(bmap);
1046 memcpy(&pref, pnt + 5, 2);
1047 pref = ntohs(pref);
1048
1049 if (bmap)
1050 snprintf(
1051 encbuf, sizeof(encbuf),
1052 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1053 alg, bmap, pref);
1054 else
1055 snprintf(encbuf, sizeof(encbuf),
1056 "DF: (alg: %u, pref: %u)", alg,
1057 pref);
1058 } else
1059 unk_ecom = 1;
1060 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1061 sub_type = *pnt++;
1062 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
1063 snprintf(encbuf, sizeof(encbuf),
1064 "FS:redirect IP 0x%x", *(pnt + 5));
1065 } else
1066 unk_ecom = 1;
1067 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1068 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1069 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
1070 sub_type = *pnt++;
1071
1072 if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
1073 char buf[ECOMMUNITY_STRLEN];
1074
1075 memset(buf, 0, sizeof(buf));
1076 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1077 (const uint8_t *)pnt,
1078 type &
1079 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1080 ECOMMUNITY_ROUTE_TARGET,
1081 format,
1082 ecom->unit_size);
1083 snprintf(encbuf, sizeof(encbuf), "%s", buf);
1084 } else if (sub_type ==
1085 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
1086 char buf[64];
1087
1088 memset(buf, 0, sizeof(buf));
1089 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1090 (const uint8_t *)pnt,
1091 type &
1092 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1093 ECOMMUNITY_ROUTE_TARGET,
1094 ECOMMUNITY_FORMAT_DISPLAY,
1095 ecom->unit_size);
1096 snprintf(encbuf, sizeof(encbuf),
1097 "FS:redirect VRF %s", buf);
1098 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1099 char buf[16];
1100
1101 memset(buf, 0, sizeof(buf));
1102 ecommunity_rt_soo_str(buf, sizeof(buf),
1103 (const uint8_t *)pnt,
1104 type &
1105 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1106 ECOMMUNITY_ROUTE_TARGET,
1107 ECOMMUNITY_FORMAT_DISPLAY);
1108 snprintf(encbuf, sizeof(encbuf),
1109 "FS:redirect VRF %s", buf);
1110 snprintf(encbuf, sizeof(encbuf),
1111 "FS:redirect VRF %s", buf);
1112 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1113 unk_ecom = 1;
1114 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
1115 char action[64];
1116
1117 if (*(pnt+3) ==
1118 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
1119 strlcpy(action, "terminate (apply)",
1120 sizeof(action));
1121 else
1122 strlcpy(action, "eval stops",
1123 sizeof(action));
1124
1125 if (*(pnt+3) ==
1126 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1127 strlcat(action, ", sample",
1128 sizeof(action));
1129
1130
1131 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1132 action);
1133 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1134 union traffic_rate data;
1135
1136 data.rate_byte[3] = *(pnt+2);
1137 data.rate_byte[2] = *(pnt+3);
1138 data.rate_byte[1] = *(pnt+4);
1139 data.rate_byte[0] = *(pnt+5);
1140 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1141 data.rate_float);
1142 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
1143 snprintf(encbuf, sizeof(encbuf),
1144 "FS:marking %u", *(pnt + 5));
1145 } else
1146 unk_ecom = 1;
1147 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1148 sub_type = *pnt++;
1149 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
1150 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt);
1151 else
1152 unk_ecom = 1;
1153 } else {
1154 sub_type = *pnt++;
1155 unk_ecom = 1;
1156 }
1157
1158 if (unk_ecom)
1159 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1160 sub_type);
1161
1162 int r = strlcat(str_buf, encbuf, str_size);
1163 assert(r < str_size);
1164 }
1165
1166 return str_buf;
1167 }
1168
1169 bool ecommunity_match(const struct ecommunity *ecom1,
1170 const struct ecommunity *ecom2)
1171 {
1172 uint32_t i = 0;
1173 uint32_t j = 0;
1174
1175 if (ecom1 == NULL && ecom2 == NULL)
1176 return true;
1177
1178 if (ecom1 == NULL || ecom2 == NULL)
1179 return false;
1180
1181 if (ecom1->size < ecom2->size)
1182 return false;
1183
1184 /* Every community on com2 needs to be on com1 for this to match */
1185 while (i < ecom1->size && j < ecom2->size) {
1186 if (memcmp(ecom1->val + i * ecom1->unit_size,
1187 ecom2->val + j * ecom2->unit_size,
1188 ecom2->unit_size)
1189 == 0)
1190 j++;
1191 i++;
1192 }
1193
1194 if (j == ecom2->size)
1195 return true;
1196 else
1197 return false;
1198 }
1199
1200 /* return first occurence of type */
1201 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1202 uint8_t type, uint8_t subtype)
1203 {
1204 uint8_t *p;
1205 uint32_t c;
1206
1207 /* If the value already exists in the structure return 0. */
1208 c = 0;
1209 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1210 if (p == NULL) {
1211 continue;
1212 }
1213 if (p[0] == type && p[1] == subtype)
1214 return (struct ecommunity_val *)p;
1215 }
1216 return NULL;
1217 }
1218
1219 /* remove ext. community matching type and subtype
1220 * return 1 on success ( removed ), 0 otherwise (not present)
1221 */
1222 bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1223 uint8_t subtype)
1224 {
1225 uint8_t *p, *q, *new;
1226 uint32_t c, found = 0;
1227 /* When this is fist value, just add it. */
1228 if (ecom == NULL || ecom->val == NULL)
1229 return false;
1230
1231 /* Check if any existing ext community matches. */
1232 /* Certain extended communities like the Route Target can be present
1233 * multiple times, handle that.
1234 */
1235 c = 0;
1236 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1237 if (p[0] == type && p[1] == subtype)
1238 found++;
1239 }
1240 /* If no matching ext community exists, return. */
1241 if (found == 0)
1242 return false;
1243
1244 /* Handle the case where everything needs to be stripped. */
1245 if (found == ecom->size) {
1246 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1247 ecom->size = 0;
1248 return true;
1249 }
1250
1251 /* Strip matching ext community(ies). */
1252 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1253 (ecom->size - found) * ecom->unit_size);
1254 q = new;
1255 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
1256 if (!(p[0] == type && p[1] == subtype)) {
1257 memcpy(q, p, ecom->unit_size);
1258 q += ecom->unit_size;
1259 }
1260 }
1261 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1262 ecom->val = new;
1263 ecom->size -= found;
1264 return true;
1265 }
1266
1267 /*
1268 * Remove specified extended community value from extended community.
1269 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1270 */
1271 bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
1272 {
1273 uint8_t *p;
1274 uint32_t c, found = 0;
1275
1276 /* Make sure specified value exists. */
1277 if (ecom == NULL || ecom->val == NULL)
1278 return false;
1279 c = 0;
1280 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1281 if (!memcmp(p, eval->val, ecom->unit_size)) {
1282 found = 1;
1283 break;
1284 }
1285 }
1286 if (found == 0)
1287 return false;
1288
1289 /* Delete the selected value */
1290 ecom->size--;
1291 if (ecom->size) {
1292 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1293 if (c != 0)
1294 memcpy(p, ecom->val, c * ecom->unit_size);
1295 if ((ecom->size - c) != 0)
1296 memcpy(p + (c)*ecom->unit_size,
1297 ecom->val + (c + 1) * ecom->unit_size,
1298 (ecom->size - c) * ecom->unit_size);
1299 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1300 ecom->val = p;
1301 } else
1302 ecom->val = NULL;
1303
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 uint32_t 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 }