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