]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
zebra: Refactor kernel_rtm to be a bit smarter about how it handles options
[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 if (sub_type ==
693 ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
694 type == ECOMMUNITY_ENCODE_IP) {
695 struct in_addr *ipv4 =
696 (struct in_addr *)pnt;
697 char ipv4str[INET_ADDRSTRLEN];
698
699 inet_ntop(AF_INET, ipv4,
700 ipv4str,
701 INET_ADDRSTRLEN);
702 len = sprintf(str_buf + str_pnt,
703 "NH:%s:%d",
704 ipv4str, pnt[5]);
705 } else
706 unk_ecom = 1;
707 } else
708 len = ecommunity_rt_soo_str(str_buf + str_pnt,
709 pnt, type, sub_type,
710 format);
711 } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
712 if (filter == ECOMMUNITY_ROUTE_TARGET)
713 continue;
714 if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
715 uint16_t tunneltype;
716 memcpy(&tunneltype, pnt + 5, 2);
717 tunneltype = ntohs(tunneltype);
718 len = sprintf(str_buf + str_pnt, "ET:%d",
719 tunneltype);
720 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
721 len = sprintf(str_buf + str_pnt,
722 "Default Gateway");
723 } else
724 unk_ecom = 1;
725 } else if (type == ECOMMUNITY_ENCODE_EVPN) {
726 if (filter == ECOMMUNITY_ROUTE_TARGET)
727 continue;
728 if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
729 struct ethaddr rmac;
730 pnt++;
731 memcpy(&rmac, pnt, ETH_ALEN);
732 len = sprintf(
733 str_buf + str_pnt,
734 "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
735 (uint8_t)rmac.octet[0],
736 (uint8_t)rmac.octet[1],
737 (uint8_t)rmac.octet[2],
738 (uint8_t)rmac.octet[3],
739 (uint8_t)rmac.octet[4],
740 (uint8_t)rmac.octet[5]);
741 } else if (*pnt
742 == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
743 uint32_t seqnum;
744 uint8_t flags = *++pnt;
745
746 memcpy(&seqnum, pnt + 2, 4);
747 seqnum = ntohl(seqnum);
748 if (flags
749 & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
750 len = sprintf(str_buf + str_pnt,
751 "MM:%u, sticky MAC",
752 seqnum);
753 else
754 len = sprintf(str_buf + str_pnt,
755 "MM:%u", seqnum);
756 } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
757 uint8_t flags = *++pnt;
758
759 if (flags
760 & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
761 len = sprintf(str_buf + str_pnt,
762 "ND:Router Flag");
763 } else
764 unk_ecom = 1;
765 } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
766 sub_type = *pnt++;
767 if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
768 len = sprintf(
769 str_buf + str_pnt,
770 "FS:redirect IP 0x%x", *(pnt+5));
771 } else
772 unk_ecom = 1;
773 } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
774 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
775 type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
776 sub_type = *pnt++;
777 if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
778 char buf[16];
779
780 memset(buf, 0, sizeof(buf));
781 ecommunity_rt_soo_str(buf, (uint8_t *)pnt,
782 type &
783 ~ECOMMUNITY_ENCODE_TRANS_EXP,
784 ECOMMUNITY_ROUTE_TARGET,
785 ECOMMUNITY_FORMAT_DISPLAY);
786 len = snprintf(str_buf + str_pnt,
787 str_size - len,
788 "FS:redirect VRF %s", buf);
789 } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
790 unk_ecom = 1;
791 else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
792 char action[64];
793 char *ptr = action;
794
795 if (*(pnt+3) ==
796 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
797 ptr += snprintf(ptr, sizeof(action),
798 "terminate (apply)");
799 else
800 ptr += snprintf(ptr, sizeof(action),
801 "eval stops");
802 if (*(pnt+3) ==
803 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
804 snprintf(ptr, sizeof(action) -
805 (size_t)(ptr-action),
806 ", sample");
807 len = snprintf(str_buf + str_pnt,
808 str_size - len,
809 "FS:action %s", action);
810 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
811 union traffic_rate data;
812
813 data.rate_byte[3] = *(pnt+2);
814 data.rate_byte[2] = *(pnt+3);
815 data.rate_byte[1] = *(pnt+4);
816 data.rate_byte[0] = *(pnt+5);
817 len = sprintf(
818 str_buf + str_pnt,
819 "FS:rate %f", data.rate_float);
820 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
821 len = sprintf(
822 str_buf + str_pnt,
823 "FS:marking %u", *(pnt+5));
824 } else if (*pnt
825 == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
826 struct ethaddr mac;
827
828 pnt++;
829 memcpy(&mac, pnt, ETH_ALEN);
830 len = sprintf(
831 str_buf + str_pnt,
832 "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
833 (uint8_t)mac.octet[0],
834 (uint8_t)mac.octet[1],
835 (uint8_t)mac.octet[2],
836 (uint8_t)mac.octet[3],
837 (uint8_t)mac.octet[4],
838 (uint8_t)mac.octet[5]);
839 } else
840 unk_ecom = 1;
841 } else {
842 sub_type = *pnt++;
843 unk_ecom = 1;
844 }
845
846 if (unk_ecom)
847 len = sprintf(str_buf + str_pnt, "UNK:%d, %d",
848 type, sub_type);
849
850 str_pnt += len;
851 first = 0;
852 }
853
854 return str_buf;
855 }
856
857 int ecommunity_match(const struct ecommunity *ecom1,
858 const struct ecommunity *ecom2)
859 {
860 int i = 0;
861 int j = 0;
862
863 if (ecom1 == NULL && ecom2 == NULL)
864 return 1;
865
866 if (ecom1 == NULL || ecom2 == NULL)
867 return 0;
868
869 if (ecom1->size < ecom2->size)
870 return 0;
871
872 /* Every community on com2 needs to be on com1 for this to match */
873 while (i < ecom1->size && j < ecom2->size) {
874 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
875 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
876 == 0)
877 j++;
878 i++;
879 }
880
881 if (j == ecom2->size)
882 return 1;
883 else
884 return 0;
885 }
886
887 /* return first occurence of type */
888 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
889 uint8_t type, uint8_t subtype)
890 {
891 uint8_t *p;
892 int c;
893
894 /* If the value already exists in the structure return 0. */
895 c = 0;
896 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
897 if (p == NULL) {
898 continue;
899 }
900 if (p[0] == type && p[1] == subtype)
901 return (struct ecommunity_val *)p;
902 }
903 return NULL;
904 }
905
906 /* remove ext. community matching type and subtype
907 * return 1 on success ( removed ), 0 otherwise (not present)
908 */
909 extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
910 uint8_t subtype)
911 {
912 uint8_t *p;
913 int c, found = 0;
914 /* When this is fist value, just add it. */
915 if (ecom == NULL || ecom->val == NULL) {
916 return 0;
917 }
918
919 /* If the value already exists in the structure return 0. */
920 c = 0;
921 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
922 if (p[0] == type && p[1] == subtype) {
923 found = 1;
924 break;
925 }
926 }
927 if (found == 0)
928 return 0;
929 /* Strip The selected value */
930 ecom->size--;
931 /* size is reduced. no memmove to do */
932 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
933 if (c != 0)
934 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
935 if ((ecom->size - c) != 0)
936 memcpy(p + (c)*ECOMMUNITY_SIZE,
937 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
938 (ecom->size - c) * ECOMMUNITY_SIZE);
939 /* shift last ecommunities */
940 XFREE(MTYPE_ECOMMUNITY, ecom->val);
941 ecom->val = p;
942 return 1;
943 }
944
945 /*
946 * Remove specified extended community value from extended community.
947 * Returns 1 if value was present (and hence, removed), 0 otherwise.
948 */
949 int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
950 {
951 uint8_t *p;
952 int c, found = 0;
953
954 /* Make sure specified value exists. */
955 if (ecom == NULL || ecom->val == NULL)
956 return 0;
957 c = 0;
958 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
959 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
960 found = 1;
961 break;
962 }
963 }
964 if (found == 0)
965 return 0;
966
967 /* Delete the selected value */
968 ecom->size--;
969 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
970 if (c != 0)
971 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
972 if ((ecom->size - c) != 0)
973 memcpy(p + (c)*ECOMMUNITY_SIZE,
974 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
975 (ecom->size - c) * ECOMMUNITY_SIZE);
976 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
977 ecom->val = p;
978 return 1;
979 }
980
981 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
982 struct bgp_pbr_entry_action *api)
983 {
984 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
985 api->action = ACTION_TRAFFICRATE;
986 api->u.r.rate_info[3] = ecom_eval->val[4];
987 api->u.r.rate_info[2] = ecom_eval->val[5];
988 api->u.r.rate_info[1] = ecom_eval->val[6];
989 api->u.r.rate_info[0] = ecom_eval->val[7];
990 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
991 api->action = ACTION_TRAFFIC_ACTION;
992 /* else distribute code is set by default */
993 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
994 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
995 else
996 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
997 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
998 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
999
1000 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
1001 api->action = ACTION_MARKING;
1002 api->u.marking_dscp = ecom_eval->val[7];
1003 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
1004 /* must use external function */
1005 return 0;
1006 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
1007 /* see draft-ietf-idr-flowspec-redirect-ip-02
1008 * Q1: how come a ext. community can host ipv6 address
1009 * Q2 : from cisco documentation:
1010 * Announces the reachability of one or more flowspec NLRI.
1011 * When a BGP speaker receives an UPDATE message with the
1012 * redirect-to-IP extended community, it is expected to
1013 * create a traffic filtering rule for every flow-spec
1014 * NLRI in the message that has this path as its best
1015 * path. The filter entry matches the IP packets
1016 * described in the NLRI field and redirects them or
1017 * copies them towards the IPv4 or IPv6 address specified
1018 * in the 'Network Address of Next- Hop'
1019 * field of the associated MP_REACH_NLRI.
1020 */
1021 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
1022 ecom_eval + 2;
1023
1024 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
1025 } else
1026 return -1;
1027 return 0;
1028 }