Annotation of embedaddon/quagga/bgpd/bgp_ecommunity.c, revision 1.1.1.3
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. */
1.1.1.2 misho 102: struct ecommunity *
1.1 misho 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;
1.1.1.3 ! misho 236: int size = ecom->size * ECOMMUNITY_SIZE;
! 237: u_int8_t *pnt = ecom->val;
! 238: unsigned int key = 0;
1.1 misho 239: int c;
240:
1.1.1.3 ! misho 241: for (c = 0; c < size; c += ECOMMUNITY_SIZE)
! 242: {
! 243: key += pnt[c];
! 244: key += pnt[c + 1];
! 245: key += pnt[c + 2];
! 246: key += pnt[c + 3];
! 247: key += pnt[c + 4];
! 248: key += pnt[c + 5];
! 249: key += pnt[c + 6];
! 250: key += pnt[c + 7];
! 251: }
1.1 misho 252:
253: return key;
254: }
255:
256: /* Compare two Extended Communities Attribute structure. */
257: int
258: ecommunity_cmp (const void *arg1, const void *arg2)
259: {
260: const struct ecommunity *ecom1 = arg1;
261: const struct ecommunity *ecom2 = arg2;
262:
263: return (ecom1->size == ecom2->size
264: && memcmp (ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) == 0);
265: }
266:
267: /* Initialize Extended Comminities related hash. */
268: void
269: ecommunity_init (void)
270: {
271: ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp);
272: }
273:
274: void
275: ecommunity_finish (void)
276: {
277: hash_free (ecomhash);
278: ecomhash = NULL;
279: }
280:
281: /* Extended Communities token enum. */
282: enum ecommunity_token
283: {
284: ecommunity_token_rt,
285: ecommunity_token_soo,
286: ecommunity_token_val,
287: ecommunity_token_unknown
288: };
289:
290: /* Get next Extended Communities token from the string. */
291: static const char *
292: ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
293: enum ecommunity_token *token)
294: {
295: int ret;
296: int dot = 0;
297: int digit = 0;
298: int separator = 0;
299: const char *p = str;
300: char *endptr;
301: struct in_addr ip;
302: as_t as = 0;
303: u_int32_t val = 0;
304: char buf[INET_ADDRSTRLEN + 1];
305:
306: /* Skip white space. */
307: while (isspace ((int) *p))
308: {
309: p++;
310: str++;
311: }
312:
313: /* Check the end of the line. */
314: if (*p == '\0')
315: return NULL;
316:
317: /* "rt" and "soo" keyword parse. */
318: if (! isdigit ((int) *p))
319: {
320: /* "rt" match check. */
321: if (tolower ((int) *p) == 'r')
322: {
323: p++;
324: if (tolower ((int) *p) == 't')
325: {
326: p++;
327: *token = ecommunity_token_rt;
328: return p;
329: }
330: if (isspace ((int) *p) || *p == '\0')
331: {
332: *token = ecommunity_token_rt;
333: return p;
334: }
335: goto error;
336: }
337: /* "soo" match check. */
338: else if (tolower ((int) *p) == 's')
339: {
340: p++;
341: if (tolower ((int) *p) == 'o')
342: {
343: p++;
344: if (tolower ((int) *p) == 'o')
345: {
346: p++;
347: *token = ecommunity_token_soo;
348: return p;
349: }
350: if (isspace ((int) *p) || *p == '\0')
351: {
352: *token = ecommunity_token_soo;
353: return p;
354: }
355: goto error;
356: }
357: if (isspace ((int) *p) || *p == '\0')
358: {
359: *token = ecommunity_token_soo;
360: return p;
361: }
362: goto error;
363: }
364: goto error;
365: }
366:
367: /* What a mess, there are several possibilities:
368: *
369: * a) A.B.C.D:MN
370: * b) EF:OPQR
371: * c) GHJK:MN
372: *
373: * A.B.C.D: Four Byte IP
374: * EF: Two byte ASN
375: * GHJK: Four-byte ASN
376: * MN: Two byte value
377: * OPQR: Four byte value
378: *
379: */
380: while (isdigit ((int) *p) || *p == ':' || *p == '.')
381: {
382: if (*p == ':')
383: {
384: if (separator)
385: goto error;
386:
387: separator = 1;
388: digit = 0;
389:
390: if ((p - str) > INET_ADDRSTRLEN)
391: goto error;
392: memset (buf, 0, INET_ADDRSTRLEN + 1);
393: memcpy (buf, str, p - str);
394:
395: if (dot)
396: {
397: /* Parsing A.B.C.D in:
398: * A.B.C.D:MN
399: */
400: ret = inet_aton (buf, &ip);
401: if (ret == 0)
402: goto error;
403: }
404: else
405: {
406: /* ASN */
407: as = strtoul (buf, &endptr, 10);
408: if (*endptr != '\0' || as == BGP_AS4_MAX)
409: goto error;
410: }
411: }
412: else if (*p == '.')
413: {
414: if (separator)
415: goto error;
416: dot++;
417: if (dot > 4)
418: goto error;
419: }
420: else
421: {
422: digit = 1;
423:
424: /* We're past the IP/ASN part */
425: if (separator)
426: {
427: val *= 10;
428: val += (*p - '0');
429: }
430: }
431: p++;
432: }
433:
434: /* Low digit part must be there. */
435: if (!digit || !separator)
436: goto error;
437:
438: /* Encode result into routing distinguisher. */
439: if (dot)
440: {
441: if (val > UINT16_MAX)
442: goto error;
443:
444: eval->val[0] = ECOMMUNITY_ENCODE_IP;
445: eval->val[1] = 0;
446: memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
447: eval->val[6] = (val >> 8) & 0xff;
448: eval->val[7] = val & 0xff;
449: }
450: else if (as > BGP_AS_MAX)
451: {
452: if (val > UINT16_MAX)
453: goto error;
454:
455: eval->val[0] = ECOMMUNITY_ENCODE_AS4;
456: eval->val[1] = 0;
457: eval->val[2] = (as >>24) & 0xff;
458: eval->val[3] = (as >>16) & 0xff;
459: eval->val[4] = (as >>8) & 0xff;
460: eval->val[5] = as & 0xff;
461: eval->val[6] = (val >> 8) & 0xff;
462: eval->val[7] = val & 0xff;
463: }
464: else
465: {
466: eval->val[0] = ECOMMUNITY_ENCODE_AS;
467: eval->val[1] = 0;
468:
469: eval->val[2] = (as >>8) & 0xff;
470: eval->val[3] = as & 0xff;
471: eval->val[4] = (val >>24) & 0xff;
472: eval->val[5] = (val >>16) & 0xff;
473: eval->val[6] = (val >>8) & 0xff;
474: eval->val[7] = val & 0xff;
475: }
476: *token = ecommunity_token_val;
477: return p;
478:
479: error:
480: *token = ecommunity_token_unknown;
481: return p;
482: }
483:
484: /* Convert string to extended community attribute.
485:
486: When type is already known, please specify both str and type. str
487: should not include keyword such as "rt" and "soo". Type is
488: ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
489: keyword_included should be zero.
490:
491: For example route-map's "set extcommunity" command case:
492:
493: "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
494: type = ECOMMUNITY_ROUTE_TARGET
495: keyword_included = 0
496:
497: "soo 100:1" -> str = "100:1"
498: type = ECOMMUNITY_SITE_ORIGIN
499: keyword_included = 0
500:
501: When string includes keyword for each extended community value.
502: Please specify keyword_included as non-zero value.
503:
504: For example standard extcommunity-list case:
505:
506: "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
507: type = 0
508: keyword_include = 1
509: */
510: struct ecommunity *
511: ecommunity_str2com (const char *str, int type, int keyword_included)
512: {
513: struct ecommunity *ecom = NULL;
514: enum ecommunity_token token;
515: struct ecommunity_val eval;
516: int keyword = 0;
517:
518: while ((str = ecommunity_gettoken (str, &eval, &token)))
519: {
520: switch (token)
521: {
522: case ecommunity_token_rt:
523: case ecommunity_token_soo:
524: if (! keyword_included || keyword)
525: {
526: if (ecom)
527: ecommunity_free (&ecom);
528: return NULL;
529: }
530: keyword = 1;
531:
532: if (token == ecommunity_token_rt)
533: {
534: type = ECOMMUNITY_ROUTE_TARGET;
535: }
536: if (token == ecommunity_token_soo)
537: {
538: type = ECOMMUNITY_SITE_ORIGIN;
539: }
540: break;
541: case ecommunity_token_val:
542: if (keyword_included)
543: {
544: if (! keyword)
545: {
546: if (ecom)
547: ecommunity_free (&ecom);
548: return NULL;
549: }
550: keyword = 0;
551: }
552: if (ecom == NULL)
553: ecom = ecommunity_new ();
554: eval.val[1] = type;
555: ecommunity_add_val (ecom, &eval);
556: break;
557: case ecommunity_token_unknown:
558: default:
559: if (ecom)
560: ecommunity_free (&ecom);
561: return NULL;
562: }
563: }
564: return ecom;
565: }
566:
567: /* Convert extended community attribute to string.
568:
569: Due to historical reason of industry standard implementation, there
570: are three types of format.
571:
572: route-map set extcommunity format
573: "rt 100:1 100:2"
574: "soo 100:3"
575:
576: extcommunity-list
577: "rt 100:1 rt 100:2 soo 100:3"
578:
579: "show ip bgp" and extcommunity-list regular expression matching
580: "RT:100:1 RT:100:2 SoO:100:3"
581:
582: For each formath please use below definition for format:
583:
584: ECOMMUNITY_FORMAT_ROUTE_MAP
585: ECOMMUNITY_FORMAT_COMMUNITY_LIST
586: ECOMMUNITY_FORMAT_DISPLAY
587: */
588: char *
589: ecommunity_ecom2str (struct ecommunity *ecom, int format)
590: {
591: int i;
592: u_int8_t *pnt;
593: int encode = 0;
594: int type = 0;
595: #define ECOMMUNITY_STR_DEFAULT_LEN 27
596: int str_size;
597: int str_pnt;
598: char *str_buf;
599: const char *prefix;
600: int len = 0;
601: int first = 1;
602:
603: /* For parse Extended Community attribute tupple. */
604: struct ecommunity_as
605: {
606: as_t as;
607: u_int32_t val;
608: } eas;
609:
610: struct ecommunity_ip
611: {
612: struct in_addr ip;
613: u_int16_t val;
614: } eip;
615:
616: if (ecom->size == 0)
617: {
618: str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
619: str_buf[0] = '\0';
620: return str_buf;
621: }
622:
623: /* Prepare buffer. */
624: str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1);
625: str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1;
626: str_pnt = 0;
627:
628: for (i = 0; i < ecom->size; i++)
629: {
630: /* Make it sure size is enough. */
631: while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
632: {
633: str_size *= 2;
634: str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size);
635: }
636:
637: /* Space between each value. */
638: if (! first)
639: str_buf[str_pnt++] = ' ';
640:
641: pnt = ecom->val + (i * 8);
642:
643: /* High-order octet of type. */
644: encode = *pnt++;
645: if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP
646: && encode != ECOMMUNITY_ENCODE_AS4)
647: {
648: len = sprintf (str_buf + str_pnt, "?");
649: str_pnt += len;
650: first = 0;
651: continue;
652: }
653:
654: /* Low-order octet of type. */
655: type = *pnt++;
656: if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
657: {
658: len = sprintf (str_buf + str_pnt, "?");
659: str_pnt += len;
660: first = 0;
661: continue;
662: }
663:
664: switch (format)
665: {
666: case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
667: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
668: break;
669: case ECOMMUNITY_FORMAT_DISPLAY:
670: prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
671: break;
672: case ECOMMUNITY_FORMAT_ROUTE_MAP:
673: prefix = "";
674: break;
675: default:
676: prefix = "";
677: break;
678: }
679:
680: /* Put string into buffer. */
681: if (encode == ECOMMUNITY_ENCODE_AS4)
682: {
683: eas.as = (*pnt++ << 24);
684: eas.as |= (*pnt++ << 16);
685: eas.as |= (*pnt++ << 8);
686: eas.as |= (*pnt++);
687:
688: eas.val = (*pnt++ << 8);
689: eas.val |= (*pnt++);
690:
691: len = sprintf( str_buf + str_pnt, "%s%u:%d", prefix,
692: eas.as, eas.val );
693: str_pnt += len;
694: first = 0;
695: }
696: if (encode == ECOMMUNITY_ENCODE_AS)
697: {
698: eas.as = (*pnt++ << 8);
699: eas.as |= (*pnt++);
700:
701: eas.val = (*pnt++ << 24);
702: eas.val |= (*pnt++ << 16);
703: eas.val |= (*pnt++ << 8);
704: eas.val |= (*pnt++);
705:
706: len = sprintf (str_buf + str_pnt, "%s%u:%d", prefix,
707: eas.as, eas.val);
708: str_pnt += len;
709: first = 0;
710: }
711: else if (encode == ECOMMUNITY_ENCODE_IP)
712: {
713: memcpy (&eip.ip, pnt, 4);
714: pnt += 4;
715: eip.val = (*pnt++ << 8);
716: eip.val |= (*pnt++);
717:
718: len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix,
719: inet_ntoa (eip.ip), eip.val);
720: str_pnt += len;
721: first = 0;
722: }
723: }
724: return str_buf;
725: }
726:
727: int
728: ecommunity_match (const struct ecommunity *ecom1,
729: const struct ecommunity *ecom2)
730: {
731: int i = 0;
732: int j = 0;
733:
734: if (ecom1 == NULL && ecom2 == NULL)
735: return 1;
736:
737: if (ecom1 == NULL || ecom2 == NULL)
738: return 0;
739:
740: if (ecom1->size < ecom2->size)
741: return 0;
742:
743: /* Every community on com2 needs to be on com1 for this to match */
744: while (i < ecom1->size && j < ecom2->size)
745: {
746: if (memcmp (ecom1->val + i, ecom2->val + j, ECOMMUNITY_SIZE) == 0)
747: j++;
748: i++;
749: }
750:
751: if (j == ecom2->size)
752: return 1;
753: else
754: return 0;
755: }
756:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>