Annotation of embedaddon/quagga/bgpd/bgp_community.c, revision 1.1.1.3
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)
1.1.1.3 ! misho 81: memmove (com->val + i, com->val + (i + 1), c * sizeof (*val));
1.1 misho 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:
1.1.1.3 ! misho 147: u_int32_t
1.1 misho 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: {
1.1.1.2 misho 397: unsigned char *pnt = (unsigned char *)com->val;
398: int size = com->size * 4;
399: unsigned int key = 0;
1.1 misho 400: int c;
401:
1.1.1.2 misho 402: for (c = 0; c < size; c += 4)
403: {
404: key += pnt[c];
405: key += pnt[c + 1];
406: key += pnt[c + 2];
407: key += pnt[c + 3];
408: }
409:
1.1 misho 410: return key;
411: }
412:
413: int
414: community_match (const struct community *com1, const struct community *com2)
415: {
416: int i = 0;
417: int j = 0;
418:
419: if (com1 == NULL && com2 == NULL)
420: return 1;
421:
422: if (com1 == NULL || com2 == NULL)
423: return 0;
424:
425: if (com1->size < com2->size)
426: return 0;
427:
428: /* Every community on com2 needs to be on com1 for this to match */
429: while (i < com1->size && j < com2->size)
430: {
431: if (memcmp (com1->val + i, com2->val + j, sizeof (u_int32_t)) == 0)
432: j++;
433: i++;
434: }
435:
436: if (j == com2->size)
437: return 1;
438: else
439: return 0;
440: }
441:
442: /* If two aspath have same value then return 1 else return 0. This
443: function is used by hash package. */
444: int
445: community_cmp (const struct community *com1, const struct community *com2)
446: {
447: if (com1 == NULL && com2 == NULL)
448: return 1;
449: if (com1 == NULL || com2 == NULL)
450: return 0;
451:
452: if (com1->size == com2->size)
453: if (memcmp (com1->val, com2->val, com1->size * 4) == 0)
454: return 1;
455: return 0;
456: }
457:
458: /* Add com2 to the end of com1. */
459: struct community *
460: community_merge (struct community *com1, struct community *com2)
461: {
462: if (com1->val)
463: com1->val = XREALLOC (MTYPE_COMMUNITY_VAL, com1->val,
464: (com1->size + com2->size) * 4);
465: else
466: com1->val = XMALLOC (MTYPE_COMMUNITY_VAL, (com1->size + com2->size) * 4);
467:
468: memcpy (com1->val + com1->size, com2->val, com2->size * 4);
469: com1->size += com2->size;
470:
471: return com1;
472: }
473:
474: /* Community token enum. */
475: enum community_token
476: {
477: community_token_val,
478: community_token_no_export,
479: community_token_no_advertise,
480: community_token_local_as,
481: community_token_unknown
482: };
483:
484: /* Get next community token from string. */
485: static const char *
486: community_gettoken (const char *buf, enum community_token *token,
487: u_int32_t *val)
488: {
489: const char *p = buf;
490:
491: /* Skip white space. */
492: while (isspace ((int) *p))
493: p++;
494:
495: /* Check the end of the line. */
496: if (*p == '\0')
497: return NULL;
498:
499: /* Well known community string check. */
500: if (isalpha ((int) *p))
501: {
502: if (strncmp (p, "internet", strlen ("internet")) == 0)
503: {
504: *val = COMMUNITY_INTERNET;
505: *token = community_token_no_export;
506: p += strlen ("internet");
507: return p;
508: }
509: if (strncmp (p, "no-export", strlen ("no-export")) == 0)
510: {
511: *val = COMMUNITY_NO_EXPORT;
512: *token = community_token_no_export;
513: p += strlen ("no-export");
514: return p;
515: }
516: if (strncmp (p, "no-advertise", strlen ("no-advertise")) == 0)
517: {
518: *val = COMMUNITY_NO_ADVERTISE;
519: *token = community_token_no_advertise;
520: p += strlen ("no-advertise");
521: return p;
522: }
523: if (strncmp (p, "local-AS", strlen ("local-AS")) == 0)
524: {
525: *val = COMMUNITY_LOCAL_AS;
526: *token = community_token_local_as;
527: p += strlen ("local-AS");
528: return p;
529: }
530:
531: /* Unknown string. */
532: *token = community_token_unknown;
533: return NULL;
534: }
535:
536: /* Community value. */
537: if (isdigit ((int) *p))
538: {
539: int separator = 0;
540: int digit = 0;
541: u_int32_t community_low = 0;
542: u_int32_t community_high = 0;
543:
544: while (isdigit ((int) *p) || *p == ':')
545: {
546: if (*p == ':')
547: {
548: if (separator)
549: {
550: *token = community_token_unknown;
551: return NULL;
552: }
553: else
554: {
555: separator = 1;
556: digit = 0;
557: community_high = community_low << 16;
558: community_low = 0;
559: }
560: }
561: else
562: {
563: digit = 1;
564: community_low *= 10;
565: community_low += (*p - '0');
566: }
567: p++;
568: }
569: if (! digit)
570: {
571: *token = community_token_unknown;
572: return NULL;
573: }
574: *val = community_high + community_low;
575: *token = community_token_val;
576: return p;
577: }
578: *token = community_token_unknown;
579: return NULL;
580: }
581:
582: /* convert string to community structure */
583: struct community *
584: community_str2com (const char *str)
585: {
586: struct community *com = NULL;
587: struct community *com_sort = NULL;
588: u_int32_t val = 0;
589: enum community_token token = community_token_unknown;
590:
591: do
592: {
593: str = community_gettoken (str, &token, &val);
594:
595: switch (token)
596: {
597: case community_token_val:
598: case community_token_no_export:
599: case community_token_no_advertise:
600: case community_token_local_as:
601: if (com == NULL)
602: com = community_new();
603: community_add_val (com, val);
604: break;
605: case community_token_unknown:
606: default:
607: if (com)
608: community_free (com);
609: return NULL;
610: }
611: } while (str);
612:
613: if (! com)
614: return NULL;
615:
616: com_sort = community_uniq_sort (com);
617: community_free (com);
618:
619: return com_sort;
620: }
621:
622: /* Return communities hash entry count. */
623: unsigned long
624: community_count (void)
625: {
626: return comhash->count;
627: }
628:
629: /* Return communities hash. */
630: struct hash *
631: community_hash (void)
632: {
633: return comhash;
634: }
635:
636: /* Initialize comminity related hash. */
637: void
638: community_init (void)
639: {
640: comhash = hash_create ((unsigned int (*) (void *))community_hash_make,
641: (int (*) (const void *, const void *))community_cmp);
642: }
643:
644: void
645: community_finish (void)
646: {
647: hash_free (comhash);
648: comhash = NULL;
649: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>