]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_ecommunity.c
Merge pull request #2198 from LabNConsulting/working/master/bgpd-nht-crash
[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 int type = 0;
646 int sub_type = 0;
647 #define ECOMMUNITY_STR_DEFAULT_LEN 27
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 sub_type = *pnt++;
755
756 if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
757 char action[64];
758 char *ptr = action;
759
760 if (*(pnt+3) ==
761 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
762 ptr += snprintf(ptr, sizeof(action),
763 "terminate (apply)");
764 else
765 ptr += snprintf(ptr, sizeof(action),
766 "eval stops");
767 if (*(pnt+3) ==
768 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
769 snprintf(ptr, sizeof(action) -
770 (size_t)(ptr-action),
771 ", sample");
772 len = snprintf(str_buf + str_pnt,
773 str_size - len,
774 "FS:action %s", action);
775 } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
776 union traffic_rate data;
777
778 data.rate_byte[3] = *(pnt+2);
779 data.rate_byte[2] = *(pnt+3);
780 data.rate_byte[1] = *(pnt+4);
781 data.rate_byte[0] = *(pnt+5);
782 len = sprintf(
783 str_buf + str_pnt,
784 "FS:rate %f", data.rate_float);
785 } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
786 char buf[16];
787
788 memset(buf, 0, sizeof(buf));
789 ecommunity_rt_soo_str(buf, (uint8_t *)pnt,
790 type &
791 ~ECOMMUNITY_ENCODE_TRANS_EXP,
792 ECOMMUNITY_ROUTE_TARGET,
793 ECOMMUNITY_FORMAT_DISPLAY);
794 len = snprintf(
795 str_buf + str_pnt,
796 str_size - len,
797 "FS:redirect VRF %s", buf);
798 } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
799 len = sprintf(
800 str_buf + str_pnt,
801 "FS:marking %u", *(pnt+5));
802 } else
803 unk_ecom = 1;
804 } else
805 unk_ecom = 1;
806
807 if (unk_ecom)
808 len = sprintf(str_buf + str_pnt, "?");
809
810 str_pnt += len;
811 first = 0;
812 }
813
814 return str_buf;
815 }
816
817 int ecommunity_match(const struct ecommunity *ecom1,
818 const struct ecommunity *ecom2)
819 {
820 int i = 0;
821 int j = 0;
822
823 if (ecom1 == NULL && ecom2 == NULL)
824 return 1;
825
826 if (ecom1 == NULL || ecom2 == NULL)
827 return 0;
828
829 if (ecom1->size < ecom2->size)
830 return 0;
831
832 /* Every community on com2 needs to be on com1 for this to match */
833 while (i < ecom1->size && j < ecom2->size) {
834 if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
835 ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
836 == 0)
837 j++;
838 i++;
839 }
840
841 if (j == ecom2->size)
842 return 1;
843 else
844 return 0;
845 }
846
847 /* return first occurence of type */
848 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
849 uint8_t type, uint8_t subtype)
850 {
851 uint8_t *p;
852 int c;
853
854 /* If the value already exists in the structure return 0. */
855 c = 0;
856 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
857 if (p == NULL) {
858 continue;
859 }
860 if (p[0] == type && p[1] == subtype)
861 return (struct ecommunity_val *)p;
862 }
863 return NULL;
864 }
865
866 /* remove ext. community matching type and subtype
867 * return 1 on success ( removed ), 0 otherwise (not present)
868 */
869 extern int ecommunity_strip(struct ecommunity *ecom, uint8_t type,
870 uint8_t subtype)
871 {
872 uint8_t *p;
873 int c, found = 0;
874 /* When this is fist value, just add it. */
875 if (ecom == NULL || ecom->val == NULL) {
876 return 0;
877 }
878
879 /* If the value already exists in the structure return 0. */
880 c = 0;
881 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
882 if (p[0] == type && p[1] == subtype) {
883 found = 1;
884 break;
885 }
886 }
887 if (found == 0)
888 return 0;
889 /* Strip The selected value */
890 ecom->size--;
891 /* size is reduced. no memmove to do */
892 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
893 if (c != 0)
894 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
895 if ((ecom->size - c) != 0)
896 memcpy(p + (c)*ECOMMUNITY_SIZE,
897 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
898 (ecom->size - c) * ECOMMUNITY_SIZE);
899 /* shift last ecommunities */
900 XFREE(MTYPE_ECOMMUNITY, ecom->val);
901 ecom->val = p;
902 return 1;
903 }
904
905 /*
906 * Remove specified extended community value from extended community.
907 * Returns 1 if value was present (and hence, removed), 0 otherwise.
908 */
909 int ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
910 {
911 uint8_t *p;
912 int c, found = 0;
913
914 /* Make sure specified value exists. */
915 if (ecom == NULL || ecom->val == NULL)
916 return 0;
917 c = 0;
918 for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
919 if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
920 found = 1;
921 break;
922 }
923 }
924 if (found == 0)
925 return 0;
926
927 /* Delete the selected value */
928 ecom->size--;
929 p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
930 if (c != 0)
931 memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
932 if ((ecom->size - c) != 0)
933 memcpy(p + (c)*ECOMMUNITY_SIZE,
934 ecom->val + (c + 1) * ECOMMUNITY_SIZE,
935 (ecom->size - c) * ECOMMUNITY_SIZE);
936 XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
937 ecom->val = p;
938 return 1;
939 }
940
941 int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
942 struct bgp_pbr_entry_action *api)
943 {
944 if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
945 api->action = ACTION_TRAFFICRATE;
946 api->u.r.rate_info[3] = ecom_eval->val[4];
947 api->u.r.rate_info[2] = ecom_eval->val[5];
948 api->u.r.rate_info[1] = ecom_eval->val[6];
949 api->u.r.rate_info[0] = ecom_eval->val[7];
950 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
951 api->action = ACTION_TRAFFIC_ACTION;
952 /* else distribute code is set by default */
953 if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
954 api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
955 else
956 api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
957 if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
958 api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
959
960 } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
961 api->action = ACTION_MARKING;
962 api->u.marking_dscp = ecom_eval->val[7];
963 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
964 /* must use external function */
965 return 0;
966 } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) {
967 /* see draft-ietf-idr-flowspec-redirect-ip-02
968 * Q1: how come a ext. community can host ipv6 address
969 * Q2 : from cisco documentation:
970 * Announces the reachability of one or more flowspec NLRI.
971 * When a BGP speaker receives an UPDATE message with the
972 * redirect-to-IP extended community, it is expected to
973 * create a traffic filtering rule for every flow-spec
974 * NLRI in the message that has this path as its best
975 * path. The filter entry matches the IP packets
976 * described in the NLRI field and redirects them or
977 * copies them towards the IPv4 or IPv6 address specified
978 * in the 'Network Address of Next- Hop'
979 * field of the associated MP_REACH_NLRI.
980 */
981 struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
982 ecom_eval + 2;
983
984 api->u.zr.redirect_ip_v4 = ip_ecom->ip;
985 } else
986 return -1;
987 return 0;
988 }