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