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