Annotation of embedaddon/quagga/bgpd/bgp_community.c, revision 1.1.1.1
1.1 misho 1: /* Community attribute related functions.
2: Copyright (C) 1998, 2001 Kunihiro Ishiguro
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:
26: #include "bgpd/bgp_community.h"
27:
28: /* Hash of community attribute. */
29: static struct hash *comhash;
30:
31: /* Allocate a new communities value. */
32: static struct community *
33: community_new (void)
34: {
35: return (struct community *) XCALLOC (MTYPE_COMMUNITY,
36: sizeof (struct community));
37: }
38:
39: /* Free communities value. */
40: void
41: community_free (struct community *com)
42: {
43: if (com->val)
44: XFREE (MTYPE_COMMUNITY_VAL, com->val);
45: if (com->str)
46: XFREE (MTYPE_COMMUNITY_STR, com->str);
47: XFREE (MTYPE_COMMUNITY, com);
48: }
49:
50: /* Add one community value to the community. */
51: static void
52: community_add_val (struct community *com, u_int32_t val)
53: {
54: com->size++;
55: if (com->val)
56: com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val, com_length (com));
57: else
58: com->val = XMALLOC (MTYPE_COMMUNITY_VAL, com_length (com));
59:
60: val = htonl (val);
61: memcpy (com_lastval (com), &val, sizeof (u_int32_t));
62: }
63:
64: /* Delete one community. */
65: void
66: community_del_val (struct community *com, u_int32_t *val)
67: {
68: int i = 0;
69: int c = 0;
70:
71: if (! com->val)
72: return;
73:
74: while (i < com->size)
75: {
76: if (memcmp (com->val + i, val, sizeof (u_int32_t)) == 0)
77: {
78: c = com->size -i -1;
79:
80: if (c > 0)
81: memcpy (com->val + i, com->val + (i + 1), c * sizeof (*val));
82:
83: com->size--;
84:
85: if (com->size > 0)
86: com->val = XREALLOC (MTYPE_COMMUNITY_VAL, com->val,
87: com_length (com));
88: else
89: {
90: XFREE (MTYPE_COMMUNITY_VAL, com->val);
91: com->val = NULL;
92: }
93: return;
94: }
95: i++;
96: }
97: }
98:
99: /* Delete all communities listed in com2 from com1 */
100: struct community *
101: community_delete (struct community *com1, struct community *com2)
102: {
103: int i = 0;
104:
105: while(i < com2->size)
106: {
107: community_del_val (com1, com2->val + i);
108: i++;
109: }
110:
111: return com1;
112: }
113:
114: /* Callback function from qsort(). */
115: static int
116: community_compare (const void *a1, const void *a2)
117: {
118: u_int32_t v1;
119: u_int32_t v2;
120:
121: memcpy (&v1, a1, sizeof (u_int32_t));
122: memcpy (&v2, a2, sizeof (u_int32_t));
123: v1 = ntohl (v1);
124: v2 = ntohl (v2);
125:
126: if (v1 < v2)
127: return -1;
128: if (v1 > v2)
129: return 1;
130: return 0;
131: }
132:
133: int
134: community_include (struct community *com, u_int32_t val)
135: {
136: int i;
137:
138: val = htonl (val);
139:
140: for (i = 0; i < com->size; i++)
141: if (memcmp (&val, com_nthval (com, i), sizeof (u_int32_t)) == 0)
142: return 1;
143:
144: return 0;
145: }
146:
147: static u_int32_t
148: community_val_get (struct community *com, int i)
149: {
150: u_char *p;
151: u_int32_t val;
152:
153: p = (u_char *) com->val;
154: p += (i * 4);
155:
156: memcpy (&val, p, sizeof (u_int32_t));
157:
158: return ntohl (val);
159: }
160:
161: /* Sort and uniq given community. */
162: struct community *
163: community_uniq_sort (struct community *com)
164: {
165: int i;
166: struct community *new;
167: u_int32_t val;
168:
169: if (! com)
170: return NULL;
171:
172: new = community_new ();;
173:
174: for (i = 0; i < com->size; i++)
175: {
176: val = community_val_get (com, i);
177:
178: if (! community_include (new, val))
179: community_add_val (new, val);
180: }
181:
182: qsort (new->val, new->size, sizeof (u_int32_t), community_compare);
183:
184: return new;
185: }
186:
187: /* Convert communities attribute to string.
188:
189: For Well-known communities value, below keyword is used.
190:
191: 0x0 "internet"
192: 0xFFFFFF01 "no-export"
193: 0xFFFFFF02 "no-advertise"
194: 0xFFFFFF03 "local-AS"
195:
196: For other values, "AS:VAL" format is used. */
197: static char *
198: community_com2str (struct community *com)
199: {
200: int i;
201: char *str;
202: char *pnt;
203: int len;
204: int first;
205: u_int32_t comval;
206: u_int16_t as;
207: u_int16_t val;
208:
209: if (!com)
210: return NULL;
211:
212: /* When communities attribute is empty. */
213: if (com->size == 0)
214: {
215: str = XMALLOC (MTYPE_COMMUNITY_STR, 1);
216: str[0] = '\0';
217: return str;
218: }
219:
220: /* Memory allocation is time consuming work. So we calculate
221: required string length first. */
222: len = 0;
223:
224: for (i = 0; i < com->size; i++)
225: {
226: memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
227: comval = ntohl (comval);
228:
229: switch (comval)
230: {
231: case COMMUNITY_INTERNET:
232: len += strlen (" internet");
233: break;
234: case COMMUNITY_NO_EXPORT:
235: len += strlen (" no-export");
236: break;
237: case COMMUNITY_NO_ADVERTISE:
238: len += strlen (" no-advertise");
239: break;
240: case COMMUNITY_LOCAL_AS:
241: len += strlen (" local-AS");
242: break;
243: default:
244: len += strlen (" 65536:65535");
245: break;
246: }
247: }
248:
249: /* Allocate memory. */
250: str = pnt = XMALLOC (MTYPE_COMMUNITY_STR, len);
251: first = 1;
252:
253: /* Fill in string. */
254: for (i = 0; i < com->size; i++)
255: {
256: memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t));
257: comval = ntohl (comval);
258:
259: if (first)
260: first = 0;
261: else
262: *pnt++ = ' ';
263:
264: switch (comval)
265: {
266: case COMMUNITY_INTERNET:
267: strcpy (pnt, "internet");
268: pnt += strlen ("internet");
269: break;
270: case COMMUNITY_NO_EXPORT:
271: strcpy (pnt, "no-export");
272: pnt += strlen ("no-export");
273: break;
274: case COMMUNITY_NO_ADVERTISE:
275: strcpy (pnt, "no-advertise");
276: pnt += strlen ("no-advertise");
277: break;
278: case COMMUNITY_LOCAL_AS:
279: strcpy (pnt, "local-AS");
280: pnt += strlen ("local-AS");
281: break;
282: default:
283: as = (comval >> 16) & 0xFFFF;
284: val = comval & 0xFFFF;
285: sprintf (pnt, "%u:%d", as, val);
286: pnt += strlen (pnt);
287: break;
288: }
289: }
290: *pnt = '\0';
291:
292: return str;
293: }
294:
295: /* Intern communities attribute. */
296: struct community *
297: community_intern (struct community *com)
298: {
299: struct community *find;
300:
301: /* Assert this community structure is not interned. */
302: assert (com->refcnt == 0);
303:
304: /* Lookup community hash. */
305: find = (struct community *) hash_get (comhash, com, hash_alloc_intern);
306:
307: /* Arguemnt com is allocated temporary. So when it is not used in
308: hash, it should be freed. */
309: if (find != com)
310: community_free (com);
311:
312: /* Increment refrence counter. */
313: find->refcnt++;
314:
315: /* Make string. */
316: if (! find->str)
317: find->str = community_com2str (find);
318:
319: return find;
320: }
321:
322: /* Free community attribute. */
323: void
324: community_unintern (struct community **com)
325: {
326: struct community *ret;
327:
328: if ((*com)->refcnt)
329: (*com)->refcnt--;
330:
331: /* Pull off from hash. */
332: if ((*com)->refcnt == 0)
333: {
334: /* Community value com must exist in hash. */
335: ret = (struct community *) hash_release (comhash, *com);
336: assert (ret != NULL);
337:
338: community_free (*com);
339: *com = NULL;
340: }
341: }
342:
343: /* Create new community attribute. */
344: struct community *
345: community_parse (u_int32_t *pnt, u_short length)
346: {
347: struct community tmp;
348: struct community *new;
349:
350: /* If length is malformed return NULL. */
351: if (length % 4)
352: return NULL;
353:
354: /* Make temporary community for hash look up. */
355: tmp.size = length / 4;
356: tmp.val = pnt;
357:
358: new = community_uniq_sort (&tmp);
359:
360: return community_intern (new);
361: }
362:
363: struct community *
364: community_dup (struct community *com)
365: {
366: struct community *new;
367:
368: new = XCALLOC (MTYPE_COMMUNITY, sizeof (struct community));
369: new->size = com->size;
370: if (new->size)
371: {
372: new->val = XMALLOC (MTYPE_COMMUNITY_VAL, com->size * 4);
373: memcpy (new->val, com->val, com->size * 4);
374: }
375: else
376: new->val = NULL;
377: return new;
378: }
379:
380: /* Retrun string representation of communities attribute. */
381: char *
382: community_str (struct community *com)
383: {
384: if (!com)
385: return NULL;
386:
387: if (! com->str)
388: com->str = community_com2str (com);
389: return com->str;
390: }
391:
392: /* Make hash value of community attribute. This function is used by
393: hash package.*/
394: unsigned int
395: community_hash_make (struct community *com)
396: {
397: int c;
398: unsigned int key;
399: unsigned char *pnt;
400:
401: key = 0;
402: pnt = (unsigned char *)com->val;
403:
404: for(c = 0; c < com->size * 4; c++)
405: key += pnt[c];
406:
407: return key;
408: }
409:
410: int
411: community_match (const struct community *com1, const struct community *com2)
412: {
413: int i = 0;
414: int j = 0;
415:
416: if (com1 == NULL && com2 == NULL)
417: return 1;
418:
419: if (com1 == NULL || com2 == NULL)
420: return 0;
421:
422: if (com1->size < com2->size)
423: return 0;
424:
425: /* Every community on com2 needs to be on com1 for this to match */
426: while (i < com1->size && j < com2->size)
427: {
428: if (memcmp (com1->val + i, com2->val + j, sizeof (u_int32_t)) == 0)
429: j++;
430: i++;
431: }
432:
433: if (j == com2->size)
434: return 1;
435: else
436: return 0;
437: }
438:
439: /* If two aspath have same value then return 1 else return 0. This
440: function is used by hash package. */
441: int
442: community_cmp (const struct community *com1, const struct community *com2)
443: {
444: if (com1 == NULL && com2 == NULL)
445: return 1;
446: if (com1 == NULL || com2 == NULL)
447: return 0;
448:
449: if (com1->size == com2->size)
450: if (memcmp (com1->val, com2->val, com1->size * 4) == 0)
451: return 1;
452: return 0;
453: }
454:
455: /* Add com2 to the end of com1. */
456: struct community *
457: community_merge (struct community *com1, struct community *com2)
458: {
459: if (com1->val)
460: com1->val = XREALLOC (MTYPE_COMMUNITY_VAL, com1->val,
461: (com1->size + com2->size) * 4);
462: else
463: com1->val = XMALLOC (MTYPE_COMMUNITY_VAL, (com1->size + com2->size) * 4);
464:
465: memcpy (com1->val + com1->size, com2->val, com2->size * 4);
466: com1->size += com2->size;
467:
468: return com1;
469: }
470:
471: /* Community token enum. */
472: enum community_token
473: {
474: community_token_val,
475: community_token_no_export,
476: community_token_no_advertise,
477: community_token_local_as,
478: community_token_unknown
479: };
480:
481: /* Get next community token from string. */
482: static const char *
483: community_gettoken (const char *buf, enum community_token *token,
484: u_int32_t *val)
485: {
486: const char *p = buf;
487:
488: /* Skip white space. */
489: while (isspace ((int) *p))
490: p++;
491:
492: /* Check the end of the line. */
493: if (*p == '\0')
494: return NULL;
495:
496: /* Well known community string check. */
497: if (isalpha ((int) *p))
498: {
499: if (strncmp (p, "internet", strlen ("internet")) == 0)
500: {
501: *val = COMMUNITY_INTERNET;
502: *token = community_token_no_export;
503: p += strlen ("internet");
504: return p;
505: }
506: if (strncmp (p, "no-export", strlen ("no-export")) == 0)
507: {
508: *val = COMMUNITY_NO_EXPORT;
509: *token = community_token_no_export;
510: p += strlen ("no-export");
511: return p;
512: }
513: if (strncmp (p, "no-advertise", strlen ("no-advertise")) == 0)
514: {
515: *val = COMMUNITY_NO_ADVERTISE;
516: *token = community_token_no_advertise;
517: p += strlen ("no-advertise");
518: return p;
519: }
520: if (strncmp (p, "local-AS", strlen ("local-AS")) == 0)
521: {
522: *val = COMMUNITY_LOCAL_AS;
523: *token = community_token_local_as;
524: p += strlen ("local-AS");
525: return p;
526: }
527:
528: /* Unknown string. */
529: *token = community_token_unknown;
530: return NULL;
531: }
532:
533: /* Community value. */
534: if (isdigit ((int) *p))
535: {
536: int separator = 0;
537: int digit = 0;
538: u_int32_t community_low = 0;
539: u_int32_t community_high = 0;
540:
541: while (isdigit ((int) *p) || *p == ':')
542: {
543: if (*p == ':')
544: {
545: if (separator)
546: {
547: *token = community_token_unknown;
548: return NULL;
549: }
550: else
551: {
552: separator = 1;
553: digit = 0;
554: community_high = community_low << 16;
555: community_low = 0;
556: }
557: }
558: else
559: {
560: digit = 1;
561: community_low *= 10;
562: community_low += (*p - '0');
563: }
564: p++;
565: }
566: if (! digit)
567: {
568: *token = community_token_unknown;
569: return NULL;
570: }
571: *val = community_high + community_low;
572: *token = community_token_val;
573: return p;
574: }
575: *token = community_token_unknown;
576: return NULL;
577: }
578:
579: /* convert string to community structure */
580: struct community *
581: community_str2com (const char *str)
582: {
583: struct community *com = NULL;
584: struct community *com_sort = NULL;
585: u_int32_t val = 0;
586: enum community_token token = community_token_unknown;
587:
588: do
589: {
590: str = community_gettoken (str, &token, &val);
591:
592: switch (token)
593: {
594: case community_token_val:
595: case community_token_no_export:
596: case community_token_no_advertise:
597: case community_token_local_as:
598: if (com == NULL)
599: com = community_new();
600: community_add_val (com, val);
601: break;
602: case community_token_unknown:
603: default:
604: if (com)
605: community_free (com);
606: return NULL;
607: }
608: } while (str);
609:
610: if (! com)
611: return NULL;
612:
613: com_sort = community_uniq_sort (com);
614: community_free (com);
615:
616: return com_sort;
617: }
618:
619: /* Return communities hash entry count. */
620: unsigned long
621: community_count (void)
622: {
623: return comhash->count;
624: }
625:
626: /* Return communities hash. */
627: struct hash *
628: community_hash (void)
629: {
630: return comhash;
631: }
632:
633: /* Initialize comminity related hash. */
634: void
635: community_init (void)
636: {
637: comhash = hash_create ((unsigned int (*) (void *))community_hash_make,
638: (int (*) (const void *, const void *))community_cmp);
639: }
640:
641: void
642: community_finish (void)
643: {
644: hash_free (comhash);
645: comhash = NULL;
646: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>