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