Annotation of embedaddon/bird/nest/rt-fib.c, revision 1.1.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>