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