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>