Annotation of embedaddon/quagga/bgpd/bgp_ecommunity.c, revision 1.1.1.1
1.1 misho 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
17: along with GNU Zebra; see the file COPYING. If not, write to the Free
18: Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19: 02111-1307, USA. */
20:
21: #include <zebra.h>
22:
23: #include "hash.h"
24: #include "memory.h"
25: #include "prefix.h"
26: #include "command.h"
27:
28: #include "bgpd/bgpd.h"
29: #include "bgpd/bgp_ecommunity.h"
30: #include "bgpd/bgp_aspath.h"
31:
32: /* Hash of community attribute. */
33: static struct hash *ecomhash;
34:
35: /* Allocate a new ecommunities. */
36: static struct ecommunity *
37: ecommunity_new (void)
38: {
39: return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY,
40: sizeof (struct ecommunity));
41: }
42:
43: /* Allocate ecommunities. */
44: void
45: ecommunity_free (struct ecommunity **ecom)
46: {
47: if ((*ecom)->val)
48: XFREE (MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
49: if ((*ecom)->str)
50: XFREE (MTYPE_ECOMMUNITY_STR, (*ecom)->str);
51: XFREE (MTYPE_ECOMMUNITY, *ecom);
52: ecom = NULL;
53: }
54:
55: /* Add a new Extended Communities value to Extended Communities
56: Attribute structure. When the value is already exists in the
57: structure, we don't add the value. Newly added value is sorted by
58: numerical order. When the value is added to the structure return 1
59: else return 0. */
60: static int
61: ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval)
62: {
63: u_int8_t *p;
64: int ret;
65: int c;
66:
67: /* When this is fist value, just add it. */
68: if (ecom->val == NULL)
69: {
70: ecom->size++;
71: ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom));
72: memcpy (ecom->val, eval->val, ECOMMUNITY_SIZE);
73: return 1;
74: }
75:
76: /* If the value already exists in the structure return 0. */
77: c = 0;
78: for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++)
79: {
80: ret = memcmp (p, eval->val, ECOMMUNITY_SIZE);
81: if (ret == 0)
82: return 0;
83: if (ret > 0)
84: break;
85: }
86:
87: /* Add the value to the structure with numerical sorting. */
88: ecom->size++;
89: ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom));
90:
91: memmove (ecom->val + (c + 1) * ECOMMUNITY_SIZE,
92: ecom->val + c * ECOMMUNITY_SIZE,
93: (ecom->size - 1 - c) * ECOMMUNITY_SIZE);
94: memcpy (ecom->val + c * ECOMMUNITY_SIZE, eval->val, ECOMMUNITY_SIZE);
95:
96: return 1;
97: }
98:
99: /* This function takes pointer to Extended Communites strucutre then
100: create a new Extended Communities structure by uniq and sort each
101: Extended Communities value. */
102: static struct ecommunity *
103: ecommunity_uniq_sort (struct ecommunity *ecom)
104: {
105: int i;
106: struct ecommunity *new;
107: struct ecommunity_val *eval;
108:
109: if (! ecom)
110: return NULL;
111:
112: new = ecommunity_new ();
113:
114: for (i = 0; i < ecom->size; i++)
115: {
116: eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
117: ecommunity_add_val (new, eval);
118: }
119: return new;
120: }
121:
122: /* Parse Extended Communites Attribute in BGP packet. */
123: struct ecommunity *
124: ecommunity_parse (u_int8_t *pnt, u_short length)
125: {
126: struct ecommunity tmp;
127: struct ecommunity *new;
128:
129: /* Length check. */
130: if (length % ECOMMUNITY_SIZE)
131: return NULL;
132:
133: /* Prepare tmporary structure for making a new Extended Communities
134: Attribute. */
135: tmp.size = length / ECOMMUNITY_SIZE;
136: tmp.val = pnt;
137:
138: /* Create a new Extended Communities Attribute by uniq and sort each
139: Extended Communities value */
140: new = ecommunity_uniq_sort (&tmp);
141:
142: return ecommunity_intern (new);
143: }
144:
145: /* Duplicate the Extended Communities Attribute structure. */
146: struct ecommunity *
147: ecommunity_dup (struct ecommunity *ecom)
148: {
149: struct ecommunity *new;
150:
151: new = XCALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity));
152: new->size = ecom->size;
153: if (new->size)
154: {
155: new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
156: memcpy (new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
157: }
158: else
159: new->val = NULL;
160: return new;
161: }
162:
163: /* Retrun string representation of communities attribute. */
164: char *
165: ecommunity_str (struct ecommunity *ecom)
166: {
167: if (! ecom->str)
168: ecom->str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_DISPLAY);
169: return ecom->str;
170: }
171:
172: /* Merge two Extended Communities Attribute structure. */
173: struct ecommunity *
174: ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2)
175: {
176: if (ecom1->val)
177: ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val,
178: (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
179: else
180: ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL,
181: (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
182:
183: memcpy (ecom1->val + (ecom1->size * ECOMMUNITY_SIZE),
184: ecom2->val, ecom2->size * ECOMMUNITY_SIZE);
185: ecom1->size += ecom2->size;
186:
187: return ecom1;
188: }
189:
190: /* Intern Extended Communities Attribute. */
191: struct ecommunity *
192: ecommunity_intern (struct ecommunity *ecom)
193: {
194: struct ecommunity *find;
195:
196: assert (ecom->refcnt == 0);
197:
198: find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern);
199:
200: if (find != ecom)
201: ecommunity_free (&ecom);
202:
203: find->refcnt++;
204:
205: if (! find->str)
206: find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY);
207:
208: return find;
209: }
210:
211: /* Unintern Extended Communities Attribute. */
212: void
213: ecommunity_unintern (struct ecommunity **ecom)
214: {
215: struct ecommunity *ret;
216:
217: if ((*ecom)->refcnt)
218: (*ecom)->refcnt--;
219:
220: /* Pull off from hash. */
221: if ((*ecom)->refcnt == 0)
222: {
223: /* Extended community must be in the hash. */
224: ret = (struct ecommunity *) hash_release (ecomhash, *ecom);
225: assert (ret != NULL);
226:
227: ecommunity_free (ecom);
228: }
229: }
230:
231: /* Utinity function to make hash key. */
232: unsigned int
233: ecommunity_hash_make (void *arg)
234: {
235: const struct ecommunity *ecom = arg;
236: int c;
237: unsigned int key;
238: u_int8_t *pnt;
239:
240: key = 0;
241: pnt = ecom->val;
242:
243: for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++)
244: key += pnt[c];
245:
246: return key;
247: }
248:
249: /* Compare two Extended Communities Attribute structure. */
250: int
251: ecommunity_cmp (const void *arg1, const void *arg2)
252: {
253: const struct ecommunity *ecom1 = arg1;
254: const struct ecommunity *ecom2 = arg2;
255:
256: return (ecom1->size == ecom2->size
257: && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
258: }
259:
260: /* Initialize Extended Comminities related hash. */
261: void
262: ecommunity_init (void)
263: {
264: ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
265: }
266:
267: void
268: ecommunity_finish (void)
269: {
270: hash_free (ecomhash);
271: ecomhash = NULL;
272: }
273:
274: /* Extended Communities token enum. */
275: enum ecommunity_token
276: {
277: ecommunity_token_rt,
278: ecommunity_token_soo,
279: ecommunity_token_val,
280: ecommunity_token_unknown
281: };
282:
283: /* Get next Extended Communities token from the string. */
284: static const char *
285: ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
286: enum ecommunity_token *token)
287: {
288: int ret;
289: int dot = 0;
290: int digit = 0;
291: int separator = 0;
292: const char *p = str;
293: char *endptr;
294: struct in_addr ip;
295: as_t as = 0;
296: u_int32_t val = 0;
297: char buf[INET_ADDRSTRLEN + 1];
298:
299: /* Skip white space. */
300: while (isspace ((int) *p))
301: {
302: p++;
303: str++;
304: }
305:
306: /* Check the end of the line. */
307: if (*p == '\0')
308: return NULL;
309:
310: /* "rt" and "soo" keyword parse. */
311: if (! isdigit ((int) *p))
312: {
313: /* "rt" match check. */
314: if (tolower ((int) *p) == 'r')
315: {
316: p++;
317: if (tolower ((int) *p) == 't')
318: {
319: p++;
320: *token = ecommunity_token_rt;
321: return p;
322: }
323: if (isspace ((int) *p) || *p == '\0')
324: {
325: *token = ecommunity_token_rt;
326: return p;
327: }
328: goto error;
329: }
330: /* "soo" match check. */
331: else if (tolower ((int) *p) == 's')
332: {
333: p++;
334: if (tolower ((int) *p) == 'o')
335: {
336: p++;
337: if (tolower ((int) *p) == 'o')
338: {
339: p++;
340: *token = ecommunity_token_soo;
341: return p;
342: }
343: if (isspace ((int) *p) || *p == '\0')
344: {
345: *token = ecommunity_token_soo;
346: return p;
347: }
348: goto error;
349: }
350: if (isspace ((int) *p) || *p == '\0')
351: {
352: *token = ecommunity_token_soo;
353: return p;
354: }
355: goto error;
356: }
357: goto error;
358: }
359:
360: /* What a mess, there are several possibilities:
361: *
362: * a) A.B.C.D:MN
363: * b) EF:OPQR
364: * c) GHJK:MN
365: *
366: * A.B.C.D: Four Byte IP
367: * EF: Two byte ASN
368: * GHJK: Four-byte ASN
369: * MN: Two byte value
370: * OPQR: Four byte value
371: *
372: */
373: while (isdigit ((int) *p) || *p == ':' || *p == '.')
374: {
375: if (*p == ':')
376: {
377: if (separator)
378: goto error;
379:
380: separator = 1;
381: digit = 0;
382:
383: if ((p - str) > INET_ADDRSTRLEN)
384: goto error;
385: memset (buf, 0, INET_ADDRSTRLEN + 1);
386: memcpy (buf, str, p - str);
387:
388: if (dot)
389: {
390: /* Parsing A.B.C.D in:
391: * A.B.C.D:MN
392: */
393: ret = inet_aton (buf, &ip);
394: if (ret == 0)
395: goto error;
396: }
397: else
398: {
399: /* ASN */
400: as = strtoul (buf, &endptr, 10);
401: if (*endptr != '\0' || as == BGP_AS4_MAX)
402: goto error;
403: }
404: }
405: else if (*p == '.')
406: {
407: if (separator)
408: goto error;
409: dot++;
410: if (dot > 4)
411: goto error;
412: }
413: else
414: {
415: digit = 1;
416:
417: /* We're past the IP/ASN part */
418: if (separator)
419: {
420: val *= 10;
421: val += (*p - '0');
422: }
423: }
424: p++;
425: }
426:
427: /* Low digit part must be there. */
428: if (!digit || !separator)
429: goto error;
430:
431: /* Encode result into routing distinguisher. */
432: if (dot)
433: {
434: if (val > UINT16_MAX)
435: goto error;
436:
437: eval->val[0] = ECOMMUNITY_ENCODE_IP;
438: eval->val[1] = 0;
439: memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
440: eval->val[6] = (val >> 8) & 0xff;
441: eval->val[7] = val & 0xff;
442: }
443: else if (as > BGP_AS_MAX)
444: {
445: if (val > UINT16_MAX)
446: goto error;
447:
448: eval->val[0] = ECOMMUNITY_ENCODE_AS4;
449: eval->val[1] = 0;
450: eval->val[2] = (as >>24) & 0xff;
451: eval->val[3] = (as >>16) & 0xff;
452: eval->val[4] = (as >>8) & 0xff;
453: eval->val[5] = as & 0xff;
454: eval->val[6] = (val >> 8) & 0xff;
455: eval->val[7] = val & 0xff;
456: }
457: else
458: {
459: eval->val[0] = ECOMMUNITY_ENCODE_AS;
460: eval->val[1] = 0;
461:
462: eval->val[2] = (as >>8) & 0xff;
463: eval->val[3] = as & 0xff;
464: eval->val[4] = (val >>24) & 0xff;
465: eval->val[5] = (val >>16) & 0xff;
466: eval->val[6] = (val >>8) & 0xff;
467: eval->val[7] = val & 0xff;
468: }
469: *token = ecommunity_token_val;
470: return p;
471:
472: error:
473: *token = ecommunity_token_unknown;
474: return p;
475: }
476:
477: /* Convert string to extended community attribute.
478:
479: When type is already known, please specify both str and type. str
480: should not include keyword such as "rt" and "soo". Type is
481: ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
482: keyword_included should be zero.
483:
484: For example route-map's "set extcommunity" command case:
485:
486: "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
487: type = ECOMMUNITY_ROUTE_TARGET
488: keyword_included = 0
489:
490: "soo 100:1" -> str = "100:1"
491: type = ECOMMUNITY_SITE_ORIGIN
492: keyword_included = 0
493:
494: When string includes keyword for each extended community value.
495: Please specify keyword_included as non-zero value.
496:
497: For example standard extcommunity-list case:
498:
499: "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
500: type = 0
501: keyword_include = 1
502: */
503: struct ecommunity *
504: ecommunity_str2com (const char *str, int type, int keyword_included)
505: {
506: struct ecommunity *ecom = NULL;
507: enum ecommunity_token token;
508: struct ecommunity_val eval;
509: int keyword = 0;
510:
511: while ((str = ecommunity_gettoken (str, &eval, &token)))
512: {
513: switch (token)
514: {
515: case ecommunity_token_rt:
516: case ecommunity_token_soo:
517: if (! keyword_included || keyword)
518: {
519: if (ecom)
520: ecommunity_free (&ecom);
521: return NULL;
522: }
523: keyword = 1;
524:
525: if (token == ecommunity_token_rt)
526: {
527: type = ECOMMUNITY_ROUTE_TARGET;
528: }
529: if (token == ecommunity_token_soo)
530: {
531: type = ECOMMUNITY_SITE_ORIGIN;
532: }
533: break;
534: case ecommunity_token_val:
535: if (keyword_included)
536: {
537: if (! keyword)
538: {
539: if (ecom)
540: ecommunity_free (&ecom);
541: return NULL;
542: }
543: keyword = 0;
544: }
545: if (ecom == NULL)
546: ecom = ecommunity_new ();
547: eval.val[1] = type;
548: ecommunity_add_val (ecom, &eval);
549: break;
550: case ecommunity_token_unknown:
551: default:
552: if (ecom)
553: ecommunity_free (&ecom);
554: return NULL;
555: }
556: }
557: return ecom;
558: }
559:
560: /* Convert extended community attribute to string.
561:
562: Due to historical reason of industry standard implementation, there
563: are three types of format.
564:
565: route-map set extcommunity format
566: "rt 100:1 100:2"
567: "soo 100:3"
568:
569: extcommunity-list
570: "rt 100:1 rt 100:2 soo 100:3"
571:
572: "show ip bgp" and extcommunity-list regular expression matching
573: "RT:100:1 RT:100:2 SoO:100:3"
574:
575: For each formath please use below definition for format:
576:
577: ECOMMUNITY_FORMAT_ROUTE_MAP
578: ECOMMUNITY_FORMAT_COMMUNITY_LIST
579: ECOMMUNITY_FORMAT_DISPLAY
580: */
581: char *
582: ecommunity_ecom2str (struct ecommunity *ecom, int format)
583: {
584: int i;
585: u_int8_t *pnt;
586: int encode = 0;
587: int type = 0;
588: #define ECOMMUNITY_STR_DEFAULT_LEN 27
589: int str_size;
590: int str_pnt;
591: char *str_buf;
592: const char *prefix;
593: int len = 0;
594: int first = 1;
595:
596: /* For parse Extended Community attribute tupple. */
597: struct ecommunity_as
598: {
599: as_t as;
600: u_int32_t val;
601: } eas;
602:
603: struct ecommunity_ip
604: {
605: struct in_addr ip;
606: u_int16_t val;
607: } eip;
608:
609: if (ecom->size == 0)
610: {
611: str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
612: str_buf[0] = '\0';
613: return str_buf;
614: }
615:
616: /* Prepare buffer. */
617: str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
618: str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
619: str_pnt = 0;
620:
621: for (i = 0; i < ecom->size; i++)
622: {
623: /* Make it sure size is enough. */
624: while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
625: {
626: str_size *= 2;
627: str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
628: }
629:
630: /* Space between each value. */
631: if (! first)
632: str_buf[str_pnt++] = ' ';
633:
634: pnt = ecom->val + (i * 8);
635:
636: /* High-order octet of type. */
637: encode = *pnt++;
638: if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP
639: && encode != ECOMMUNITY_ENCODE_AS4)
640: {
641: len = sprintf (str_buf + str_pnt, "?");
642: str_pnt += len;
643: first = 0;
644: continue;
645: }
646:
647: /* Low-order octet of type. */
648: type = *pnt++;
649: if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
650: {
651: len = sprintf (str_buf + str_pnt, "?");
652: str_pnt += len;
653: first = 0;
654: continue;
655: }
656:
657: switch (format)
658: {
659: case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
660: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
661: break;
662: case ECOMMUNITY_FORMAT_DISPLAY:
663: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
664: break;
665: case ECOMMUNITY_FORMAT_ROUTE_MAP:
666: prefix = "";
667: break;
668: default:
669: prefix = "";
670: break;
671: }
672:
673: /* Put string into buffer. */
674: if (encode == ECOMMUNITY_ENCODE_AS4)
675: {
676: eas.as = (*pnt++ << 24);
677: eas.as |= (*pnt++ << 16);
678: eas.as |= (*pnt++ << 8);
679: eas.as |= (*pnt++);
680:
681: eas.val = (*pnt++ << 8);
682: eas.val |= (*pnt++);
683:
684: len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix,
685: eas.as, eas.val );
686: str_pnt += len;
687: first = 0;
688: }
689: if (encode == ECOMMUNITY_ENCODE_AS)
690: {
691: eas.as = (*pnt++ << 8);
692: eas.as |= (*pnt++);
693:
694: eas.val = (*pnt++ << 24);
695: eas.val |= (*pnt++ << 16);
696: eas.val |= (*pnt++ << 8);
697: eas.val |= (*pnt++);
698:
699: len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix,
700: eas.as, eas.val);
701: str_pnt += len;
702: first = 0;
703: }
704: else if (encode == ECOMMUNITY_ENCODE_IP)
705: {
706: memcpy (&eip.ip, pnt, 4);
707: pnt += 4;
708: eip.val = (*pnt++ << 8);
709: eip.val |= (*pnt++);
710:
711: len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
712: inet_ntoa (eip.ip), eip.val);
713: str_pnt += len;
714: first = 0;
715: }
716: }
717: return str_buf;
718: }
719:
720: int
721: ecommunity_match (const struct ecommunity *ecom1,
722: const struct ecommunity *ecom2)
723: {
724: int i = 0;
725: int j = 0;
726:
727: if (ecom1 == NULL && ecom2 == NULL)
728: return 1;
729:
730: if (ecom1 == NULL || ecom2 == NULL)
731: return 0;
732:
733: if (ecom1->size < ecom2->size)
734: return 0;
735:
736: /* Every community on com2 needs to be on com1 for this to match */
737: while (i < ecom1->size && j < ecom2->size)
738: {
739: if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
740: j++;
741: i++;
742: }
743:
744: if (j == ecom2->size)
745: return 1;
746: else
747: return 0;
748: }
749:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>