]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #3162 from pguibert6WIND/vpn_route_map_issue
[mirror_frr.git] / bgpd / bgp_ecommunity.c
1 /* BGP Extended Communities Attribute
2 * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include "hash.h"
24 #include "memory.h"
25 #include "prefix.h"
26 #include "command.h"
27 #include "queue.h"
28 #include "filter.h"
29 #include "jhash.h"
30 #include "stream.h"
31
32 #include "bgpd/bgpd.h"
33 #include "bgpd/bgp_ecommunity.h"
34 #include "bgpd/bgp_lcommunity.h"
35 #include "bgpd/bgp_aspath.h"
36 #include "bgpd/bgp_flowspec_private.h"
37 #include "bgpd/bgp_pbr.h"
38
39 /* struct used to dump the rate contained in FS set traffic-rate EC */
40 union traffic_rate {
41 float rate_float;
42 uint8_t rate_byte[4];
43 };
44
45 /* Hash of community attribute. */
46 static struct hash *ecomhash;
47
48 /* Allocate a new ecommunities. */
49 struct ecommunity *ecommunity_new(void)
50 {
51 return (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
52 sizeof(struct ecommunity));
53 }
54
55 void ecommunity_strfree(char **s)
56 {
57 XFREE(MTYPE_ECOMMUNITY_STR, *s);
58 }
59
60 /* Allocate ecommunities. */
61 void ecommunity_free(struct ecommunity **ecom)
62 {
63 if ((*ecom)->val)
64 XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
65 if ((*ecom)->str)
66 XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
67 XFREE(MTYPE_ECOMMUNITY, *ecom);
68 }
69
70 static void ecommunity_hash_free(struct ecommunity *ecom)
71 {
72 ecommunity_free(&ecom);
73 }
74
75
76 /* Add a new Extended Communities value to Extended Communities
77 Attribute structure. When the value is already exists in the
78 structure, we don't add the value. Newly added value is sorted by
79 numerical order. When the value is added to the structure return 1
80 else return 0. */
81 int ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval)
82 {
83 uint8_t *p;
84 int ret;
85 int c;
86
87 /* When this is fist value, just add it. */
88 if (ecom->val == NULL) {
89 ecom->size++;
90 ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom_length(ecom));
91 memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
92 return 1;
93 }
94
95 /* If the value already exists in the structure return 0. */
96 c = 0;
97 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
98 ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
99 if (ret == 0)
100 return 0;
101 if (ret > 0)
102 break;
103 }
104
105 /* Add the value to the structure with numerical sorting. */
106 ecom->size++;
107 ecom->val =
108 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length(ecom));
109
110 memmove(ecom->val + (c + 1) * ECOMMUNITY_SIZE,
111 ecom->val + c * ECOMMUNITY_SIZE,
112 (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
113 memcpy(ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
114
115 return 1;
116 }
117
118 /* This function takes pointer to Extended Communites strucutre then
119 create a new Extended Communities structure by uniq and sort each
120 Extended Communities value. */
121 struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
122 {
123 int i;
124 struct ecommunity *new;
125 struct ecommunity_val *eval;
126
127 if (!ecom)
128 return NULL;
129
130 new = ecommunity_new();
131
132 for (i = 0; i < ecom->size; i++) {
133 eval = (struct ecommunity_val *)(ecom->val
134 + (i * ECOMMUNITY_SIZE));
135 ecommunity_add_val(new, eval);
136 }
137 return new;
138 }
139
140 /* Parse Extended Communites Attribute in BGP packet. */
141 struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
142 {
143 struct ecommunity tmp;
144 struct ecommunity *new;
145
146 /* Length check. */
147 if (length % ECOMMUNITY_SIZE)
148 return NULL;
149
150 /* Prepare tmporary structure for making a new Extended Communities
151 Attribute. */
152 tmp.size = length / ECOMMUNITY_SIZE;
153 tmp.val = pnt;
154
155 /* Create a new Extended Communities Attribute by uniq and sort each
156 Extended Communities value */
157 new = ecommunity_uniq_sort(&tmp);
158
159 return ecommunity_intern(new);
160 }
161
162 /* Duplicate the Extended Communities Attribute structure. */
163 struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
164 {
165 struct ecommunity *new;
166
167 new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
168 new->size = ecom->size;
169 if (new->size) {
170 new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
171 ecom->size * ECOMMUNITY_SIZE);
172 memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
173 } else
174 new->val = NULL;
175 return new;
176 }
177
178 /* Retrun string representation of communities attribute. */
179 char *ecommunity_str(struct ecommunity *ecom)
180 {
181 if (!ecom->str)
182 ecom->str =
183 ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
184 return ecom->str;
185 }
186
187 /* Merge two Extended Communities Attribute structure. */
188 struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
189 struct ecommunity *ecom2)
190 {
191 if (ecom1->val)
192 ecom1->val =
193 XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
194 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
195 else
196 ecom1->val =
197 XMALLOC(MTYPE_ECOMMUNITY_VAL,
198 (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
199
200 memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
201 ecom2->size * ECOMMUNITY_SIZE);
202 ecom1->size += ecom2->size;
203
204 return ecom1;
205 }
206
207 /* Intern Extended Communities Attribute. */
208 struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
209 {
210 struct ecommunity *find;
211
212 assert(ecom->refcnt == 0);
213
214 find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
215
216 if (find != ecom)
217 ecommunity_free(&ecom);
218
219 find->refcnt++;
220
221 if (!find->str)
222 find->str =
223 ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
224
225 return find;
226 }
227
228 /* Unintern Extended Communities Attribute. */
229 void ecommunity_unintern(struct ecommunity **ecom)
230 {
231 struct ecommunity *ret;
232
233 if ((*ecom)->refcnt)
234 (*ecom)->refcnt--;
235
236 /* Pull off from hash. */
237 if ((*ecom)->refcnt == 0) {
238 /* Extended community must be in the hash. */
239 ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
240 assert(ret != NULL);
241
242 ecommunity_free(ecom);
243 }
244 }
245
246 /* Utinity function to make hash key. */
247 unsigned int ecommunity_hash_make(void *arg)
248 {
249 const struct ecommunity *ecom = arg;
250 int size = ecom->size * ECOMMUNITY_SIZE;
251
252 return jhash(ecom->val, size, 0x564321ab);
253 }
254
255 /* Compare two Extended Communities Attribute structure. */
256 bool ecommunity_cmp(const void *arg1, const void *arg2)
257 {
258 const struct ecommunity *ecom1 = arg1;
259 const struct ecommunity *ecom2 = arg2;
260
261 if (ecom1 == NULL && ecom2 == NULL)
262 return true;
263
264 if (ecom1 == NULL || ecom2 == NULL)
265 return false;
266
267 return (ecom1->size == ecom2->size
268 && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
269 == 0);
270 }
271
272 /* Initialize Extended Comminities related hash. */
273 void ecommunity_init(void)
274 {
275 ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
276 "BGP ecommunity hash");
277 }
278
279 void ecommunity_finish(void)
280 {
281 hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
282 hash_free(ecomhash);
283 ecomhash = NULL;
284 }
285
286 /* Extended Communities token enum. */
287 enum ecommunity_token {
288 ecommunity_token_unknown = 0,
289 ecommunity_token_rt,
290 ecommunity_token_soo,
291 ecommunity_token_val,
292 };
293
294 /*
295 * Encode BGP extended community from passed values. Supports types
296 * defined in RFC 4360 and well-known sub-types.
297 */
298 static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
299 struct in_addr ip, uint32_t val,
300 struct ecommunity_val *eval)
301 {
302 assert(eval);
303 if (type == ECOMMUNITY_ENCODE_AS) {
304 if (as > BGP_AS_MAX)
305 return -1;
306 } else if (type == ECOMMUNITY_ENCODE_IP
307 || type == ECOMMUNITY_ENCODE_AS4) {
308 if (val > UINT16_MAX)
309 return -1;
310 }
311
312 /* Fill in the values. */
313 eval->val[0] = type;
314 if (!trans)
315 eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
316 eval->val[1] = sub_type;
317 if (type == ECOMMUNITY_ENCODE_AS) {
318 eval->val[2] = (as >> 8) & 0xff;
319 eval->val[3] = as & 0xff;
320 eval->val[4] = (val >> 24) & 0xff;
321 eval->val[5] = (val >> 16) & 0xff;
322 eval->val[6] = (val >> 8) & 0xff;
323 eval->val[7] = val & 0xff;
324 } else if (type == ECOMMUNITY_ENCODE_IP) {
325 memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
326 eval->val[6] = (val >> 8) & 0xff;
327 eval->val[7] = val & 0xff;
328 } else {
329 eval->val[2] = (as >> 24) & 0xff;
330 eval->val[3] = (as >> 16) & 0xff;
331 eval->val[4] = (as >> 8) & 0xff;
332 eval->val[5] = as & 0xff;
333 eval->val[6] = (val >> 8) & 0xff;
334 eval->val[7] = val & 0xff;
335 }
336
337 return 0;
338 }
339
340 /* Get next Extended Communities token from the string. */
341 static const char *ecommunity_gettoken(const char *str,
342 struct ecommunity_val *eval,
343 enum ecommunity_token *token)
344 {
345 int ret;
346 int dot = 0;
347 int digit = 0;
348 int separator = 0;
349 const char *p = str;
350 char *endptr;
351 struct in_addr ip;
352 as_t as = 0;
353 uint32_t val = 0;
354 uint8_t ecomm_type;
355 char buf[INET_ADDRSTRLEN + 1];
356
357 /* Skip white space. */
358 while (isspace((int)*p)) {
359 p++;
360 str++;
361 }
362
363 /* Check the end of the line. */
364 if (*p == '\0')
365 return NULL;
366
367 /* "rt" and "soo" keyword parse. */
368 if (!isdigit((int)*p)) {
369 /* "rt" match check. */
370 if (tolower((int)*p) == 'r') {
371 p++;
372 if (tolower((int)*p) == 't') {
373 p++;
374 *token = ecommunity_token_rt;
375 return p;
376 }
377 if (isspace((int)*p) || *p == '\0') {
378 *token = ecommunity_token_rt;
379 return p;
380 }
381 goto error;
382 }
383 /* "soo" match check. */
384 else if (tolower((int)*p) == 's') {
385 p++;
386 if (tolower((int)*p) == 'o') {
387 p++;
388 if (tolower((int)*p) == 'o') {
389 p++;
390 *token = ecommunity_token_soo;
391 return p;
392 }
393 if (isspace((int)*p) || *p == '\0') {
394 *token = ecommunity_token_soo;
395 return p;
396 }
397 goto error;
398 }
399 if (isspace((int)*p) || *p == '\0') {
400 *token = ecommunity_token_soo;
401 return p;
402 }
403 goto error;
404 }
405 goto error;
406 }
407
408 /* What a mess, there are several possibilities:
409 *
410 * a) A.B.C.D:MN
411 * b) EF:OPQR
412 * c) GHJK:MN
413 *
414 * A.B.C.D: Four Byte IP
415 * EF: Two byte ASN
416 * GHJK: Four-byte ASN
417 * MN: Two byte value
418 * OPQR: Four byte value
419 *
420 */
421 while (isdigit((int)*p) || *p == ':' || *p == '.') {
422 if (*p == ':') {
423 if (separator)
424 goto error;
425
426 separator = 1;
427 digit = 0;
428
429 if ((p - str) > INET_ADDRSTRLEN)
430 goto error;
431 memset(buf, 0, INET_ADDRSTRLEN + 1);
432 memcpy(buf, str, p - str);
433
434 if (dot) {
435 /* Parsing A.B.C.D in:
436 * A.B.C.D:MN
437 */
438 ret = inet_aton(buf, &ip);
439 if (ret == 0)
440 goto error;
441 } else {
442 /* ASN */
443 as = strtoul(buf, &endptr, 10);
444 if (*endptr != '\0' || as == BGP_AS4_MAX)
445 goto error;
446 }
447 } else if (*p == '.') {
448 if (separator)
449 goto error;
450 dot++;
451 if (dot > 4)
452 goto error;
453 } else {
454 digit = 1;
455
456 /* We're past the IP/ASN part */
457 if (separator) {
458 val *= 10;
459 val += (*p - '0');
460 }
461 }
462 p++;
463 }
464
465 /* Low digit part must be there. */
466 if (!digit || !separator)
467 goto error;
468
469 /* Encode result into extended community. */
470 if (dot)
471 ecomm_type = ECOMMUNITY_ENCODE_IP;
472 else if (as > BGP_AS_MAX)
473 ecomm_type = ECOMMUNITY_ENCODE_AS4;
474 else
475 ecomm_type = ECOMMUNITY_ENCODE_AS;
476 if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
477 goto error;
478 *token = ecommunity_token_val;
479 return p;
480
481 error:
482 *token = ecommunity_token_unknown;
483 return p;
484 }
485
486 /* Convert string to extended community attribute.
487
488 When type is already known, please specify both str and type. str
489 should not include keyword such as "rt" and "soo". Type is
490 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
491 keyword_included should be zero.
492
493 For example route-map's "set extcommunity" command case:
494
495 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
496 type = ECOMMUNITY_ROUTE_TARGET
497 keyword_included = 0
498
499 "soo 100:1" -> str = "100:1"
500 type = ECOMMUNITY_SITE_ORIGIN
501 keyword_included = 0
502
503 When string includes keyword for each extended community value.
504 Please specify keyword_included as non-zero value.
505
506 For example standard extcommunity-list case:
507
508 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
509 type = 0
510 keyword_include = 1
511 */
512 struct ecommunity *ecommunity_str2com(const char *str, int type,
513 int keyword_included)
514 {
515 struct ecommunity *ecom = NULL;
516 enum ecommunity_token token = ecommunity_token_unknown;
517 struct ecommunity_val eval;
518 int keyword = 0;
519
520 while ((str = ecommunity_gettoken(str, &eval, &token))) {
521 switch (token) {
522 case ecommunity_token_rt:
523 case ecommunity_token_soo:
524 if (!keyword_included || keyword) {
525 if (ecom)
526 ecommunity_free(&ecom);
527 return NULL;
528 }
529 keyword = 1;
530
531 if (token == ecommunity_token_rt) {
532 type = ECOMMUNITY_ROUTE_TARGET;
533 }
534 if (token == ecommunity_token_soo) {
535 type = ECOMMUNITY_SITE_ORIGIN;
536 }
537 break;
538 case ecommunity_token_val:
539 if (keyword_included) {
540 if (!keyword) {
541 if (ecom)
542 ecommunity_free(&ecom);
543 return NULL;
544 }
545 keyword = 0;
546 }
547 if (ecom == NULL)
548 ecom = ecommunity_new();
549 eval.val[1] = type;
550 ecommunity_add_val(ecom, &eval);
551 break;
552 case ecommunity_token_unknown:
553 default:
554 if (ecom)
555 ecommunity_free(&ecom);
556 return NULL;
557 }
558 }
559 return ecom;
560 }
561
562 static int ecommunity_rt_soo_str(char *buf, uint8_t *pnt, int type,
563 int sub_type, int format)
564 {
565 int len = 0;
566 const char *prefix;
567
568 /* For parse Extended Community attribute tupple. */
569 struct ecommunity_as eas;
570 struct ecommunity_ip eip;
571
572
573 /* Determine prefix for string, if any. */
574 switch (format) {
575 case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
576 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
577 break;
578 case ECOMMUNITY_FORMAT_DISPLAY:
579 prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
580 break;
581 case ECOMMUNITY_FORMAT_ROUTE_MAP:
582 prefix = "";
583 break;
584 default:
585 prefix = "";
586 break;
587 }
588
589 /* Put string into buffer. */
590 if (type == ECOMMUNITY_ENCODE_AS4) {
591 pnt = ptr_get_be32(pnt, &eas.as);
592 eas.val = (*pnt++ << 8);
593 eas.val |= (*pnt++);
594
595 len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val);
596 } else if (type == ECOMMUNITY_ENCODE_AS) {
597 eas.as = (*pnt++ << 8);
598 eas.as |= (*pnt++);
599 pnt = ptr_get_be32(pnt, &eas.val);
600
601 len = sprintf(buf, "%s%u:%u", prefix, eas.as, eas.val);
602 } else if (type == ECOMMUNITY_ENCODE_IP) {
603 memcpy(&eip.ip, pnt, 4);
604 pnt += 4;
605 eip.val = (*pnt++ << 8);
606 eip.val |= (*pnt++);
607
608 len = sprintf(buf, "%s%s:%u", prefix, inet_ntoa(eip.ip),
609 eip.val);
610 }
611 (void)pnt; /* consume value */
612
613 return len;
614 }
615
616 /* Convert extended community attribute to string.
617
618 Due to historical reason of industry standard implementation, there
619 are three types of format.
620
621 route-map set extcommunity format
622 "rt 100:1 100:2"
623 "soo 100:3"
624
625 extcommunity-list
626 "rt 100:1 rt 100:2 soo 100:3"
627
628 "show [ip] bgp" and extcommunity-list regular expression matching
629 "RT:100:1 RT:100:2 SoO:100:3"
630
631 For each formath please use below definition for format:
632
633 ECOMMUNITY_FORMAT_ROUTE_MAP
634 ECOMMUNITY_FORMAT_COMMUNITY_LIST
635 ECOMMUNITY_FORMAT_DISPLAY
636
637 Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
638 0 value displays all
639 */
640 char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
641 {
642 int i;
643 uint8_t *pnt;
644 uint8_t type = 0;
645 uint8_t sub_type = 0;
646 #define ECOMMUNITY_STR_DEFAULT_LEN 64
647 int str_size;
648 int str_pnt;
649 char *str_buf;
650 int len = 0;
651 int first = 1;
652
653 if (ecom->size == 0) {
654 str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, 1);
655 str_buf[0] = '\0';
656 return str_buf;
657 }
658
659 /* Prepare buffer. */
660 str_buf = XMALLOC(MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
661 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
662 str_buf[0] = '\0';
663 str_pnt = 0;
664
665 for (i = 0; i < ecom->size; i++) {
666 int unk_ecom = 0;
667
668 /* Make it sure size is enough. */
669 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) {
670 str_size *= 2;
671 str_buf = XREALLOC(MTYPE_ECOMMUNITY_STR, str_buf,
672 str_size);
673 }
674
675 /* Space between each value. */
676 if (!first) {
677 str_buf[str_pnt++] = ' ';
678 len++;
679 }
680
681 pnt = ecom->val + (i * 8);
682
683 /* High-order octet of type. */
684 type = *pnt++;
685
686 if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
687 || type == ECOMMUNITY_ENCODE_AS4) {
688 /* Low-order octet of type. */
689 sub_type = *pnt++;
690 if (sub_type != ECOMMUNITY_ROUTE_TARGET
691 && sub_type != ECOMMUNITY_SITE_ORIGIN)
692 unk_ecom = 1;
693 else
694 len = ecommunity_rt_soo_str(str_buf + str_pnt,
695 pnt, type, sub_type,
696 format);
697 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
698 if (filter == ECOMMUNITY_ROUTE_TARGET)
699 continue;
700 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
701 uint16_t tunneltype;
702 memcpy(&tunneltype, pnt + 5, 2);
703 tunneltype = ntohs(tunneltype);
704 len = sprintf(str_buf + str_pnt, "ET:%d",
705 tunneltype);
706 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
707 len = sprintf(str_buf + str_pnt,
708 "Default Gateway");
709 } else
710 unk_ecom = 1;
711 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
712 if (filter == ECOMMUNITY_ROUTE_TARGET)
713 continue;
714 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
715 struct ethaddr rmac;
716 pnt++;
717 memcpy(&rmac, pnt, ETH_ALEN);
718 len = sprintf(
719 str_buf + str_pnt,
720 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
721 (uint8_t)rmac.octet[0],
722 (uint8_t)rmac.octet[1],
723 (uint8_t)rmac.octet[2],
724 (uint8_t)rmac.octet[3],
725 (uint8_t)rmac.octet[4],
726 (uint8_t)rmac.octet[5]);
727 } else if (*pnt
728 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
729 uint32_t seqnum;
730 uint8_t flags = *++pnt;
731
732 memcpy(&seqnum, pnt + 2, 4);
733 seqnum = ntohl(seqnum);
734 if (flags
735 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
736 len = sprintf(str_buf + str_pnt,
737 "MM:%u, sticky MAC",
738 seqnum);
739 else
740 len = sprintf(str_buf + str_pnt,
741 "MM:%u", seqnum);
742 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
743 uint8_t flags = *++pnt;
744
745 if (flags
746 & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
747 len = sprintf(str_buf + str_pnt,
748 "ND:Router Flag");
749 } else
750 unk_ecom = 1;
751 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
752 sub_type = *pnt++;
753 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
754 len = sprintf(
755 str_buf + str_pnt,
756 "FS:redirect IP 0x%x", *(pnt+5));
757 } else
758 unk_ecom = 1;
759 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
760 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
761 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
762 sub_type = *pnt++;
763 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
764 char buf[16];
765
766 memset(buf, 0, sizeof(buf));
767 ecommunity_rt_soo_str(buf, (uint8_t *)pnt,
768 type &
769 ~ECOMMUNITY_ENCODE_TRANS_EXP,
770 ECOMMUNITY_ROUTE_TARGET,
771 ECOMMUNITY_FORMAT_DISPLAY);
772 len = snprintf(str_buf + str_pnt,
773 str_size - len,
774 "FS:redirect VRF %s", buf);
775 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
776 unk_ecom = 1;
777 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
778 char action[64];
779 char *ptr = action;
780
781 if (*(pnt+3) ==
782 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
783 ptr += snprintf(ptr, sizeof(action),
784 "terminate (apply)");
785 else
786 ptr += snprintf(ptr, sizeof(action),
787 "eval stops");
788 if (*(pnt+3) ==
789 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
790 snprintf(ptr, sizeof(action) -
791 (size_t)(ptr-action),
792 ", sample");
793 len = snprintf(str_buf + str_pnt,
794 str_size - len,
795 "FS:action %s", action);
796 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
797 union traffic_rate data;
798
799 data.rate_byte[3] = *(pnt+2);
800 data.rate_byte[2] = *(pnt+3);
801 data.rate_byte[1] = *(pnt+4);
802 data.rate_byte[0] = *(pnt+5);
803 len = sprintf(
804 str_buf + str_pnt,
805 "FS:rate %f", data.rate_float);
806 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
807 len = sprintf(
808 str_buf + str_pnt,
809 "FS:marking %u", *(pnt+5));
810 } else if (*pnt
811 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
812 struct ethaddr mac;
813
814 pnt++;
815 memcpy(&mac, pnt, ETH_ALEN);
816 len = sprintf(
817 str_buf + str_pnt,
818 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
819 (uint8_t)mac.octet[0],
820 (uint8_t)mac.octet[1],
821 (uint8_t)mac.octet[2],
822 (uint8_t)mac.octet[3],
823 (uint8_t)mac.octet[4],
824 (uint8_t)mac.octet[5]);
825 } else
826 unk_ecom = 1;
827 } else {
828 sub_type = *pnt++;
829 unk_ecom = 1;
830 }
831
832 if (unk_ecom)
833 len = sprintf(str_buf + str_pnt, "UNK:%d, %d",
834 type, sub_type);
835
836 str_pnt += len;
837 first = 0;
838 }
839
840 return str_buf;
841 }
842
843 int ecommunity_match(const struct ecommunity *ecom1,
844 const struct ecommunity *ecom2)
845 {
846 int i = 0;
847 int j = 0;
848
849 if (ecom1 == NULL && ecom2 == NULL)
850 return 1;
851
852 if (ecom1 == NULL || ecom2 == NULL)
853 return 0;
854
855 if (ecom1->size < ecom2->size)
856 return 0;
857
858 /* Every community on com2 needs to be on com1 for this to match */
859 while (i < ecom1->size && j < ecom2->size) {
860 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
861 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
862 == 0)
863 j++;
864 i++;
865 }
866
867 if (j == ecom2->size)
868 return 1;
869 else
870 return 0;
871 }
872
873 /* return first occurence of type */
874 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
875 uint8_t type, uint8_t subtype)
876 {
877 uint8_t *p;
878 int c;
879
880 /* If the value already exists in the structure return 0. */
881 c = 0;
882 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
883 if (p == NULL) {
884 continue;
885 }
886 if (p[0] == type && p[1] == subtype)
887 return (struct ecommunity_val *)p;
888 }
889 return NULL;
890 }
891
892 /* remove ext. community matching type and subtype
893 * return 1 on success ( removed ), 0 otherwise (not present)
894 */
895 extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
896 uint8_t subtype)
897 {
898 uint8_t *p;
899 int c, found = 0;
900 /* When this is fist value, just add it. */
901 if (ecom == NULL || ecom->val == NULL) {
902 return 0;
903 }
904
905 /* If the value already exists in the structure return 0. */
906 c = 0;
907 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
908 if (p[0] == type && p[1] == subtype) {
909 found = 1;
910 break;
911 }
912 }
913 if (found == 0)
914 return 0;
915 /* Strip The selected value */
916 ecom->size--;
917 /* size is reduced. no memmove to do */
918 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
919 if (c != 0)
920 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
921 if ((ecom->size - c) != 0)
922 memcpy(p + (c)*ECOMMUNITY_SIZE,
923 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
924 (ecom->size - c) * ECOMMUNITY_SIZE);
925 /* shift last ecommunities */
926 XFREE(MTYPE_ECOMMUNITY, ecom->val);
927 ecom->val = p;
928 return 1;
929 }
930
931 /*
932 * Remove specified extended community value from extended community.
933 * Returns 1 if value was present (and hence, removed), 0 otherwise.
934 */
935 int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
936 {
937 uint8_t *p;
938 int c, found = 0;
939
940 /* Make sure specified value exists. */
941 if (ecom == NULL || ecom->val == NULL)
942 return 0;
943 c = 0;
944 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
945 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
946 found = 1;
947 break;
948 }
949 }
950 if (found == 0)
951 return 0;
952
953 /* Delete the selected value */
954 ecom->size--;
955 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
956 if (c != 0)
957 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
958 if ((ecom->size - c) != 0)
959 memcpy(p + (c)*ECOMMUNITY_SIZE,
960 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
961 (ecom->size - c) * ECOMMUNITY_SIZE);
962 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
963 ecom->val = p;
964 return 1;
965 }
966
967 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
968 struct bgp_pbr_entry_action *api)
969 {
970 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
971 api->action = ACTION_TRAFFICRATE;
972 api->u.r.rate_info[3] = ecom_eval->val[4];
973 api->u.r.rate_info[2] = ecom_eval->val[5];
974 api->u.r.rate_info[1] = ecom_eval->val[6];
975 api->u.r.rate_info[0] = ecom_eval->val[7];
976 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
977 api->action = ACTION_TRAFFIC_ACTION;
978 /* else distribute code is set by default */
979 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
980 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
981 else
982 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
983 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
984 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
985
986 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
987 api->action = ACTION_MARKING;
988 api->u.marking_dscp = ecom_eval->val[7];
989 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
990 /* must use external function */
991 return 0;
992 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
993 /* see draft-ietf-idr-flowspec-redirect-ip-02
994 * Q1: how come a ext. community can host ipv6 address
995 * Q2 : from cisco documentation:
996 * Announces the reachability of one or more flowspec NLRI.
997 * When a BGP speaker receives an UPDATE message with the
998 * redirect-to-IP extended community, it is expected to
999 * create a traffic filtering rule for every flow-spec
1000 * NLRI in the message that has this path as its best
1001 * path. The filter entry matches the IP packets
1002 * described in the NLRI field and redirects them or
1003 * copies them towards the IPv4 or IPv6 address specified
1004 * in the 'Network Address of Next- Hop'
1005 * field of the associated MP_REACH_NLRI.
1006 */
1007 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1008 ecom_eval + 2;
1009
1010 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1011 } else
1012 return -1;
1013 return 0;
1014 }