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