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