Annotation of embedaddon/bird/nest/rt-fib.c, revision 1.1.1.2
1.1 misho 1: /*
2: * BIRD -- Forwarding Information Base -- Data Structures
3: *
4: * (c) 1998--2000 Martin Mares <mj@ucw.cz>
5: *
6: * Can be freely distributed and used under the terms of the GNU GPL.
7: */
8:
9: /**
10: * DOC: Forwarding Information Base
11: *
12: * FIB is a data structure designed for storage of routes indexed by their
13: * network prefixes. It supports insertion, deletion, searching by prefix,
14: * `routing' (in CIDR sense, that is searching for a longest prefix matching
15: * a given IP address) and (which makes the structure very tricky to implement)
16: * asynchronous reading, that is enumerating the contents of a FIB while other
17: * modules add, modify or remove entries.
18: *
19: * Internally, each FIB is represented as a collection of nodes of type &fib_node
20: * indexed using a sophisticated hashing mechanism.
21: * We use two-stage hashing where we calculate a 16-bit primary hash key independent
22: * on hash table size and then we just divide the primary keys modulo table size
23: * to get a real hash key used for determining the bucket containing the node.
24: * The lists of nodes in each bucket are sorted according to the primary hash
25: * key, hence if we keep the total number of buckets to be a power of two,
26: * re-hashing of the structure keeps the relative order of the nodes.
27: *
28: * To get the asynchronous reading consistent over node deletions, we need to
29: * keep a list of readers for each node. When a node gets deleted, its readers
30: * are automatically moved to the next node in the table.
31: *
32: * Basic FIB operations are performed by functions defined by this module,
33: * enumerating of FIB contents is accomplished by using the FIB_WALK() macro
34: * or FIB_ITERATE_START() if you want to do it asynchronously.
1.1.1.2 ! misho 35: *
! 36: * For simple iteration just place the body of the loop between FIB_WALK() and
! 37: * FIB_WALK_END(). You can't modify the FIB during the iteration (you can modify
! 38: * data in the node, but not add or remove nodes).
! 39: *
! 40: * If you need more freedom, you can use the FIB_ITERATE_*() group of macros.
! 41: * First, you initialize an iterator with FIB_ITERATE_INIT(). Then you can put
! 42: * the loop body in between FIB_ITERATE_START() and FIB_ITERATE_END(). In
! 43: * addition, the iteration can be suspended by calling FIB_ITERATE_PUT().
! 44: * This'll link the iterator inside the FIB. While suspended, you may modify the
! 45: * FIB, exit the current function, etc. To resume the iteration, enter the loop
! 46: * again. You can use FIB_ITERATE_UNLINK() to unlink the iterator (while
! 47: * iteration is suspended) in cases like premature end of FIB iteration.
! 48: *
! 49: * Note that the iterator must not be destroyed when the iteration is suspended,
! 50: * the FIB would then contain a pointer to invalid memory. Therefore, after each
! 51: * FIB_ITERATE_INIT() or FIB_ITERATE_PUT() there must be either
! 52: * FIB_ITERATE_START() or FIB_ITERATE_UNLINK() before the iterator is destroyed.
1.1 misho 53: */
54:
55: #undef LOCAL_DEBUG
56:
57: #include "nest/bird.h"
58: #include "nest/route.h"
59: #include "lib/string.h"
60:
61: #define HASH_DEF_ORDER 10
62: #define HASH_HI_MARK *4
63: #define HASH_HI_STEP 2
64: #define HASH_HI_MAX 16 /* Must be at most 16 */
65: #define HASH_LO_MARK /5
66: #define HASH_LO_STEP 2
67: #define HASH_LO_MIN 10
68:
69: static void
70: fib_ht_alloc(struct fib *f)
71: {
72: f->hash_size = 1 << f->hash_order;
73: f->hash_shift = 16 - f->hash_order;
74: if (f->hash_order > HASH_HI_MAX - HASH_HI_STEP)
75: f->entries_max = ~0;
76: else
77: f->entries_max = f->hash_size HASH_HI_MARK;
78: if (f->hash_order < HASH_LO_MIN + HASH_LO_STEP)
79: f->entries_min = 0;
80: else
81: f->entries_min = f->hash_size HASH_LO_MARK;
82: DBG("Allocating FIB hash of order %d: %d entries, %d low, %d high\n",
83: f->hash_order, f->hash_size, f->entries_min, f->entries_max);
84: f->hash_table = mb_alloc(f->fib_pool, f->hash_size * sizeof(struct fib_node *));
85: }
86:
87: static inline void
88: fib_ht_free(struct fib_node **h)
89: {
90: mb_free(h);
91: }
92:
93: static inline unsigned
94: fib_hash(struct fib *f, ip_addr *a)
95: {
96: return ipa_hash(*a) >> f->hash_shift;
97: }
98:
99: static void
100: fib_dummy_init(struct fib_node *dummy UNUSED)
101: {
102: }
103:
104: /**
105: * fib_init - initialize a new FIB
106: * @f: the FIB to be initialized (the structure itself being allocated by the caller)
107: * @p: pool to allocate the nodes in
108: * @node_size: node size to be used (each node consists of a standard header &fib_node
109: * followed by user data)
110: * @hash_order: initial hash order (a binary logarithm of hash table size), 0 to use default order
111: * (recommended)
112: * @init: pointer a function to be called to initialize a newly created node
113: *
114: * This function initializes a newly allocated FIB and prepares it for use.
115: */
116: void
117: fib_init(struct fib *f, pool *p, unsigned node_size, unsigned hash_order, fib_init_func init)
118: {
119: if (!hash_order)
120: hash_order = HASH_DEF_ORDER;
121: f->fib_pool = p;
122: f->fib_slab = sl_new(p, node_size);
123: f->hash_order = hash_order;
124: fib_ht_alloc(f);
125: bzero(f->hash_table, f->hash_size * sizeof(struct fib_node *));
126: f->entries = 0;
127: f->entries_min = 0;
128: f->init = init ? : fib_dummy_init;
129: }
130:
131: static void
132: fib_rehash(struct fib *f, int step)
133: {
134: unsigned old, new, oldn, newn, ni, nh;
135: struct fib_node **n, *e, *x, **t, **m, **h;
136:
137: old = f->hash_order;
138: oldn = f->hash_size;
139: new = old + step;
140: m = h = f->hash_table;
141: DBG("Re-hashing FIB from order %d to %d\n", old, new);
142: f->hash_order = new;
143: fib_ht_alloc(f);
144: t = n = f->hash_table;
145: newn = f->hash_size;
146: ni = 0;
147:
148: while (oldn--)
149: {
150: x = *h++;
151: while (e = x)
152: {
153: x = e->next;
154: nh = fib_hash(f, &e->prefix);
155: while (nh > ni)
156: {
157: *t = NULL;
158: ni++;
159: t = ++n;
160: }
161: *t = e;
162: t = &e->next;
163: }
164: }
165: while (ni < newn)
166: {
167: *t = NULL;
168: ni++;
169: t = ++n;
170: }
171: fib_ht_free(m);
172: }
173:
174: /**
175: * fib_find - search for FIB node by prefix
176: * @f: FIB to search in
177: * @a: pointer to IP address of the prefix
178: * @len: prefix length
179: *
180: * Search for a FIB node corresponding to the given prefix, return
181: * a pointer to it or %NULL if no such node exists.
182: */
183: void *
184: fib_find(struct fib *f, ip_addr *a, int len)
185: {
186: struct fib_node *e = f->hash_table[fib_hash(f, a)];
187:
188: while (e && (e->pxlen != len || !ipa_equal(*a, e->prefix)))
189: e = e->next;
190: return e;
191: }
192:
193: /*
194: int
195: fib_histogram(struct fib *f)
196: {
197: log(L_WARN "Histogram dump start %d %d", f->hash_size, f->entries);
198:
199: int i, j;
200: struct fib_node *e;
201:
202: for (i = 0; i < f->hash_size; i++)
203: {
204: j = 0;
205: for (e = f->hash_table[i]; e != NULL; e = e->next)
206: j++;
207: if (j > 0)
208: log(L_WARN "Histogram line %d: %d", i, j);
209: }
210:
211: log(L_WARN "Histogram dump end");
212: }
213: */
214:
215: /**
216: * fib_get - find or create a FIB node
217: * @f: FIB to work with
218: * @a: pointer to IP address of the prefix
219: * @len: prefix length
220: *
221: * Search for a FIB node corresponding to the given prefix and
222: * return a pointer to it. If no such node exists, create it.
223: */
224: void *
225: fib_get(struct fib *f, ip_addr *a, int len)
226: {
227: uint h = ipa_hash(*a);
228: struct fib_node **ee = f->hash_table + (h >> f->hash_shift);
229: struct fib_node *g, *e = *ee;
230: u32 uid = h << 16;
231:
232: while (e && (e->pxlen != len || !ipa_equal(*a, e->prefix)))
233: e = e->next;
234: if (e)
235: return e;
236: #ifdef DEBUGGING
237: if (len < 0 || len > BITS_PER_IP_ADDRESS || !ip_is_prefix(*a,len))
238: bug("fib_get() called for invalid address");
239: #endif
240:
241: while ((g = *ee) && g->uid < uid)
242: ee = &g->next;
243: while ((g = *ee) && g->uid == uid)
244: {
245: ee = &g->next;
246: uid++;
247: }
248:
249: if ((uid >> 16) != h)
250: log(L_ERR "FIB hash table chains are too long");
251:
252: // log (L_WARN "FIB_GET %I %x %x", *a, h, uid);
253:
254: e = sl_alloc(f->fib_slab);
255: e->prefix = *a;
256: e->pxlen = len;
257: e->next = *ee;
258: e->uid = uid;
259: *ee = e;
260: e->readers = NULL;
261: f->init(e);
262: if (f->entries++ > f->entries_max)
263: fib_rehash(f, HASH_HI_STEP);
264:
265: return e;
266: }
267:
268: /**
269: * fib_route - CIDR routing lookup
270: * @f: FIB to search in
271: * @a: pointer to IP address of the prefix
272: * @len: prefix length
273: *
274: * Search for a FIB node with longest prefix matching the given
275: * network, that is a node which a CIDR router would use for routing
276: * that network.
277: */
278: void *
279: fib_route(struct fib *f, ip_addr a, int len)
280: {
281: ip_addr a0;
282: void *t;
283:
284: while (len >= 0)
285: {
286: a0 = ipa_and(a, ipa_mkmask(len));
287: t = fib_find(f, &a0, len);
288: if (t)
289: return t;
290: len--;
291: }
292: return NULL;
293: }
294:
295: static inline void
296: fib_merge_readers(struct fib_iterator *i, struct fib_node *to)
297: {
298: if (to)
299: {
300: struct fib_iterator *j = to->readers;
301: if (!j)
302: {
303: /* Fast path */
304: to->readers = i;
305: i->prev = (struct fib_iterator *) to;
306: }
307: else
308: {
309: /* Really merging */
310: while (j->next)
311: j = j->next;
312: j->next = i;
313: i->prev = j;
314: }
315: while (i && i->node)
316: {
317: i->node = NULL;
318: i = i->next;
319: }
320: }
321: else /* No more nodes */
322: while (i)
323: {
324: i->prev = NULL;
325: i = i->next;
326: }
327: }
328:
329: /**
330: * fib_delete - delete a FIB node
331: * @f: FIB to delete from
332: * @E: entry to delete
333: *
334: * This function removes the given entry from the FIB,
335: * taking care of all the asynchronous readers by shifting
336: * them to the next node in the canonical reading order.
337: */
338: void
339: fib_delete(struct fib *f, void *E)
340: {
341: struct fib_node *e = E;
342: uint h = fib_hash(f, &e->prefix);
343: struct fib_node **ee = f->hash_table + h;
344: struct fib_iterator *it;
345:
346: while (*ee)
347: {
348: if (*ee == e)
349: {
350: *ee = e->next;
351: if (it = e->readers)
352: {
353: struct fib_node *l = e->next;
354: while (!l)
355: {
356: h++;
357: if (h >= f->hash_size)
358: break;
359: else
360: l = f->hash_table[h];
361: }
362: fib_merge_readers(it, l);
363: }
364: sl_free(f->fib_slab, e);
365: if (f->entries-- < f->entries_min)
366: fib_rehash(f, -HASH_LO_STEP);
367: return;
368: }
369: ee = &((*ee)->next);
370: }
371: bug("fib_delete() called for invalid node");
372: }
373:
374: /**
375: * fib_free - delete a FIB
376: * @f: FIB to be deleted
377: *
378: * This function deletes a FIB -- it frees all memory associated
379: * with it and all its entries.
380: */
381: void
382: fib_free(struct fib *f)
383: {
384: fib_ht_free(f->hash_table);
385: rfree(f->fib_slab);
386: }
387:
388: void
389: fit_init(struct fib_iterator *i, struct fib *f)
390: {
391: unsigned h;
392: struct fib_node *n;
393:
394: i->efef = 0xff;
395: for(h=0; h<f->hash_size; h++)
396: if (n = f->hash_table[h])
397: {
398: i->prev = (struct fib_iterator *) n;
399: if (i->next = n->readers)
400: i->next->prev = i;
401: n->readers = i;
402: i->node = n;
403: return;
404: }
405: /* The fib is empty, nothing to do */
406: i->prev = i->next = NULL;
407: i->node = NULL;
408: }
409:
410: struct fib_node *
411: fit_get(struct fib *f, struct fib_iterator *i)
412: {
413: struct fib_node *n;
414: struct fib_iterator *j, *k;
415:
416: if (!i->prev)
417: {
418: /* We are at the end */
419: i->hash = ~0 - 1;
420: return NULL;
421: }
422: if (!(n = i->node))
423: {
424: /* No node info available, we are a victim of merging. Try harder. */
425: j = i;
426: while (j->efef == 0xff)
427: j = j->prev;
428: n = (struct fib_node *) j;
429: }
430: j = i->prev;
431: if (k = i->next)
432: k->prev = j;
433: j->next = k;
434: i->hash = fib_hash(f, &n->prefix);
435: return n;
436: }
437:
438: void
439: fit_put(struct fib_iterator *i, struct fib_node *n)
440: {
441: struct fib_iterator *j;
442:
443: i->node = n;
444: if (j = n->readers)
445: j->prev = i;
446: i->next = j;
447: n->readers = i;
448: i->prev = (struct fib_iterator *) n;
449: }
450:
451: void
452: fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos)
453: {
454: if (n = n->next)
455: goto found;
456:
457: while (++hpos < f->hash_size)
458: if (n = f->hash_table[hpos])
459: goto found;
460:
461: /* We are at the end */
462: i->prev = i->next = NULL;
463: i->node = NULL;
464: return;
465:
466: found:
467: fit_put(i, n);
468: }
469:
470: #ifdef DEBUGGING
471:
472: /**
473: * fib_check - audit a FIB
474: * @f: FIB to be checked
475: *
476: * This debugging function audits a FIB by checking its internal consistency.
477: * Use when you suspect somebody of corrupting innocent data structures.
478: */
479: void
480: fib_check(struct fib *f)
481: {
482: uint i, ec, lo, nulls;
483:
484: ec = 0;
485: for(i=0; i<f->hash_size; i++)
486: {
487: struct fib_node *n;
488: lo = 0;
489: for(n=f->hash_table[i]; n; n=n->next)
490: {
491: struct fib_iterator *j, *j0;
492: uint h0 = ipa_hash(n->prefix);
493: if (h0 < lo)
494: bug("fib_check: discord in hash chains");
495: lo = h0;
496: if ((h0 >> f->hash_shift) != i)
497: bug("fib_check: mishashed %x->%x (order %d)", h0, i, f->hash_order);
498: j0 = (struct fib_iterator *) n;
499: nulls = 0;
500: for(j=n->readers; j; j=j->next)
501: {
502: if (j->prev != j0)
503: bug("fib_check: iterator->prev mismatch");
504: j0 = j;
505: if (!j->node)
506: nulls++;
507: else if (nulls)
508: bug("fib_check: iterator nullified");
509: else if (j->node != n)
510: bug("fib_check: iterator->node mismatch");
511: }
512: ec++;
513: }
514: }
515: if (ec != f->entries)
516: bug("fib_check: invalid entry count (%d != %d)", ec, f->entries);
517: }
518:
519: #endif
520:
521: #ifdef TEST
522:
523: #include "lib/resource.h"
524:
525: struct fib f;
526:
527: void dump(char *m)
528: {
529: uint i;
530:
531: debug("%s ... order=%d, size=%d, entries=%d\n", m, f.hash_order, f.hash_size, f.hash_size);
532: for(i=0; i<f.hash_size; i++)
533: {
534: struct fib_node *n;
535: struct fib_iterator *j;
536: for(n=f.hash_table[i]; n; n=n->next)
537: {
538: debug("%04x %04x %p %I/%2d", i, ipa_hash(n->prefix), n, n->prefix, n->pxlen);
539: for(j=n->readers; j; j=j->next)
540: debug(" %p[%p]", j, j->node);
541: debug("\n");
542: }
543: }
544: fib_check(&f);
545: debug("-----\n");
546: }
547:
548: void init(struct fib_node *n)
549: {
550: }
551:
552: int main(void)
553: {
554: struct fib_node *n;
555: struct fib_iterator i, j;
556: ip_addr a;
557: int c;
558:
559: log_init_debug(NULL);
560: resource_init();
561: fib_init(&f, &root_pool, sizeof(struct fib_node), 4, init);
562: dump("init");
563:
564: a = ipa_from_u32(0x01020304); n = fib_get(&f, &a, 32);
565: a = ipa_from_u32(0x02030405); n = fib_get(&f, &a, 32);
566: a = ipa_from_u32(0x03040506); n = fib_get(&f, &a, 32);
567: a = ipa_from_u32(0x00000000); n = fib_get(&f, &a, 32);
568: a = ipa_from_u32(0x00000c01); n = fib_get(&f, &a, 32);
569: a = ipa_from_u32(0xffffffff); n = fib_get(&f, &a, 32);
570: dump("fill");
571:
572: fit_init(&i, &f);
573: dump("iter init");
574:
575: fib_rehash(&f, 1);
576: dump("rehash up");
577:
578: fib_rehash(&f, -1);
579: dump("rehash down");
580:
581: next:
582: c = 0;
583: FIB_ITERATE_START(&f, &i, z)
584: {
585: if (c)
586: {
587: FIB_ITERATE_PUT(&i, z);
588: dump("iter");
589: goto next;
590: }
591: c = 1;
592: debug("got %p\n", z);
593: }
594: FIB_ITERATE_END(z);
595: dump("iter end");
596:
597: fit_init(&i, &f);
598: fit_init(&j, &f);
599: dump("iter init 2");
600:
601: n = fit_get(&f, &i);
602: dump("iter step 2");
603:
604: fit_put(&i, n->next);
605: dump("iter step 3");
606:
607: a = ipa_from_u32(0xffffffff); n = fib_get(&f, &a, 32);
608: fib_delete(&f, n);
609: dump("iter step 3");
610:
611: return 0;
612: }
613:
614: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>