]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #12205 from proelbtn/fix-ipv4-vpn-nexthop-over-ipv6-peer
[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 char ipv4str[INET_ADDRSTRLEN];
992
993 inet_ntop(AF_INET, ipv4,
994 ipv4str,
995 INET_ADDRSTRLEN);
996 snprintf(encbuf, sizeof(encbuf),
997 "NH:%s:%d", ipv4str, pnt[5]);
998 } else if (sub_type ==
999 ECOMMUNITY_LINK_BANDWIDTH &&
1000 type == ECOMMUNITY_ENCODE_AS) {
1001 ecommunity_lb_str(
1002 encbuf, sizeof(encbuf), pnt,
1003 ecom->disable_ieee_floating);
1004 } else
1005 unk_ecom = 1;
1006 } else {
1007 ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
1008 pnt, type, sub_type,
1009 format);
1010 }
1011 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
1012 if (filter == ECOMMUNITY_ROUTE_TARGET)
1013 continue;
1014 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
1015 uint16_t tunneltype;
1016 memcpy(&tunneltype, pnt + 5, 2);
1017 tunneltype = ntohs(tunneltype);
1018
1019 snprintf(encbuf, sizeof(encbuf), "ET:%d",
1020 tunneltype);
1021 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
1022 strlcpy(encbuf, "Default Gateway",
1023 sizeof(encbuf));
1024 } else {
1025 unk_ecom = 1;
1026 }
1027 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
1028 if (filter == ECOMMUNITY_ROUTE_TARGET)
1029 continue;
1030 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
1031 struct ethaddr rmac;
1032 pnt++;
1033 memcpy(&rmac, pnt, ETH_ALEN);
1034
1035 snprintf(encbuf, sizeof(encbuf),
1036 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
1037 (uint8_t)rmac.octet[0],
1038 (uint8_t)rmac.octet[1],
1039 (uint8_t)rmac.octet[2],
1040 (uint8_t)rmac.octet[3],
1041 (uint8_t)rmac.octet[4],
1042 (uint8_t)rmac.octet[5]);
1043 } else if (*pnt
1044 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
1045 uint32_t seqnum;
1046 uint8_t flags = *++pnt;
1047
1048 memcpy(&seqnum, pnt + 2, 4);
1049 seqnum = ntohl(seqnum);
1050
1051 snprintf(encbuf, sizeof(encbuf), "MM:%u",
1052 seqnum);
1053
1054 if (CHECK_FLAG(
1055 flags,
1056 ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
1057 strlcat(encbuf, ", sticky MAC",
1058 sizeof(encbuf));
1059 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
1060 uint8_t flags = *++pnt;
1061
1062 if (CHECK_FLAG(
1063 flags,
1064 ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
1065 strlcpy(encbuf, "ND:Router Flag",
1066 sizeof(encbuf));
1067 if (CHECK_FLAG(
1068 flags,
1069 ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
1070 strlcpy(encbuf, "ND:Proxy",
1071 sizeof(encbuf));
1072 } else if (*pnt
1073 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
1074 struct ethaddr mac;
1075
1076 pnt++;
1077 memcpy(&mac, pnt, ETH_ALEN);
1078 snprintf(encbuf,
1079 sizeof(encbuf),
1080 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
1081 (uint8_t)mac.octet[0],
1082 (uint8_t)mac.octet[1],
1083 (uint8_t)mac.octet[2],
1084 (uint8_t)mac.octet[3],
1085 (uint8_t)mac.octet[4],
1086 (uint8_t)mac.octet[5]);
1087 } else if (*pnt
1088 == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
1089 uint8_t flags = *++pnt;
1090
1091 snprintf(encbuf,
1092 sizeof(encbuf), "ESI-label-Rt:%s",
1093 (flags &
1094 ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
1095 "SA":"AA");
1096 } else if (*pnt
1097 == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
1098 uint8_t alg;
1099 uint16_t pref;
1100 uint16_t bmap;
1101
1102 alg = *(pnt + 1);
1103 memcpy(&bmap, pnt + 2, 2);
1104 bmap = ntohs(bmap);
1105 memcpy(&pref, pnt + 5, 2);
1106 pref = ntohs(pref);
1107
1108 if (bmap)
1109 snprintf(
1110 encbuf, sizeof(encbuf),
1111 "DF: (alg: %u, bmap: 0x%x pref: %u)",
1112 alg, bmap, pref);
1113 else
1114 snprintf(encbuf, sizeof(encbuf),
1115 "DF: (alg: %u, pref: %u)", alg,
1116 pref);
1117 } else
1118 unk_ecom = 1;
1119 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
1120 sub_type = *pnt++;
1121 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
1122 snprintf(encbuf, sizeof(encbuf),
1123 "FS:redirect IP 0x%x", *(pnt + 5));
1124 } else
1125 unk_ecom = 1;
1126 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
1127 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
1128 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
1129 sub_type = *pnt++;
1130
1131 if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
1132 char buf[ECOMMUNITY_STRLEN];
1133
1134 memset(buf, 0, sizeof(buf));
1135 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1136 (const uint8_t *)pnt,
1137 type &
1138 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1139 ECOMMUNITY_ROUTE_TARGET,
1140 format,
1141 ecom->unit_size);
1142 snprintf(encbuf, sizeof(encbuf), "%s", buf);
1143 } else if (sub_type ==
1144 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
1145 char buf[64];
1146
1147 memset(buf, 0, sizeof(buf));
1148 ecommunity_rt_soo_str_internal(buf, sizeof(buf),
1149 (const uint8_t *)pnt,
1150 type &
1151 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1152 ECOMMUNITY_ROUTE_TARGET,
1153 ECOMMUNITY_FORMAT_DISPLAY,
1154 ecom->unit_size);
1155 snprintf(encbuf, sizeof(encbuf),
1156 "FS:redirect VRF %s", buf);
1157 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
1158 char buf[16];
1159
1160 memset(buf, 0, sizeof(buf));
1161 ecommunity_rt_soo_str(buf, sizeof(buf),
1162 (const uint8_t *)pnt,
1163 type &
1164 ~ECOMMUNITY_ENCODE_TRANS_EXP,
1165 ECOMMUNITY_ROUTE_TARGET,
1166 ECOMMUNITY_FORMAT_DISPLAY);
1167 snprintf(encbuf, sizeof(encbuf),
1168 "FS:redirect VRF %s", buf);
1169 snprintf(encbuf, sizeof(encbuf),
1170 "FS:redirect VRF %s", buf);
1171 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
1172 unk_ecom = 1;
1173 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
1174 char action[64];
1175
1176 if (*(pnt+3) ==
1177 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
1178 strlcpy(action, "terminate (apply)",
1179 sizeof(action));
1180 else
1181 strlcpy(action, "eval stops",
1182 sizeof(action));
1183
1184 if (*(pnt+3) ==
1185 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1186 strlcat(action, ", sample",
1187 sizeof(action));
1188
1189
1190 snprintf(encbuf, sizeof(encbuf), "FS:action %s",
1191 action);
1192 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
1193 union traffic_rate data;
1194
1195 data.rate_byte[3] = *(pnt+2);
1196 data.rate_byte[2] = *(pnt+3);
1197 data.rate_byte[1] = *(pnt+4);
1198 data.rate_byte[0] = *(pnt+5);
1199 snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
1200 data.rate_float);
1201 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
1202 snprintf(encbuf, sizeof(encbuf),
1203 "FS:marking %u", *(pnt + 5));
1204 } else
1205 unk_ecom = 1;
1206 } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
1207 sub_type = *pnt++;
1208 if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
1209 ecommunity_lb_str(encbuf, sizeof(encbuf), pnt,
1210 ecom->disable_ieee_floating);
1211 else
1212 unk_ecom = 1;
1213 } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
1214 sub_type = *pnt++;
1215 if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
1216 ecommunity_origin_validation_state_str(
1217 encbuf, sizeof(encbuf), pnt);
1218 else
1219 unk_ecom = 1;
1220 } else {
1221 sub_type = *pnt++;
1222 unk_ecom = 1;
1223 }
1224
1225 if (unk_ecom)
1226 snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
1227 sub_type);
1228
1229 int r = strlcat(str_buf, encbuf, str_size);
1230 assert(r < str_size);
1231 }
1232
1233 return str_buf;
1234 }
1235
1236 bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2)
1237 {
1238 uint32_t i, j;
1239
1240 if (!e1 || !e2)
1241 return false;
1242 for (i = 0; i < e1->size; ++i) {
1243 for (j = 0; j < e2->size; ++j) {
1244 if (!memcmp(e1->val + (i * e1->unit_size),
1245 e2->val + (j * e2->unit_size),
1246 e1->unit_size))
1247 return true;
1248 }
1249 }
1250 return false;
1251 }
1252
1253 bool ecommunity_match(const struct ecommunity *ecom1,
1254 const struct ecommunity *ecom2)
1255 {
1256 uint32_t i = 0;
1257 uint32_t j = 0;
1258
1259 if (ecom1 == NULL && ecom2 == NULL)
1260 return true;
1261
1262 if (ecom1 == NULL || ecom2 == NULL)
1263 return false;
1264
1265 if (ecom1->size < ecom2->size)
1266 return false;
1267
1268 /* Every community on com2 needs to be on com1 for this to match */
1269 while (i < ecom1->size && j < ecom2->size) {
1270 if (memcmp(ecom1->val + i * ecom1->unit_size,
1271 ecom2->val + j * ecom2->unit_size,
1272 ecom2->unit_size)
1273 == 0)
1274 j++;
1275 i++;
1276 }
1277
1278 if (j == ecom2->size)
1279 return true;
1280 else
1281 return false;
1282 }
1283
1284 /* return first occurence of type */
1285 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
1286 uint8_t type, uint8_t subtype)
1287 {
1288 uint8_t *p;
1289 uint32_t c;
1290
1291 /* If the value already exists in the structure return 0. */
1292 c = 0;
1293 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1294 if (p == NULL) {
1295 continue;
1296 }
1297 if (p[0] == type && p[1] == subtype)
1298 return (struct ecommunity_val *)p;
1299 }
1300 return NULL;
1301 }
1302
1303 /* remove ext. community matching type and subtype
1304 * return 1 on success ( removed ), 0 otherwise (not present)
1305 */
1306 bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
1307 uint8_t subtype)
1308 {
1309 uint8_t *p, *q, *new;
1310 uint32_t c, found = 0;
1311 /* When this is fist value, just add it. */
1312 if (ecom == NULL || ecom->val == NULL)
1313 return false;
1314
1315 /* Check if any existing ext community matches. */
1316 /* Certain extended communities like the Route Target can be present
1317 * multiple times, handle that.
1318 */
1319 c = 0;
1320 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1321 if (p[0] == type && p[1] == subtype)
1322 found++;
1323 }
1324 /* If no matching ext community exists, return. */
1325 if (found == 0)
1326 return false;
1327
1328 /* Handle the case where everything needs to be stripped. */
1329 if (found == ecom->size) {
1330 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1331 ecom->size = 0;
1332 return true;
1333 }
1334
1335 /* Strip matching ext community(ies). */
1336 new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
1337 (ecom->size - found) * ecom->unit_size);
1338 q = new;
1339 for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
1340 if (!(p[0] == type && p[1] == subtype)) {
1341 memcpy(q, p, ecom->unit_size);
1342 q += ecom->unit_size;
1343 }
1344 }
1345 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1346 ecom->val = new;
1347 ecom->size -= found;
1348 return true;
1349 }
1350
1351 /*
1352 * Remove specified extended community value from extended community.
1353 * Returns 1 if value was present (and hence, removed), 0 otherwise.
1354 */
1355 bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
1356 {
1357 uint8_t *p;
1358 uint32_t c, found = 0;
1359
1360 /* Make sure specified value exists. */
1361 if (ecom == NULL || ecom->val == NULL)
1362 return false;
1363 c = 0;
1364 for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
1365 if (!memcmp(p, eval->val, ecom->unit_size)) {
1366 found = 1;
1367 break;
1368 }
1369 }
1370 if (found == 0)
1371 return false;
1372
1373 /* Delete the selected value */
1374 ecom->size--;
1375 if (ecom->size) {
1376 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
1377 if (c != 0)
1378 memcpy(p, ecom->val, c * ecom->unit_size);
1379 if ((ecom->size - c) != 0)
1380 memcpy(p + (c)*ecom->unit_size,
1381 ecom->val + (c + 1) * ecom->unit_size,
1382 (ecom->size - c) * ecom->unit_size);
1383 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1384 ecom->val = p;
1385 } else
1386 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
1387
1388 return true;
1389 }
1390
1391 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
1392 struct bgp_pbr_entry_action *api,
1393 afi_t afi)
1394 {
1395 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
1396 api->action = ACTION_TRAFFICRATE;
1397 api->u.r.rate_info[3] = ecom_eval->val[4];
1398 api->u.r.rate_info[2] = ecom_eval->val[5];
1399 api->u.r.rate_info[1] = ecom_eval->val[6];
1400 api->u.r.rate_info[0] = ecom_eval->val[7];
1401 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
1402 api->action = ACTION_TRAFFIC_ACTION;
1403 /* else distribute code is set by default */
1404 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
1405 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
1406 else
1407 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
1408 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
1409 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
1410
1411 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1412 api->action = ACTION_MARKING;
1413 api->u.marking_dscp = ecom_eval->val[7];
1414 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1415 /* must use external function */
1416 return 0;
1417 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
1418 afi == AFI_IP) {
1419 /* see draft-ietf-idr-flowspec-redirect-ip-02
1420 * Q1: how come a ext. community can host ipv6 address
1421 * Q2 : from cisco documentation:
1422 * Announces the reachability of one or more flowspec NLRI.
1423 * When a BGP speaker receives an UPDATE message with the
1424 * redirect-to-IP extended community, it is expected to
1425 * create a traffic filtering rule for every flow-spec
1426 * NLRI in the message that has this path as its best
1427 * path. The filter entry matches the IP packets
1428 * described in the NLRI field and redirects them or
1429 * copies them towards the IPv4 or IPv6 address specified
1430 * in the 'Network Address of Next- Hop'
1431 * field of the associated MP_REACH_NLRI.
1432 */
1433 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1434 ecom_eval + 2;
1435
1436 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1437 } else
1438 return -1;
1439 return 0;
1440 }
1441
1442 static struct ecommunity *bgp_aggr_ecommunity_lookup(
1443 struct bgp_aggregate *aggregate,
1444 struct ecommunity *ecommunity)
1445 {
1446 return hash_lookup(aggregate->ecommunity_hash, ecommunity);
1447 }
1448
1449 static void *bgp_aggr_ecommunty_hash_alloc(void *p)
1450 {
1451 struct ecommunity *ref = (struct ecommunity *)p;
1452 struct ecommunity *ecommunity = NULL;
1453
1454 ecommunity = ecommunity_dup(ref);
1455 return ecommunity;
1456 }
1457
1458 static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
1459 {
1460 struct ecommunity *hb_ecommunity = hb->data;
1461 struct ecommunity **aggr_ecommunity = arg;
1462
1463 if (*aggr_ecommunity)
1464 *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
1465 hb_ecommunity);
1466 else
1467 *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
1468 }
1469
1470 void bgp_aggr_ecommunity_remove(void *arg)
1471 {
1472 struct ecommunity *ecommunity = arg;
1473
1474 ecommunity_free(&ecommunity);
1475 }
1476
1477 void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
1478 struct ecommunity *ecommunity)
1479 {
1480 bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
1481 bgp_compute_aggregate_ecommunity_val(aggregate);
1482 }
1483
1484
1485 void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
1486 struct ecommunity *ecommunity)
1487 {
1488 struct ecommunity *aggr_ecommunity = NULL;
1489
1490 if ((aggregate == NULL) || (ecommunity == NULL))
1491 return;
1492
1493 /* Create hash if not already created.
1494 */
1495 if (aggregate->ecommunity_hash == NULL)
1496 aggregate->ecommunity_hash = hash_create(
1497 ecommunity_hash_make, ecommunity_cmp,
1498 "BGP Aggregator ecommunity hash");
1499
1500 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1501 if (aggr_ecommunity == NULL) {
1502 /* Insert ecommunity into hash.
1503 */
1504 aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
1505 ecommunity,
1506 bgp_aggr_ecommunty_hash_alloc);
1507 }
1508
1509 /* Increment reference counter.
1510 */
1511 aggr_ecommunity->refcnt++;
1512 }
1513
1514 void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
1515 {
1516 struct ecommunity *ecommerge = NULL;
1517
1518 if (aggregate == NULL)
1519 return;
1520
1521 /* Re-compute aggregate's ecommunity.
1522 */
1523 if (aggregate->ecommunity)
1524 ecommunity_free(&aggregate->ecommunity);
1525 if (aggregate->ecommunity_hash
1526 && aggregate->ecommunity_hash->count) {
1527 hash_iterate(aggregate->ecommunity_hash,
1528 bgp_aggr_ecommunity_prepare,
1529 &aggregate->ecommunity);
1530 ecommerge = aggregate->ecommunity;
1531 aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
1532 if (ecommerge)
1533 ecommunity_free(&ecommerge);
1534 }
1535 }
1536
1537 void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
1538 struct ecommunity *ecommunity)
1539 {
1540 struct ecommunity *aggr_ecommunity = NULL;
1541 struct ecommunity *ret_ecomm = NULL;
1542
1543 if ((!aggregate)
1544 || (!aggregate->ecommunity_hash)
1545 || (!ecommunity))
1546 return;
1547
1548 /* Look-up the ecommunity in the hash.
1549 */
1550 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1551 if (aggr_ecommunity) {
1552 aggr_ecommunity->refcnt--;
1553
1554 if (aggr_ecommunity->refcnt == 0) {
1555 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1556 aggr_ecommunity);
1557 ecommunity_free(&ret_ecomm);
1558 bgp_compute_aggregate_ecommunity_val(aggregate);
1559 }
1560 }
1561 }
1562
1563 void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
1564 struct ecommunity *ecommunity)
1565 {
1566
1567 struct ecommunity *aggr_ecommunity = NULL;
1568 struct ecommunity *ret_ecomm = NULL;
1569
1570 if ((!aggregate)
1571 || (!aggregate->ecommunity_hash)
1572 || (!ecommunity))
1573 return;
1574
1575 /* Look-up the ecommunity in the hash.
1576 */
1577 aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
1578 if (aggr_ecommunity) {
1579 aggr_ecommunity->refcnt--;
1580
1581 if (aggr_ecommunity->refcnt == 0) {
1582 ret_ecomm = hash_release(aggregate->ecommunity_hash,
1583 aggr_ecommunity);
1584 ecommunity_free(&ret_ecomm);
1585 }
1586 }
1587 }
1588
1589 struct ecommunity *
1590 ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
1591 struct ecommunity *old)
1592 {
1593 struct ecommunity *new = NULL;
1594 struct ecommunity ovs_ecomm = {0};
1595 struct ecommunity_val ovs_eval;
1596
1597 encode_origin_validation_state(rpki_state, &ovs_eval);
1598
1599 if (old) {
1600 new = ecommunity_dup(old);
1601 ecommunity_add_val(new, &ovs_eval, true, true);
1602 if (!old->refcnt)
1603 ecommunity_free(&old);
1604 } else {
1605 ovs_ecomm.size = 1;
1606 ovs_ecomm.unit_size = ECOMMUNITY_SIZE;
1607 ovs_ecomm.val = (uint8_t *)&ovs_eval.val;
1608 new = ecommunity_dup(&ovs_ecomm);
1609 }
1610
1611 return new;
1612 }
1613
1614 /*
1615 * return the BGP link bandwidth extended community, if present;
1616 * the actual bandwidth is returned via param
1617 */
1618 const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
1619 {
1620 const uint8_t *eval;
1621 uint32_t i;
1622
1623 if (bw)
1624 *bw = 0;
1625
1626 if (!ecom || !ecom->size)
1627 return NULL;
1628
1629 for (i = 0; i < ecom->size; i++) {
1630 const uint8_t *pnt;
1631 uint8_t type, sub_type;
1632 uint32_t bwval;
1633
1634 eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
1635 type = *pnt++;
1636 sub_type = *pnt++;
1637
1638 if ((type == ECOMMUNITY_ENCODE_AS ||
1639 type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
1640 sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
1641 pnt += 2; /* bandwidth is encoded as AS:val */
1642 pnt = ptr_get_be32(pnt, &bwval);
1643 (void)pnt; /* consume value */
1644 if (bw)
1645 *bw = ecom->disable_ieee_floating
1646 ? bwval
1647 : ieee_float_uint32_to_uint32(
1648 bwval);
1649 return eval;
1650 }
1651 }
1652
1653 return NULL;
1654 }
1655
1656
1657 struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
1658 uint64_t cum_bw,
1659 bool disable_ieee_floating)
1660 {
1661 struct ecommunity *new;
1662 struct ecommunity_val lb_eval;
1663 const uint8_t *eval;
1664 uint8_t type;
1665 uint32_t cur_bw;
1666
1667 /* Nothing to replace if link-bandwidth doesn't exist or
1668 * is non-transitive - just return existing extcommunity.
1669 */
1670 new = ecom;
1671 if (!ecom || !ecom->size)
1672 return new;
1673
1674 eval = ecommunity_linkbw_present(ecom, &cur_bw);
1675 if (!eval)
1676 return new;
1677
1678 type = *eval;
1679 if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
1680 return new;
1681
1682 /* Transitive link-bandwidth exists, replace with the passed
1683 * (cumulative) bandwidth value. We need to create a new
1684 * extcommunity for this - refer to AS-Path replace function
1685 * for reference.
1686 */
1687 if (cum_bw > 0xFFFFFFFF)
1688 cum_bw = 0xFFFFFFFF;
1689 encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
1690 &lb_eval, disable_ieee_floating);
1691 new = ecommunity_dup(ecom);
1692 ecommunity_add_val(new, &lb_eval, true, true);
1693
1694 return new;
1695 }