Annotation of embedaddon/bird2/sysdep/unix/krt.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  *     BIRD -- UNIX Kernel Synchronization
        !             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: Kernel synchronization
        !            11:  *
        !            12:  * This system dependent module implements the Kernel and Device protocol,
        !            13:  * that is synchronization of interface lists and routing tables with the
        !            14:  * OS kernel.
        !            15:  *
        !            16:  * The whole kernel synchronization is a bit messy and touches some internals
        !            17:  * of the routing table engine, because routing table maintenance is a typical
        !            18:  * example of the proverbial compatibility between different Unices and we want
        !            19:  * to keep the overhead of our KRT business as low as possible and avoid maintaining
        !            20:  * a local routing table copy.
        !            21:  *
        !            22:  * The kernel syncer can work in three different modes (according to system config header):
        !            23:  * Either with a single routing table and single KRT protocol [traditional UNIX]
        !            24:  * or with many routing tables and separate KRT protocols for all of them
        !            25:  * or with many routing tables, but every scan including all tables, so we start
        !            26:  * separate KRT protocols which cooperate with each other [Linux].
        !            27:  * In this case, we keep only a single scan timer.
        !            28:  *
        !            29:  * We use FIB node flags in the routing table to keep track of route
        !            30:  * synchronization status. We also attach temporary &rte's to the routing table,
        !            31:  * but it cannot do any harm to the rest of BIRD since table synchronization is
        !            32:  * an atomic process.
        !            33:  *
        !            34:  * When starting up, we cheat by looking if there is another
        !            35:  * KRT instance to be initialized later and performing table scan
        !            36:  * only once for all the instances.
        !            37:  *
        !            38:  * The code uses OS-dependent parts for kernel updates and scans. These parts are
        !            39:  * in more specific sysdep directories (e.g. sysdep/linux) in functions krt_sys_*
        !            40:  * and kif_sys_* (and some others like krt_replace_rte()) and krt-sys.h header file.
        !            41:  * This is also used for platform specific protocol options and route attributes.
        !            42:  *
        !            43:  * There was also an old code that used traditional UNIX ioctls for these tasks.
        !            44:  * It was unmaintained and later removed. For reference, see sysdep/krt-* files
        !            45:  * in commit 396dfa9042305f62da1f56589c4b98fac57fc2f6
        !            46:  */
        !            47: 
        !            48: /*
        !            49:  *  If you are brave enough, continue now.  You cannot say you haven't been warned.
        !            50:  */
        !            51: 
        !            52: #undef LOCAL_DEBUG
        !            53: 
        !            54: #include "nest/bird.h"
        !            55: #include "nest/iface.h"
        !            56: #include "nest/route.h"
        !            57: #include "nest/protocol.h"
        !            58: #include "filter/filter.h"
        !            59: #include "conf/conf.h"
        !            60: #include "lib/string.h"
        !            61: #include "lib/timer.h"
        !            62: 
        !            63: #include "unix.h"
        !            64: #include "krt.h"
        !            65: 
        !            66: /*
        !            67:  *     Global resources
        !            68:  */
        !            69: 
        !            70: pool *krt_pool;
        !            71: static linpool *krt_filter_lp;
        !            72: static list krt_proto_list;
        !            73: 
        !            74: void
        !            75: krt_io_init(void)
        !            76: {
        !            77:   krt_pool = rp_new(&root_pool, "Kernel Syncer");
        !            78:   krt_filter_lp = lp_new_default(krt_pool);
        !            79:   init_list(&krt_proto_list);
        !            80:   krt_sys_io_init();
        !            81: }
        !            82: 
        !            83: /*
        !            84:  *     Interfaces
        !            85:  */
        !            86: 
        !            87: struct kif_proto *kif_proto;
        !            88: static struct kif_config *kif_cf;
        !            89: static timer *kif_scan_timer;
        !            90: static btime kif_last_shot;
        !            91: 
        !            92: static struct kif_iface_config kif_default_iface = {};
        !            93: 
        !            94: struct kif_iface_config *
        !            95: kif_get_iface_config(struct iface *iface)
        !            96: {
        !            97:   struct kif_config *cf = (void *) (kif_proto->p.cf);
        !            98:   struct kif_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
        !            99:   return ic ?: &kif_default_iface;
        !           100: }
        !           101: 
        !           102: static void
        !           103: kif_scan(timer *t)
        !           104: {
        !           105:   struct kif_proto *p = t->data;
        !           106: 
        !           107:   KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
        !           108:   kif_last_shot = current_time();
        !           109:   kif_do_scan(p);
        !           110: }
        !           111: 
        !           112: static void
        !           113: kif_force_scan(void)
        !           114: {
        !           115:   if (kif_proto && ((kif_last_shot + 2 S) < current_time()))
        !           116:     {
        !           117:       kif_scan(kif_scan_timer);
        !           118:       tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
        !           119:     }
        !           120: }
        !           121: 
        !           122: void
        !           123: kif_request_scan(void)
        !           124: {
        !           125:   if (kif_proto && (kif_scan_timer->expires > (current_time() + 1 S)))
        !           126:     tm_start(kif_scan_timer, 1 S);
        !           127: }
        !           128: 
        !           129: static struct proto *
        !           130: kif_init(struct proto_config *c)
        !           131: {
        !           132:   struct kif_proto *p = proto_new(c);
        !           133: 
        !           134:   kif_sys_init(p);
        !           135:   return &p->p;
        !           136: }
        !           137: 
        !           138: static int
        !           139: kif_start(struct proto *P)
        !           140: {
        !           141:   struct kif_proto *p = (struct kif_proto *) P;
        !           142: 
        !           143:   kif_proto = p;
        !           144:   kif_sys_start(p);
        !           145: 
        !           146:   /* Start periodic interface scanning */
        !           147:   kif_scan_timer = tm_new_init(P->pool, kif_scan, p, KIF_CF->scan_time, 0);
        !           148:   kif_scan(kif_scan_timer);
        !           149:   tm_start(kif_scan_timer, KIF_CF->scan_time);
        !           150: 
        !           151:   return PS_UP;
        !           152: }
        !           153: 
        !           154: static int
        !           155: kif_shutdown(struct proto *P)
        !           156: {
        !           157:   struct kif_proto *p = (struct kif_proto *) P;
        !           158: 
        !           159:   tm_stop(kif_scan_timer);
        !           160:   kif_sys_shutdown(p);
        !           161:   kif_proto = NULL;
        !           162: 
        !           163:   return PS_DOWN;
        !           164: }
        !           165: 
        !           166: static int
        !           167: kif_reconfigure(struct proto *p, struct proto_config *new)
        !           168: {
        !           169:   struct kif_config *o = (struct kif_config *) p->cf;
        !           170:   struct kif_config *n = (struct kif_config *) new;
        !           171: 
        !           172:   if (!kif_sys_reconfigure((struct kif_proto *) p, n, o))
        !           173:     return 0;
        !           174: 
        !           175:   if (o->scan_time != n->scan_time)
        !           176:     {
        !           177:       tm_stop(kif_scan_timer);
        !           178:       kif_scan_timer->recurrent = n->scan_time;
        !           179:       kif_scan(kif_scan_timer);
        !           180:       tm_start(kif_scan_timer, n->scan_time);
        !           181:     }
        !           182: 
        !           183:   if (!EMPTY_LIST(o->iface_list) || !EMPTY_LIST(n->iface_list))
        !           184:     {
        !           185:       /* This is hack, we have to update a configuration
        !           186:        * to the new value just now, because it is used
        !           187:        * for recalculation of preferred addresses.
        !           188:        */
        !           189:       p->cf = new;
        !           190: 
        !           191:       if_recalc_all_preferred_addresses();
        !           192:     }
        !           193: 
        !           194:   return 1;
        !           195: }
        !           196: 
        !           197: 
        !           198: static void
        !           199: kif_preconfig(struct protocol *P UNUSED, struct config *c)
        !           200: {
        !           201:   kif_cf = NULL;
        !           202:   kif_sys_preconfig(c);
        !           203: }
        !           204: 
        !           205: struct proto_config *
        !           206: kif_init_config(int class)
        !           207: {
        !           208:   if (kif_cf)
        !           209:     cf_error("Kernel device protocol already defined");
        !           210: 
        !           211:   kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
        !           212:   kif_cf->scan_time = 60 S;
        !           213:   init_list(&kif_cf->iface_list);
        !           214: 
        !           215:   kif_sys_init_config(kif_cf);
        !           216:   return (struct proto_config *) kif_cf;
        !           217: }
        !           218: 
        !           219: static void
        !           220: kif_copy_config(struct proto_config *dest, struct proto_config *src)
        !           221: {
        !           222:   struct kif_config *d = (struct kif_config *) dest;
        !           223:   struct kif_config *s = (struct kif_config *) src;
        !           224: 
        !           225:   /* Copy interface config list */
        !           226:   cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct kif_iface_config));
        !           227: 
        !           228:   /* Fix sysdep parts */
        !           229:   kif_sys_copy_config(d, s);
        !           230: }
        !           231: 
        !           232: struct protocol proto_unix_iface = {
        !           233:   .name =              "Device",
        !           234:   .template =          "device%d",
        !           235:   .class =             PROTOCOL_DEVICE,
        !           236:   .proto_size =                sizeof(struct kif_proto),
        !           237:   .config_size =       sizeof(struct kif_config),
        !           238:   .preconfig =         kif_preconfig,
        !           239:   .init =              kif_init,
        !           240:   .start =             kif_start,
        !           241:   .shutdown =          kif_shutdown,
        !           242:   .reconfigure =       kif_reconfigure,
        !           243:   .copy_config =       kif_copy_config
        !           244: };
        !           245: 
        !           246: /*
        !           247:  *     Tracing of routes
        !           248:  */
        !           249: 
        !           250: static inline void
        !           251: krt_trace_in(struct krt_proto *p, rte *e, char *msg)
        !           252: {
        !           253:   if (p->p.debug & D_PACKETS)
        !           254:     log(L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
        !           255: }
        !           256: 
        !           257: static inline void
        !           258: krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
        !           259: {
        !           260:   if (p->p.debug & D_PACKETS)
        !           261:     log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
        !           262: }
        !           263: 
        !           264: /*
        !           265:  *     Inherited Routes
        !           266:  */
        !           267: 
        !           268: #ifdef KRT_ALLOW_LEARN
        !           269: 
        !           270: static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
        !           271: 
        !           272: /*
        !           273:  * krt_same_key() specifies what (aside from the net) is the key in
        !           274:  * kernel routing tables. It should be OS-dependent, this is for
        !           275:  * Linux. It is important for asynchronous alien updates, because a
        !           276:  * positive update is implicitly a negative one for any old route with
        !           277:  * the same key.
        !           278:  */
        !           279: 
        !           280: static inline int
        !           281: krt_same_key(rte *a, rte *b)
        !           282: {
        !           283:   return a->u.krt.metric == b->u.krt.metric;
        !           284: }
        !           285: 
        !           286: static inline int
        !           287: krt_uptodate(rte *a, rte *b)
        !           288: {
        !           289:   if (a->attrs != b->attrs)
        !           290:     return 0;
        !           291: 
        !           292:   if (a->u.krt.proto != b->u.krt.proto)
        !           293:     return 0;
        !           294: 
        !           295:   return 1;
        !           296: }
        !           297: 
        !           298: static void
        !           299: krt_learn_announce_update(struct krt_proto *p, rte *e)
        !           300: {
        !           301:   net *n = e->net;
        !           302:   rta *aa = rta_clone(e->attrs);
        !           303:   rte *ee = rte_get_temp(aa);
        !           304:   ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
        !           305:   ee->u.krt = e->u.krt;
        !           306:   rte_update(&p->p, n->n.addr, ee);
        !           307: }
        !           308: 
        !           309: static void
        !           310: krt_learn_announce_delete(struct krt_proto *p, net *n)
        !           311: {
        !           312:   rte_update(&p->p, n->n.addr, NULL);
        !           313: }
        !           314: 
        !           315: /* Called when alien route is discovered during scan */
        !           316: static void
        !           317: krt_learn_scan(struct krt_proto *p, rte *e)
        !           318: {
        !           319:   net *n0 = e->net;
        !           320:   net *n = net_get(&p->krt_table, n0->n.addr);
        !           321:   rte *m, **mm;
        !           322: 
        !           323:   e->attrs = rta_lookup(e->attrs);
        !           324: 
        !           325:   for(mm=&n->routes; m = *mm; mm=&m->next)
        !           326:     if (krt_same_key(m, e))
        !           327:       break;
        !           328:   if (m)
        !           329:     {
        !           330:       if (krt_uptodate(m, e))
        !           331:        {
        !           332:          krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
        !           333:          rte_free(e);
        !           334:          m->u.krt.seen = 1;
        !           335:        }
        !           336:       else
        !           337:        {
        !           338:          krt_trace_in(p, e, "[alien] updated");
        !           339:          *mm = m->next;
        !           340:          rte_free(m);
        !           341:          m = NULL;
        !           342:        }
        !           343:     }
        !           344:   else
        !           345:     krt_trace_in(p, e, "[alien] created");
        !           346:   if (!m)
        !           347:     {
        !           348:       e->next = n->routes;
        !           349:       n->routes = e;
        !           350:       e->u.krt.seen = 1;
        !           351:     }
        !           352: }
        !           353: 
        !           354: static void
        !           355: krt_learn_prune(struct krt_proto *p)
        !           356: {
        !           357:   struct fib *fib = &p->krt_table.fib;
        !           358:   struct fib_iterator fit;
        !           359: 
        !           360:   KRT_TRACE(p, D_EVENTS, "Pruning inherited routes");
        !           361: 
        !           362:   FIB_ITERATE_INIT(&fit, fib);
        !           363: again:
        !           364:   FIB_ITERATE_START(fib, &fit, net, n)
        !           365:     {
        !           366:       rte *e, **ee, *best, **pbest, *old_best;
        !           367: 
        !           368:       /*
        !           369:        * Note that old_best may be NULL even if there was an old best route in
        !           370:        * the previous step, because it might be replaced in krt_learn_scan().
        !           371:        * But in that case there is a new valid best route.
        !           372:        */
        !           373: 
        !           374:       old_best = NULL;
        !           375:       best = NULL;
        !           376:       pbest = NULL;
        !           377:       ee = &n->routes;
        !           378:       while (e = *ee)
        !           379:        {
        !           380:          if (e->u.krt.best)
        !           381:            old_best = e;
        !           382: 
        !           383:          if (!e->u.krt.seen)
        !           384:            {
        !           385:              *ee = e->next;
        !           386:              rte_free(e);
        !           387:              continue;
        !           388:            }
        !           389: 
        !           390:          if (!best || best->u.krt.metric > e->u.krt.metric)
        !           391:            {
        !           392:              best = e;
        !           393:              pbest = ee;
        !           394:            }
        !           395: 
        !           396:          e->u.krt.seen = 0;
        !           397:          e->u.krt.best = 0;
        !           398:          ee = &e->next;
        !           399:        }
        !           400:       if (!n->routes)
        !           401:        {
        !           402:          DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
        !           403:          if (old_best)
        !           404:            krt_learn_announce_delete(p, n);
        !           405: 
        !           406:          FIB_ITERATE_PUT(&fit);
        !           407:          fib_delete(fib, n);
        !           408:          goto again;
        !           409:        }
        !           410: 
        !           411:       best->u.krt.best = 1;
        !           412:       *pbest = best->next;
        !           413:       best->next = n->routes;
        !           414:       n->routes = best;
        !           415: 
        !           416:       if ((best != old_best) || p->reload)
        !           417:        {
        !           418:          DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
        !           419:          krt_learn_announce_update(p, best);
        !           420:        }
        !           421:       else
        !           422:        DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
        !           423:     }
        !           424:   FIB_ITERATE_END;
        !           425: 
        !           426:   p->reload = 0;
        !           427: }
        !           428: 
        !           429: static void
        !           430: krt_learn_async(struct krt_proto *p, rte *e, int new)
        !           431: {
        !           432:   net *n0 = e->net;
        !           433:   net *n = net_get(&p->krt_table, n0->n.addr);
        !           434:   rte *g, **gg, *best, **bestp, *old_best;
        !           435: 
        !           436:   e->attrs = rta_lookup(e->attrs);
        !           437: 
        !           438:   old_best = n->routes;
        !           439:   for(gg=&n->routes; g = *gg; gg = &g->next)
        !           440:     if (krt_same_key(g, e))
        !           441:       break;
        !           442:   if (new)
        !           443:     {
        !           444:       if (g)
        !           445:        {
        !           446:          if (krt_uptodate(g, e))
        !           447:            {
        !           448:              krt_trace_in(p, e, "[alien async] same");
        !           449:              rte_free(e);
        !           450:              return;
        !           451:            }
        !           452:          krt_trace_in(p, e, "[alien async] updated");
        !           453:          *gg = g->next;
        !           454:          rte_free(g);
        !           455:        }
        !           456:       else
        !           457:        krt_trace_in(p, e, "[alien async] created");
        !           458: 
        !           459:       e->next = n->routes;
        !           460:       n->routes = e;
        !           461:     }
        !           462:   else if (!g)
        !           463:     {
        !           464:       krt_trace_in(p, e, "[alien async] delete failed");
        !           465:       rte_free(e);
        !           466:       return;
        !           467:     }
        !           468:   else
        !           469:     {
        !           470:       krt_trace_in(p, e, "[alien async] removed");
        !           471:       *gg = g->next;
        !           472:       rte_free(e);
        !           473:       rte_free(g);
        !           474:     }
        !           475:   best = n->routes;
        !           476:   bestp = &n->routes;
        !           477:   for(gg=&n->routes; g=*gg; gg=&g->next)
        !           478:   {
        !           479:     if (best->u.krt.metric > g->u.krt.metric)
        !           480:       {
        !           481:        best = g;
        !           482:        bestp = gg;
        !           483:       }
        !           484: 
        !           485:     g->u.krt.best = 0;
        !           486:   }
        !           487: 
        !           488:   if (best)
        !           489:     {
        !           490:       best->u.krt.best = 1;
        !           491:       *bestp = best->next;
        !           492:       best->next = n->routes;
        !           493:       n->routes = best;
        !           494:     }
        !           495: 
        !           496:   if (best != old_best)
        !           497:     {
        !           498:       DBG("krt_learn_async: distributing change\n");
        !           499:       if (best)
        !           500:        krt_learn_announce_update(p, best);
        !           501:       else
        !           502:        krt_learn_announce_delete(p, n);
        !           503:     }
        !           504: }
        !           505: 
        !           506: static void
        !           507: krt_learn_init(struct krt_proto *p)
        !           508: {
        !           509:   if (KRT_CF->learn)
        !           510:   {
        !           511:     struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
        !           512:     cf->name = "Inherited";
        !           513:     cf->addr_type = p->p.net_type;
        !           514: 
        !           515:     rt_setup(p->p.pool, &p->krt_table, cf);
        !           516:   }
        !           517: }
        !           518: 
        !           519: static void
        !           520: krt_dump(struct proto *P)
        !           521: {
        !           522:   struct krt_proto *p = (struct krt_proto *) P;
        !           523: 
        !           524:   if (!KRT_CF->learn)
        !           525:     return;
        !           526:   debug("KRT: Table of inheritable routes\n");
        !           527:   rt_dump(&p->krt_table);
        !           528: }
        !           529: 
        !           530: static void
        !           531: krt_dump_attrs(rte *e)
        !           532: {
        !           533:   debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
        !           534: }
        !           535: 
        !           536: #endif
        !           537: 
        !           538: /*
        !           539:  *     Routes
        !           540:  */
        !           541: 
        !           542: static void
        !           543: krt_flush_routes(struct krt_proto *p)
        !           544: {
        !           545:   struct rtable *t = p->p.main_channel->table;
        !           546: 
        !           547:   KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
        !           548:   FIB_WALK(&t->fib, net, n)
        !           549:     {
        !           550:       rte *e = n->routes;
        !           551:       if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
        !           552:        {
        !           553:          /* FIXME: this does not work if gw is changed in export filter */
        !           554:          krt_replace_rte(p, e->net, NULL, e);
        !           555:          n->n.flags &= ~KRF_INSTALLED;
        !           556:        }
        !           557:     }
        !           558:   FIB_WALK_END;
        !           559: }
        !           560: 
        !           561: static struct rte *
        !           562: krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
        !           563: {
        !           564:   struct channel *c = p->p.main_channel;
        !           565:   const struct filter *filter = c->out_filter;
        !           566:   rte *rt;
        !           567: 
        !           568:   if (c->ra_mode == RA_MERGED)
        !           569:     return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
        !           570: 
        !           571:   rt = net->routes;
        !           572:   *rt_free = NULL;
        !           573: 
        !           574:   if (!rte_is_valid(rt))
        !           575:     return NULL;
        !           576: 
        !           577:   if (filter == FILTER_REJECT)
        !           578:     return NULL;
        !           579: 
        !           580:   rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
        !           581: 
        !           582:   /* We could run krt_preexport() here, but it is already handled by KRF_INSTALLED */
        !           583: 
        !           584:   if (filter == FILTER_ACCEPT)
        !           585:     goto accept;
        !           586: 
        !           587:   if (f_run(filter, &rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
        !           588:     goto reject;
        !           589: 
        !           590: 
        !           591: accept:
        !           592:   if (rt != net->routes)
        !           593:     *rt_free = rt;
        !           594:   return rt;
        !           595: 
        !           596: reject:
        !           597:   if (rt != net->routes)
        !           598:     rte_free(rt);
        !           599:   return NULL;
        !           600: }
        !           601: 
        !           602: static int
        !           603: krt_same_dest(rte *k, rte *e)
        !           604: {
        !           605:   rta *ka = k->attrs, *ea = e->attrs;
        !           606: 
        !           607:   if (ka->dest != ea->dest)
        !           608:     return 0;
        !           609: 
        !           610:   if (ka->dest == RTD_UNICAST)
        !           611:     return nexthop_same(&(ka->nh), &(ea->nh));
        !           612: 
        !           613:   return 1;
        !           614: }
        !           615: 
        !           616: /*
        !           617:  *  This gets called back when the low-level scanning code discovers a route.
        !           618:  *  We expect that the route is a temporary rte and its attributes are uncached.
        !           619:  */
        !           620: 
        !           621: void
        !           622: krt_got_route(struct krt_proto *p, rte *e)
        !           623: {
        !           624:   net *net = e->net;
        !           625:   int verdict;
        !           626: 
        !           627: #ifdef KRT_ALLOW_LEARN
        !           628:   switch (e->u.krt.src)
        !           629:     {
        !           630:     case KRT_SRC_KERNEL:
        !           631:       verdict = KRF_IGNORE;
        !           632:       goto sentenced;
        !           633: 
        !           634:     case KRT_SRC_REDIRECT:
        !           635:       verdict = KRF_DELETE;
        !           636:       goto sentenced;
        !           637: 
        !           638:     case  KRT_SRC_ALIEN:
        !           639:       if (KRT_CF->learn)
        !           640:        krt_learn_scan(p, e);
        !           641:       else
        !           642:        {
        !           643:          krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
        !           644:          rte_free(e);
        !           645:        }
        !           646:       return;
        !           647:     }
        !           648: #endif
        !           649:   /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
        !           650: 
        !           651:   if (net->n.flags & KRF_VERDICT_MASK)
        !           652:     {
        !           653:       /* Route to this destination was already seen. Strange, but it happens... */
        !           654:       krt_trace_in(p, e, "already seen");
        !           655:       rte_free(e);
        !           656:       return;
        !           657:     }
        !           658: 
        !           659:   if (!p->ready)
        !           660:     {
        !           661:       /* We wait for the initial feed to have correct KRF_INSTALLED flag */
        !           662:       verdict = KRF_IGNORE;
        !           663:       goto sentenced;
        !           664:     }
        !           665: 
        !           666:   if (net->n.flags & KRF_INSTALLED)
        !           667:     {
        !           668:       rte *new, *rt_free;
        !           669: 
        !           670:       new = krt_export_net(p, net, &rt_free);
        !           671: 
        !           672:       /* TODO: There also may be changes in route eattrs, we ignore that for now. */
        !           673: 
        !           674:       if (!new)
        !           675:        verdict = KRF_DELETE;
        !           676:       else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new))
        !           677:        verdict = KRF_UPDATE;
        !           678:       else
        !           679:        verdict = KRF_SEEN;
        !           680: 
        !           681:       if (rt_free)
        !           682:        rte_free(rt_free);
        !           683: 
        !           684:       lp_flush(krt_filter_lp);
        !           685:     }
        !           686:   else
        !           687:     verdict = KRF_DELETE;
        !           688: 
        !           689:  sentenced:
        !           690:   krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
        !           691:   net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
        !           692:   if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
        !           693:     {
        !           694:       /* Get a cached copy of attributes and temporarily link the route */
        !           695:       rta *a = e->attrs;
        !           696:       a->source = RTS_DUMMY;
        !           697:       e->attrs = rta_lookup(a);
        !           698:       e->next = net->routes;
        !           699:       net->routes = e;
        !           700:     }
        !           701:   else
        !           702:     rte_free(e);
        !           703: }
        !           704: 
        !           705: static void
        !           706: krt_prune(struct krt_proto *p)
        !           707: {
        !           708:   struct rtable *t = p->p.main_channel->table;
        !           709: 
        !           710:   KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
        !           711:   FIB_WALK(&t->fib, net, n)
        !           712:     {
        !           713:       int verdict = n->n.flags & KRF_VERDICT_MASK;
        !           714:       rte *new, *old, *rt_free = NULL;
        !           715: 
        !           716:       if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
        !           717:        {
        !           718:          /* Get a dummy route from krt_got_route() */
        !           719:          old = n->routes;
        !           720:          n->routes = old->next;
        !           721:        }
        !           722:       else
        !           723:        old = NULL;
        !           724: 
        !           725:       if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
        !           726:        {
        !           727:          /* We have to run export filter to get proper 'new' route */
        !           728:          new = krt_export_net(p, n, &rt_free);
        !           729: 
        !           730:          if (!new)
        !           731:            verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
        !           732:        }
        !           733:       else
        !           734:        new = NULL;
        !           735: 
        !           736:       switch (verdict)
        !           737:        {
        !           738:        case KRF_CREATE:
        !           739:          if (new && (n->n.flags & KRF_INSTALLED))
        !           740:            {
        !           741:              krt_trace_in(p, new, "reinstalling");
        !           742:              krt_replace_rte(p, n, new, NULL);
        !           743:            }
        !           744:          break;
        !           745:        case KRF_SEEN:
        !           746:        case KRF_IGNORE:
        !           747:          /* Nothing happens */
        !           748:          break;
        !           749:        case KRF_UPDATE:
        !           750:          krt_trace_in(p, new, "updating");
        !           751:          krt_replace_rte(p, n, new, old);
        !           752:          break;
        !           753:        case KRF_DELETE:
        !           754:          krt_trace_in(p, old, "deleting");
        !           755:          krt_replace_rte(p, n, NULL, old);
        !           756:          break;
        !           757:        default:
        !           758:          bug("krt_prune: invalid route status");
        !           759:        }
        !           760: 
        !           761:       if (old)
        !           762:        rte_free(old);
        !           763:       if (rt_free)
        !           764:        rte_free(rt_free);
        !           765:       lp_flush(krt_filter_lp);
        !           766:       n->n.flags &= ~KRF_VERDICT_MASK;
        !           767:     }
        !           768:   FIB_WALK_END;
        !           769: 
        !           770: #ifdef KRT_ALLOW_LEARN
        !           771:   if (KRT_CF->learn)
        !           772:     krt_learn_prune(p);
        !           773: #endif
        !           774: 
        !           775:   if (p->ready)
        !           776:     p->initialized = 1;
        !           777: }
        !           778: 
        !           779: void
        !           780: krt_got_route_async(struct krt_proto *p, rte *e, int new)
        !           781: {
        !           782:   net *net = e->net;
        !           783: 
        !           784:   switch (e->u.krt.src)
        !           785:     {
        !           786:     case KRT_SRC_BIRD:
        !           787:       /* Should be filtered by the back end */
        !           788:       bug("BIRD originated routes should not get here.");
        !           789: 
        !           790:     case KRT_SRC_REDIRECT:
        !           791:       if (new)
        !           792:        {
        !           793:          krt_trace_in(p, e, "[redirect] deleting");
        !           794:          krt_replace_rte(p, net, NULL, e);
        !           795:        }
        !           796:       /* If !new, it is probably echo of our deletion */
        !           797:       break;
        !           798: 
        !           799: #ifdef KRT_ALLOW_LEARN
        !           800:     case KRT_SRC_ALIEN:
        !           801:       if (KRT_CF->learn)
        !           802:        {
        !           803:          krt_learn_async(p, e, new);
        !           804:          return;
        !           805:        }
        !           806: #endif
        !           807:     }
        !           808:   rte_free(e);
        !           809: }
        !           810: 
        !           811: /*
        !           812:  *     Periodic scanning
        !           813:  */
        !           814: 
        !           815: 
        !           816: #ifdef CONFIG_ALL_TABLES_AT_ONCE
        !           817: 
        !           818: static timer *krt_scan_timer;
        !           819: static int krt_scan_count;
        !           820: 
        !           821: static void
        !           822: krt_scan(timer *t UNUSED)
        !           823: {
        !           824:   struct krt_proto *p;
        !           825: 
        !           826:   kif_force_scan();
        !           827: 
        !           828:   /* We need some node to decide whether to print the debug messages or not */
        !           829:   p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
        !           830:   KRT_TRACE(p, D_EVENTS, "Scanning routing table");
        !           831: 
        !           832:   krt_do_scan(NULL);
        !           833: 
        !           834:   void *q;
        !           835:   WALK_LIST(q, krt_proto_list)
        !           836:   {
        !           837:     p = SKIP_BACK(struct krt_proto, krt_node, q);
        !           838:     krt_prune(p);
        !           839:   }
        !           840: }
        !           841: 
        !           842: static void
        !           843: krt_scan_timer_start(struct krt_proto *p)
        !           844: {
        !           845:   if (!krt_scan_count)
        !           846:     krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0);
        !           847: 
        !           848:   krt_scan_count++;
        !           849: 
        !           850:   tm_start(krt_scan_timer, 1 S);
        !           851: }
        !           852: 
        !           853: static void
        !           854: krt_scan_timer_stop(struct krt_proto *p UNUSED)
        !           855: {
        !           856:   krt_scan_count--;
        !           857: 
        !           858:   if (!krt_scan_count)
        !           859:   {
        !           860:     rfree(krt_scan_timer);
        !           861:     krt_scan_timer = NULL;
        !           862:   }
        !           863: }
        !           864: 
        !           865: static void
        !           866: krt_scan_timer_kick(struct krt_proto *p UNUSED)
        !           867: {
        !           868:   tm_start(krt_scan_timer, 0);
        !           869: }
        !           870: 
        !           871: #else
        !           872: 
        !           873: static void
        !           874: krt_scan(timer *t)
        !           875: {
        !           876:   struct krt_proto *p = t->data;
        !           877: 
        !           878:   kif_force_scan();
        !           879: 
        !           880:   KRT_TRACE(p, D_EVENTS, "Scanning routing table");
        !           881:   krt_do_scan(p);
        !           882:   krt_prune(p);
        !           883: }
        !           884: 
        !           885: static void
        !           886: krt_scan_timer_start(struct krt_proto *p)
        !           887: {
        !           888:   p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
        !           889:   tm_start(p->scan_timer, 1 S);
        !           890: }
        !           891: 
        !           892: static void
        !           893: krt_scan_timer_stop(struct krt_proto *p)
        !           894: {
        !           895:   tm_stop(p->scan_timer);
        !           896: }
        !           897: 
        !           898: static void
        !           899: krt_scan_timer_kick(struct krt_proto *p)
        !           900: {
        !           901:   tm_start(p->scan_timer, 0);
        !           902: }
        !           903: 
        !           904: #endif
        !           905: 
        !           906: 
        !           907: 
        !           908: 
        !           909: /*
        !           910:  *     Updates
        !           911:  */
        !           912: 
        !           913: static void
        !           914: krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
        !           915: {
        !           916:   rte_init_tmp_attrs(rt, pool, 2);
        !           917:   rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
        !           918:   rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
        !           919: }
        !           920: 
        !           921: static void
        !           922: krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
        !           923: {
        !           924:   rte_init_tmp_attrs(rt, pool, 2);
        !           925:   rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
        !           926:   rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
        !           927: }
        !           928: 
        !           929: static int
        !           930: krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
        !           931: {
        !           932:   // struct krt_proto *p = (struct krt_proto *) P;
        !           933:   rte *e = *new;
        !           934: 
        !           935:   if (e->attrs->src->proto == P)
        !           936:   {
        !           937: #ifdef CONFIG_SINGLE_ROUTE
        !           938:     /*
        !           939:      * Implicit withdraw - when the imported kernel route becomes the best one,
        !           940:      * we know that the previous one exported to the kernel was already removed,
        !           941:      * but if we processed the update as usual, we would send withdraw to the
        !           942:      * kernel, which would remove the new imported route instead.
        !           943:      *
        !           944:      * We will remove KRT_INSTALLED flag, which stops such withdraw to be
        !           945:      * processed in krt_rt_notify() and krt_replace_rte().
        !           946:      */
        !           947:     if (e == e->net->routes)
        !           948:       e->net->n.flags &= ~KRF_INSTALLED;
        !           949: #endif
        !           950:     return -1;
        !           951:   }
        !           952: 
        !           953:   if (!krt_capable(e))
        !           954:     return -1;
        !           955: 
        !           956:   return 0;
        !           957: }
        !           958: 
        !           959: static void
        !           960: krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
        !           961:              rte *new, rte *old)
        !           962: {
        !           963:   struct krt_proto *p = (struct krt_proto *) P;
        !           964: 
        !           965:   if (config->shutdown)
        !           966:     return;
        !           967:   if (!(net->n.flags & KRF_INSTALLED))
        !           968:     old = NULL;
        !           969:   if (new)
        !           970:     net->n.flags |= KRF_INSTALLED;
        !           971:   else
        !           972:     net->n.flags &= ~KRF_INSTALLED;
        !           973:   if (p->initialized)          /* Before first scan we don't touch the routes */
        !           974:     krt_replace_rte(p, net, new, old);
        !           975: }
        !           976: 
        !           977: static void
        !           978: krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED)
        !           979: {
        !           980:   struct krt_proto *p = (struct krt_proto *) P;
        !           981: 
        !           982:   /*
        !           983:    * When interface went down, we should remove routes to it. In the ideal world,
        !           984:    * OS kernel would send us route removal notifications in such cases, but we
        !           985:    * cannot rely on it as it is often not true. E.g. Linux kernel removes related
        !           986:    * routes when an interface went down, but it does not notify userspace about
        !           987:    * that. To be sure, we just schedule a scan to ensure synchronization.
        !           988:    */
        !           989: 
        !           990:   if ((flags & IF_CHANGE_DOWN) && KRT_CF->learn)
        !           991:     krt_scan_timer_kick(p);
        !           992: }
        !           993: 
        !           994: static void
        !           995: krt_reload_routes(struct channel *C)
        !           996: {
        !           997:   struct krt_proto *p = (void *) C->proto;
        !           998: 
        !           999:   /* Although we keep learned routes in krt_table, we rather schedule a scan */
        !          1000: 
        !          1001:   if (KRT_CF->learn)
        !          1002:   {
        !          1003:     p->reload = 1;
        !          1004:     krt_scan_timer_kick(p);
        !          1005:   }
        !          1006: }
        !          1007: 
        !          1008: static void
        !          1009: krt_feed_end(struct channel *C)
        !          1010: {
        !          1011:   struct krt_proto *p = (void *) C->proto;
        !          1012: 
        !          1013:   p->ready = 1;
        !          1014:   krt_scan_timer_kick(p);
        !          1015: }
        !          1016: 
        !          1017: 
        !          1018: static int
        !          1019: krt_rte_same(rte *a, rte *b)
        !          1020: {
        !          1021:   /* src is always KRT_SRC_ALIEN and type is irrelevant */
        !          1022:   return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
        !          1023: }
        !          1024: 
        !          1025: 
        !          1026: /*
        !          1027:  *     Protocol glue
        !          1028:  */
        !          1029: 
        !          1030: struct krt_config *krt_cf;
        !          1031: 
        !          1032: static void
        !          1033: krt_preconfig(struct protocol *P UNUSED, struct config *c)
        !          1034: {
        !          1035:   krt_cf = NULL;
        !          1036:   krt_sys_preconfig(c);
        !          1037: }
        !          1038: 
        !          1039: static void
        !          1040: krt_postconfig(struct proto_config *CF)
        !          1041: {
        !          1042:   struct krt_config *cf = (void *) CF;
        !          1043: 
        !          1044:   if (EMPTY_LIST(CF->channels))
        !          1045:     cf_error("Channel not specified");
        !          1046: 
        !          1047: #ifdef CONFIG_ALL_TABLES_AT_ONCE
        !          1048:   if (krt_cf->scan_time != cf->scan_time)
        !          1049:     cf_error("All kernel syncers must use the same table scan interval");
        !          1050: #endif
        !          1051: 
        !          1052:   struct channel_config *cc = proto_cf_main_channel(CF);
        !          1053:   struct rtable_config *tab = cc->table;
        !          1054:   if (tab->krt_attached)
        !          1055:     cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
        !          1056:   tab->krt_attached = CF;
        !          1057: 
        !          1058:   if (cf->merge_paths)
        !          1059:   {
        !          1060:     cc->ra_mode = RA_MERGED;
        !          1061:     cc->merge_limit = cf->merge_paths;
        !          1062:   }
        !          1063: 
        !          1064:   krt_sys_postconfig(cf);
        !          1065: }
        !          1066: 
        !          1067: static struct proto *
        !          1068: krt_init(struct proto_config *CF)
        !          1069: {
        !          1070:   struct krt_proto *p = proto_new(CF);
        !          1071:   // struct krt_config *cf = (void *) CF;
        !          1072: 
        !          1073:   p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
        !          1074: 
        !          1075:   p->p.preexport = krt_preexport;
        !          1076:   p->p.rt_notify = krt_rt_notify;
        !          1077:   p->p.if_notify = krt_if_notify;
        !          1078:   p->p.reload_routes = krt_reload_routes;
        !          1079:   p->p.feed_end = krt_feed_end;
        !          1080:   p->p.make_tmp_attrs = krt_make_tmp_attrs;
        !          1081:   p->p.store_tmp_attrs = krt_store_tmp_attrs;
        !          1082:   p->p.rte_same = krt_rte_same;
        !          1083: 
        !          1084:   krt_sys_init(p);
        !          1085:   return &p->p;
        !          1086: }
        !          1087: 
        !          1088: static int
        !          1089: krt_start(struct proto *P)
        !          1090: {
        !          1091:   struct krt_proto *p = (struct krt_proto *) P;
        !          1092: 
        !          1093:   switch (p->p.net_type)
        !          1094:   {
        !          1095:   case NET_IP4:                p->af = AF_INET; break;
        !          1096:   case NET_IP6:                p->af = AF_INET6; break;
        !          1097:   case NET_IP6_SADR:   p->af = AF_INET6; break;
        !          1098: #ifdef AF_MPLS
        !          1099:   case NET_MPLS:       p->af = AF_MPLS; break;
        !          1100: #endif
        !          1101:   default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
        !          1102:   }
        !          1103: 
        !          1104:   add_tail(&krt_proto_list, &p->krt_node);
        !          1105: 
        !          1106: #ifdef KRT_ALLOW_LEARN
        !          1107:   krt_learn_init(p);
        !          1108: #endif
        !          1109: 
        !          1110:   if (!krt_sys_start(p))
        !          1111:   {
        !          1112:     rem_node(&p->krt_node);
        !          1113:     return PS_START;
        !          1114:   }
        !          1115: 
        !          1116:   krt_scan_timer_start(p);
        !          1117: 
        !          1118:   if (p->p.gr_recovery && KRT_CF->graceful_restart)
        !          1119:     p->p.main_channel->gr_wait = 1;
        !          1120: 
        !          1121:   return PS_UP;
        !          1122: }
        !          1123: 
        !          1124: static int
        !          1125: krt_shutdown(struct proto *P)
        !          1126: {
        !          1127:   struct krt_proto *p = (struct krt_proto *) P;
        !          1128: 
        !          1129:   krt_scan_timer_stop(p);
        !          1130: 
        !          1131:   /* FIXME we should flush routes even when persist during reconfiguration */
        !          1132:   if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
        !          1133:     krt_flush_routes(p);
        !          1134: 
        !          1135:   p->ready = 0;
        !          1136:   p->initialized = 0;
        !          1137: 
        !          1138:   if (p->p.proto_state == PS_START)
        !          1139:     return PS_DOWN;
        !          1140: 
        !          1141:   krt_sys_shutdown(p);
        !          1142:   rem_node(&p->krt_node);
        !          1143: 
        !          1144:   return PS_DOWN;
        !          1145: }
        !          1146: 
        !          1147: static int
        !          1148: krt_reconfigure(struct proto *p, struct proto_config *CF)
        !          1149: {
        !          1150:   struct krt_config *o = (void *) p->cf;
        !          1151:   struct krt_config *n = (void *) CF;
        !          1152: 
        !          1153:   if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
        !          1154:     return 0;
        !          1155: 
        !          1156:   if (!krt_sys_reconfigure((struct krt_proto *) p, n, o))
        !          1157:     return 0;
        !          1158: 
        !          1159:   /* persist, graceful restart need not be the same */
        !          1160:   return o->scan_time == n->scan_time && o->learn == n->learn;
        !          1161: }
        !          1162: 
        !          1163: struct proto_config *
        !          1164: krt_init_config(int class)
        !          1165: {
        !          1166: #ifndef CONFIG_MULTIPLE_TABLES
        !          1167:   if (krt_cf)
        !          1168:     cf_error("Kernel protocol already defined");
        !          1169: #endif
        !          1170: 
        !          1171:   krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
        !          1172:   krt_cf->scan_time = 60 S;
        !          1173: 
        !          1174:   krt_sys_init_config(krt_cf);
        !          1175:   return (struct proto_config *) krt_cf;
        !          1176: }
        !          1177: 
        !          1178: static void
        !          1179: krt_copy_config(struct proto_config *dest, struct proto_config *src)
        !          1180: {
        !          1181:   struct krt_config *d = (struct krt_config *) dest;
        !          1182:   struct krt_config *s = (struct krt_config *) src;
        !          1183: 
        !          1184:   /* Fix sysdep parts */
        !          1185:   krt_sys_copy_config(d, s);
        !          1186: }
        !          1187: 
        !          1188: static int
        !          1189: krt_get_attr(eattr *a, byte *buf, int buflen)
        !          1190: {
        !          1191:   switch (a->id)
        !          1192:   {
        !          1193:   case EA_KRT_SOURCE:
        !          1194:     bsprintf(buf, "source");
        !          1195:     return GA_NAME;
        !          1196: 
        !          1197:   case EA_KRT_METRIC:
        !          1198:     bsprintf(buf, "metric");
        !          1199:     return GA_NAME;
        !          1200: 
        !          1201:   default:
        !          1202:     return krt_sys_get_attr(a, buf, buflen);
        !          1203:   }
        !          1204: }
        !          1205: 
        !          1206: 
        !          1207: #ifdef CONFIG_IP6_SADR_KERNEL
        !          1208: #define MAYBE_IP6_SADR NB_IP6_SADR
        !          1209: #else
        !          1210: #define MAYBE_IP6_SADR 0
        !          1211: #endif
        !          1212: 
        !          1213: #ifdef HAVE_MPLS_KERNEL
        !          1214: #define MAYBE_MPLS     NB_MPLS
        !          1215: #else
        !          1216: #define MAYBE_MPLS     0
        !          1217: #endif
        !          1218: 
        !          1219: struct protocol proto_unix_kernel = {
        !          1220:   .name =              "Kernel",
        !          1221:   .template =          "kernel%d",
        !          1222:   .class =             PROTOCOL_KERNEL,
        !          1223:   .preference =                DEF_PREF_INHERITED,
        !          1224:   .channel_mask =      NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
        !          1225:   .proto_size =                sizeof(struct krt_proto),
        !          1226:   .config_size =       sizeof(struct krt_config),
        !          1227:   .preconfig =         krt_preconfig,
        !          1228:   .postconfig =                krt_postconfig,
        !          1229:   .init =              krt_init,
        !          1230:   .start =             krt_start,
        !          1231:   .shutdown =          krt_shutdown,
        !          1232:   .reconfigure =       krt_reconfigure,
        !          1233:   .copy_config =       krt_copy_config,
        !          1234:   .get_attr =          krt_get_attr,
        !          1235: #ifdef KRT_ALLOW_LEARN
        !          1236:   .dump =              krt_dump,
        !          1237:   .dump_attrs =                krt_dump_attrs,
        !          1238: #endif
        !          1239: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>