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