Annotation of embedaddon/bird/proto/static/static.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  *     BIRD -- Static Route Generator
                      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: Static
                     11:  *
                     12:  * The Static protocol is implemented in a straightforward way. It keeps
                     13:  * two lists of static routes: one containing interface routes and one
                     14:  * holding the remaining ones. Interface routes are inserted and removed according
                     15:  * to interface events received from the core via the if_notify() hook. Routes
                     16:  * pointing to a neighboring router use a sticky node in the neighbor cache
                     17:  * to be notified about gaining or losing the neighbor. Special
                     18:  * routes like black holes or rejects are inserted all the time.
                     19:  *
                     20:  * Multipath routes are tricky. Because these routes depends on
                     21:  * several neighbors we need to integrate that to the neighbor
                     22:  * notification handling, we use dummy static_route nodes, one for
                     23:  * each nexthop. Therefore, a multipath route consists of a master
                     24:  * static_route node (of dest RTD_MULTIPATH), which specifies prefix
                     25:  * and is used in most circumstances, and a list of dummy static_route
                     26:  * nodes (of dest RTD_NONE), which stores info about nexthops and are
                     27:  * connected to neighbor entries and neighbor notifications. Dummy
                     28:  * nodes are chained using mp_next, they aren't in other_routes list,
                     29:  * and abuse some fields (masklen, if_name) for other purposes.
                     30:  *
                     31:  * The only other thing worth mentioning is that when asked for reconfiguration,
                     32:  * Static not only compares the two configurations, but it also calculates
                     33:  * difference between the lists of static routes and it just inserts the
                     34:  * newly added routes and removes the obsolete ones.
                     35:  */
                     36: 
                     37: #undef LOCAL_DEBUG
                     38: 
                     39: #include "nest/bird.h"
                     40: #include "nest/iface.h"
                     41: #include "nest/protocol.h"
                     42: #include "nest/route.h"
                     43: #include "nest/cli.h"
                     44: #include "conf/conf.h"
                     45: #include "filter/filter.h"
                     46: #include "lib/string.h"
                     47: #include "lib/alloca.h"
                     48: 
                     49: #include "static.h"
                     50: 
                     51: static linpool *static_lp;
                     52: 
                     53: static inline rtable *
                     54: p_igp_table(struct proto *p)
                     55: {
                     56:   struct static_config *cf = (void *) p->cf;
                     57:   return cf->igp_table ? cf->igp_table->table : p->table;
                     58: }
                     59: 
                     60: static void
                     61: static_install(struct proto *p, struct static_route *r, struct iface *ifa)
                     62: {
                     63:   net *n;
                     64:   rta a;
                     65:   rte *e;
                     66: 
                     67:   if (r->installed > 0)
                     68:     return;
                     69: 
                     70:   DBG("Installing static route %I/%d, rtd=%d\n", r->net, r->masklen, r->dest);
                     71:   bzero(&a, sizeof(a));
                     72:   a.src = p->main_source;
                     73:   a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
                     74:   a.scope = SCOPE_UNIVERSE;
                     75:   a.cast = RTC_UNICAST;
                     76:   a.dest = r->dest;
                     77:   a.gw = r->via;
                     78:   a.iface = ifa;
                     79: 
                     80:   if (r->dest == RTD_MULTIPATH)
                     81:     {
                     82:       struct static_route *r2;
                     83:       struct mpnh *nhs = NULL;
                     84: 
                     85:       for (r2 = r->mp_next; r2; r2 = r2->mp_next)
                     86:        if (r2->installed)
                     87:          {
                     88:            struct mpnh *nh = alloca(sizeof(struct mpnh));
                     89:            nh->gw = r2->via;
                     90:            nh->iface = r2->neigh->iface;
                     91:            nh->weight = r2->masklen; /* really */
                     92:            mpnh_insert(&nhs, nh);
                     93:          }
                     94: 
                     95:       /* There is at least one nexthop */
                     96:       if (!nhs->next)
                     97:        {
                     98:          /* Fallback to unipath route for exactly one nexthop */
                     99:          a.dest = RTD_ROUTER;
                    100:          a.gw = nhs->gw;
                    101:          a.iface = nhs->iface;
                    102:        }
                    103:       else
                    104:        a.nexthops = nhs;
                    105:     }
                    106: 
                    107:   if (r->dest == RTDX_RECURSIVE)
                    108:     rta_set_recursive_next_hop(p->table, &a, p_igp_table(p), &r->via, &r->via);
                    109: 
                    110:   /* We skip rta_lookup() here */
                    111: 
                    112:   n = net_get(p->table, r->net, r->masklen);
                    113:   e = rte_get_temp(&a);
                    114:   e->net = n;
                    115:   e->pflags = 0;
                    116: 
                    117:   if (r->cmds)
                    118:     f_eval_rte(r->cmds, &e, static_lp);
                    119: 
                    120:   rte_update(p, n, e);
                    121:   r->installed = 1;
                    122: 
                    123:   if (r->cmds)
                    124:     lp_flush(static_lp);
                    125: }
                    126: 
                    127: static void
                    128: static_remove(struct proto *p, struct static_route *r)
                    129: {
                    130:   net *n;
                    131: 
                    132:   if (!r->installed)
                    133:     return;
                    134: 
                    135:   DBG("Removing static route %I/%d via %I\n", r->net, r->masklen, r->via);
                    136:   n = net_find(p->table, r->net, r->masklen);
                    137:   rte_update(p, n, NULL);
                    138:   r->installed = 0;
                    139: }
                    140: 
                    141: static void
                    142: static_bfd_notify(struct bfd_request *req);
                    143: 
                    144: static void
                    145: static_update_bfd(struct proto *p, struct static_route *r)
                    146: {
                    147:   struct neighbor *nb = r->neigh;
                    148:   int bfd_up = (nb->scope > 0) && r->use_bfd;
                    149: 
                    150:   if (bfd_up && !r->bfd_req)
                    151:   {
                    152:     // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
1.1.1.2 ! misho     153:     r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface, p->vrf,
1.1       misho     154:                                     static_bfd_notify, r);
                    155:   }
                    156: 
                    157:   if (!bfd_up && r->bfd_req)
                    158:   {
                    159:     rfree(r->bfd_req);
                    160:     r->bfd_req = NULL;
                    161:   }
                    162: }
                    163: 
                    164: static int
                    165: static_decide(struct static_config *cf, struct static_route *r)
                    166: {
                    167:   /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
                    168:      the route also have to be valid (r->neigh != NULL) */
                    169: 
                    170:   if (r->neigh->scope < 0)
                    171:     return 0;
                    172: 
                    173:   if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP))
                    174:     return 0;
                    175: 
                    176:   if (r->bfd_req && r->bfd_req->state != BFD_STATE_UP)
                    177:     return 0;
                    178: 
                    179:   return 1;
                    180: }
                    181: 
                    182: 
                    183: static void
                    184: static_add(struct proto *p, struct static_config *cf, struct static_route *r)
                    185: {
                    186:   DBG("static_add(%I/%d,%d)\n", r->net, r->masklen, r->dest);
                    187:   switch (r->dest)
                    188:     {
                    189:     case RTD_ROUTER:
                    190:       {
                    191:        struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
                    192:        if (n)
                    193:          {
                    194:            r->chain = n->data;
                    195:            n->data = r;
                    196:            r->neigh = n;
                    197: 
                    198:            static_update_bfd(p, r);
                    199:            if (static_decide(cf, r))
                    200:              static_install(p, r, n->iface);
                    201:            else
                    202:              static_remove(p, r);
                    203:          }
                    204:        else
                    205:          {
                    206:            log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
                    207:            static_remove(p, r);
                    208:          }
                    209:        break;
                    210:       }
                    211: 
                    212:     case RTD_DEVICE:
                    213:       break;
                    214: 
                    215:     case RTD_MULTIPATH:
                    216:       {
                    217:        int count = 0;
                    218:        struct static_route *r2;
                    219: 
                    220:        for (r2 = r->mp_next; r2; r2 = r2->mp_next)
                    221:          {
                    222:            struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
                    223:            if (n)
                    224:              {
                    225:                r2->chain = n->data;
                    226:                n->data = r2;
                    227:                r2->neigh = n;
                    228: 
                    229:                static_update_bfd(p, r2);
                    230:                r2->installed = static_decide(cf, r2);
                    231:                count += r2->installed;
                    232:              }
                    233:            else
                    234:              {
                    235:                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
                    236:                r2->installed = 0;
                    237:              }
                    238:          }
                    239: 
                    240:        if (count)
                    241:          static_install(p, r, NULL);
                    242:        else
                    243:          static_remove(p, r);
                    244:        break;
                    245:       }
                    246: 
                    247:     default:
                    248:       static_install(p, r, NULL);
                    249:     }
                    250: }
                    251: 
                    252: static void
                    253: static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
                    254: {
                    255:   struct static_route *r2;
                    256: 
                    257:   if (r->bfd_req)
                    258:   {
                    259:     rfree(r->bfd_req);
                    260:     r->bfd_req = NULL;
                    261:   }
                    262: 
                    263:   if (r->dest == RTD_MULTIPATH)
                    264:     for (r2 = r->mp_next; r2; r2 = r2->mp_next)
                    265:       if (r2->bfd_req)
                    266:       {
                    267:        rfree(r2->bfd_req);
                    268:        r2->bfd_req = NULL;
                    269:       }
                    270: }
                    271: 
                    272: static int
                    273: static_start(struct proto *p)
                    274: {
                    275:   struct static_config *cf = (void *) p->cf;
                    276:   struct static_route *r;
                    277: 
                    278:   DBG("Static: take off!\n");
                    279: 
                    280:   if (!static_lp)
                    281:     static_lp = lp_new(&root_pool, 1008);
                    282: 
                    283:   if (cf->igp_table)
                    284:     rt_lock_table(cf->igp_table->table);
                    285: 
                    286:   /* We have to go UP before routes could be installed */
                    287:   proto_notify_state(p, PS_UP);
                    288: 
                    289:   WALK_LIST(r, cf->other_routes)
                    290:     static_add(p, cf, r);
                    291:   return PS_UP;
                    292: }
                    293: 
                    294: static int
                    295: static_shutdown(struct proto *p)
                    296: {
                    297:   struct static_config *cf = (void *) p->cf;
                    298:   struct static_route *r;
                    299: 
                    300:   /* Just reset the flag, the routes will be flushed by the nest */
                    301:   WALK_LIST(r, cf->iface_routes)
                    302:     r->installed = 0;
                    303:   WALK_LIST(r, cf->other_routes)
                    304:   {
                    305:     static_rte_cleanup(p, r);
                    306:     r->installed = 0;
                    307:   }
                    308: 
                    309:   return PS_DOWN;
                    310: }
                    311: 
                    312: static void
                    313: static_cleanup(struct proto *p)
                    314: {
                    315:   struct static_config *cf = (void *) p->cf;
                    316: 
                    317:   if (cf->igp_table)
                    318:     rt_unlock_table(cf->igp_table->table);
                    319: }
                    320: 
                    321: static void
                    322: static_update_rte(struct proto *p, struct static_route *r)
                    323: {
                    324:   switch (r->dest)
                    325:   {
                    326:   case RTD_ROUTER:
                    327:     if (static_decide((struct static_config *) p->cf, r))
                    328:       static_install(p, r, r->neigh->iface);
                    329:     else
                    330:       static_remove(p, r);
                    331:     break;
                    332: 
                    333:   case RTD_NONE: /* a part of multipath route */
                    334:   {
                    335:     int decision = static_decide((struct static_config *) p->cf, r);
                    336:     if (decision == r->installed)
                    337:       break; /* no change */
                    338:     r->installed = decision;
                    339: 
                    340:     struct static_route *r1, *r2;
                    341:     int count = 0;
                    342:     r1 = (void *) r->if_name; /* really */
                    343:     for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
                    344:       count += r2->installed;
                    345: 
                    346:     if (count)
                    347:     {
                    348:       /* Set of nexthops changed - force reinstall */
                    349:       r1->installed = 0;
                    350:       static_install(p, r1, NULL);
                    351:     }
                    352:     else
                    353:       static_remove(p, r1);
                    354: 
                    355:     break;
                    356:   }
                    357:   }
                    358: }
                    359: 
                    360: static void
                    361: static_neigh_notify(struct neighbor *n)
                    362: {
                    363:   struct proto *p = n->proto;
                    364:   struct static_route *r;
                    365: 
                    366:   DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
                    367:   for(r=n->data; r; r=r->chain)
                    368:   {
                    369:     static_update_bfd(p, r);
                    370:     static_update_rte(p, r);
                    371:   }
                    372: }
                    373: 
                    374: static void
                    375: static_bfd_notify(struct bfd_request *req)
                    376: {
                    377:   struct static_route *r = req->data;
                    378:   struct proto *p = r->neigh->proto;
                    379: 
                    380:   // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX);
                    381: 
                    382:   static_update_rte(p, r);
                    383: }
                    384: 
                    385: static void
                    386: static_dump_rt(struct static_route *r)
                    387: {
                    388:   debug("%-1I/%2d: ", r->net, r->masklen);
                    389:   switch (r->dest)
                    390:     {
                    391:     case RTD_ROUTER:
                    392:       debug("via %I\n", r->via);
                    393:       break;
                    394:     case RTD_DEVICE:
                    395:       debug("dev %s\n", r->if_name);
                    396:       break;
                    397:     default:
                    398:       debug("rtd %d\n", r->dest);
                    399:       break;
                    400:     }
                    401: }
                    402: 
                    403: static void
                    404: static_dump(struct proto *p)
                    405: {
                    406:   struct static_config *c = (void *) p->cf;
                    407:   struct static_route *r;
                    408: 
                    409:   debug("Independent static routes:\n");
                    410:   WALK_LIST(r, c->other_routes)
                    411:     static_dump_rt(r);
                    412:   debug("Device static routes:\n");
                    413:   WALK_LIST(r, c->iface_routes)
                    414:     static_dump_rt(r);
                    415: }
                    416: 
                    417: static void
                    418: static_if_notify(struct proto *p, unsigned flags, struct iface *i)
                    419: {
                    420:   struct static_route *r;
                    421:   struct static_config *c = (void *) p->cf;
                    422: 
                    423:   if (flags & IF_CHANGE_UP)
                    424:     {
                    425:       WALK_LIST(r, c->iface_routes)
                    426:        if (!strcmp(r->if_name, i->name))
                    427:          static_install(p, r, i);
                    428:     }
                    429:   else if (flags & IF_CHANGE_DOWN)
                    430:     {
                    431:       WALK_LIST(r, c->iface_routes)
                    432:        if (!strcmp(r->if_name, i->name))
                    433:          static_remove(p, r);
                    434:     }
                    435: }
                    436: 
                    437: int
                    438: static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED)
                    439: {
                    440:   return 1;
                    441: }
                    442: 
                    443: void
                    444: static_init_config(struct static_config *c)
                    445: {
                    446:   init_list(&c->iface_routes);
                    447:   init_list(&c->other_routes);
                    448: }
                    449: 
                    450: static struct proto *
                    451: static_init(struct proto_config *c)
                    452: {
                    453:   struct proto *p = proto_new(c, sizeof(struct proto));
                    454: 
                    455:   p->neigh_notify = static_neigh_notify;
                    456:   p->if_notify = static_if_notify;
                    457:   p->rte_mergable = static_rte_mergable;
                    458: 
                    459:   return p;
                    460: }
                    461: 
                    462: static inline int
                    463: static_same_net(struct static_route *x, struct static_route *y)
                    464: {
                    465:   return ipa_equal(x->net, y->net) && (x->masklen == y->masklen);
                    466: }
                    467: 
                    468: static inline int
                    469: static_same_dest(struct static_route *x, struct static_route *y)
                    470: {
                    471:   if (x->dest != y->dest)
                    472:     return 0;
                    473: 
                    474:   switch (x->dest)
                    475:     {
                    476:     case RTD_ROUTER:
                    477:       return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
                    478: 
                    479:     case RTD_DEVICE:
                    480:       return !strcmp(x->if_name, y->if_name);
                    481: 
                    482:     case RTD_MULTIPATH:
                    483:       for (x = x->mp_next, y = y->mp_next;
                    484:           x && y;
                    485:           x = x->mp_next, y = y->mp_next)
                    486:        if (!ipa_equal(x->via, y->via) || (x->via_if != y->via_if) || (x->use_bfd != y->use_bfd))
                    487:          return 0;
                    488:       return !x && !y;
                    489: 
                    490:     case RTDX_RECURSIVE:
                    491:       return ipa_equal(x->via, y->via);
                    492: 
                    493:     default:
                    494:       return 1;
                    495:     }
                    496: }
                    497: 
                    498: static inline int
                    499: static_same_rte(struct static_route *x, struct static_route *y)
                    500: {
1.1.1.2 ! misho     501:   /* Note that i_same() requires arguments in (new, old) order */
        !           502:   return static_same_dest(x, y) && i_same(y->cmds, x->cmds);
1.1       misho     503: }
                    504: 
                    505: 
                    506: static void
                    507: static_match(struct proto *p, struct static_route *r, struct static_config *n)
                    508: {
                    509:   struct static_route *t;
                    510: 
                    511:   /*
                    512:    * For given old route *r we find whether a route to the same
                    513:    * network is also in the new route list. In that case, we keep the
                    514:    * route and possibly update the route later if destination changed.
                    515:    * Otherwise, we remove the route.
                    516:    */
                    517: 
                    518:   if (r->neigh)
                    519:     r->neigh->data = NULL;
                    520: 
1.1.1.2 ! misho     521:   if (r->dest == RTD_MULTIPATH)
        !           522:     for (t = r->mp_next; t; t = t->mp_next)
        !           523:       if (t->neigh)
        !           524:        t->neigh->data = NULL;
        !           525: 
1.1       misho     526:   WALK_LIST(t, n->iface_routes)
                    527:     if (static_same_net(r, t))
                    528:       goto found;
                    529: 
                    530:   WALK_LIST(t, n->other_routes)
                    531:     if (static_same_net(r, t))
                    532:       goto found;
                    533: 
                    534:   static_remove(p, r);
                    535:   return;
                    536: 
                    537:  found:
                    538:   /* If destination is different, force reinstall */
                    539:   if ((r->installed > 0) && !static_same_rte(r, t))
                    540:     t->installed = -1;
                    541:   else
                    542:     t->installed = r->installed;
                    543: }
                    544: 
                    545: static inline rtable *
                    546: cf_igp_table(struct static_config *cf)
                    547: {
                    548:   return cf->igp_table ? cf->igp_table->table : NULL;
                    549: }
                    550: 
                    551: static int
                    552: static_reconfigure(struct proto *p, struct proto_config *new)
                    553: {
                    554:   struct static_config *o = (void *) p->cf;
                    555:   struct static_config *n = (void *) new;
                    556:   struct static_route *r;
                    557: 
                    558:   if (cf_igp_table(o) != cf_igp_table(n))
                    559:     return 0;
                    560: 
                    561:   /* Delete all obsolete routes and reset neighbor entries */
                    562:   WALK_LIST(r, o->iface_routes)
                    563:     static_match(p, r, n);
                    564:   WALK_LIST(r, o->other_routes)
                    565:     static_match(p, r, n);
                    566: 
                    567:   /* Now add all new routes, those not changed will be ignored by static_install() */
                    568:   WALK_LIST(r, n->iface_routes)
                    569:     {
                    570:       struct iface *ifa;
                    571:       if ((ifa = if_find_by_name(r->if_name)) && (ifa->flags & IF_UP))
                    572:        static_install(p, r, ifa);
                    573:     }
                    574:   WALK_LIST(r, n->other_routes)
                    575:     static_add(p, n, r);
                    576: 
                    577:   WALK_LIST(r, o->other_routes)
                    578:     static_rte_cleanup(p, r);
                    579: 
                    580:   return 1;
                    581: }
                    582: 
                    583: static void
                    584: static_copy_routes(list *dlst, list *slst)
                    585: {
                    586:   struct static_route *dr, *sr;
                    587: 
                    588:   init_list(dlst);
                    589:   WALK_LIST(sr, *slst)
                    590:     {
                    591:       /* copy one route */
                    592:       dr = cfg_alloc(sizeof(struct static_route));
                    593:       memcpy(dr, sr, sizeof(struct static_route));
                    594: 
                    595:       /* This fn is supposed to be called on fresh src routes, which have 'live'
                    596:         fields (like .chain, .neigh or .installed) zero, so no need to zero them */
                    597: 
                    598:       /* We need to copy multipath chain, because there are backptrs in 'if_name' */
                    599:       if (dr->dest == RTD_MULTIPATH)
                    600:        {
                    601:          struct static_route *md, *ms, **mp_last;
                    602: 
                    603:          mp_last = &(dr->mp_next);
                    604:          for (ms = sr->mp_next; ms; ms = ms->mp_next)
                    605:            {
                    606:              md = cfg_alloc(sizeof(struct static_route));
                    607:              memcpy(md, ms, sizeof(struct static_route));
                    608:              md->if_name = (void *) dr; /* really */
                    609: 
                    610:              *mp_last = md;
                    611:              mp_last = &(md->mp_next);
                    612:            }
                    613:          *mp_last = NULL;
                    614:        }
                    615: 
                    616:       add_tail(dlst, (node *) dr);
                    617:     }
                    618: }
                    619: 
                    620: static void
                    621: static_copy_config(struct proto_config *dest, struct proto_config *src)
                    622: {
                    623:   struct static_config *d = (struct static_config *) dest;
                    624:   struct static_config *s = (struct static_config *) src;
                    625: 
                    626:   /* Shallow copy of everything */
                    627:   proto_copy_rest(dest, src, sizeof(struct static_config));
                    628: 
                    629:   /* Copy route lists */
                    630:   static_copy_routes(&d->iface_routes, &s->iface_routes);
                    631:   static_copy_routes(&d->other_routes, &s->other_routes);
                    632: }
                    633: 
                    634: 
                    635: struct protocol proto_static = {
                    636:   .name =              "Static",
                    637:   .template =          "static%d",
                    638:   .preference =                DEF_PREF_STATIC,
                    639:   .config_size =       sizeof(struct static_config),
                    640:   .init =              static_init,
                    641:   .dump =              static_dump,
                    642:   .start =             static_start,
                    643:   .shutdown =          static_shutdown,
                    644:   .cleanup =           static_cleanup,
                    645:   .reconfigure =       static_reconfigure,
                    646:   .copy_config =       static_copy_config
                    647: };
                    648: 
                    649: static void
                    650: static_show_rt(struct static_route *r)
                    651: {
                    652:   byte via[STD_ADDRESS_P_LENGTH + 16];
                    653: 
                    654:   switch (r->dest)
                    655:     {
                    656:     case RTD_ROUTER:   bsprintf(via, "via %I%J", r->via, r->via_if); break;
                    657:     case RTD_DEVICE:   bsprintf(via, "dev %s", r->if_name); break;
                    658:     case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
                    659:     case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
                    660:     case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
                    661:     case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
                    662:     case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
                    663:     default:           bsprintf(via, "???");
                    664:     }
                    665:   cli_msg(-1009, "%I/%d %s%s%s", r->net, r->masklen, via,
                    666:          r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
                    667: 
                    668:   struct static_route *r2;
                    669:   if (r->dest == RTD_MULTIPATH)
                    670:     for (r2 = r->mp_next; r2; r2 = r2->mp_next)
                    671:       cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->masklen + 1, /* really */
                    672:              r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
                    673: }
                    674: 
                    675: void
                    676: static_show(struct proto *P)
                    677: {
                    678:   struct static_config *c = (void *) P->cf;
                    679:   struct static_route *r;
                    680: 
                    681:   WALK_LIST(r, c->other_routes)
                    682:     static_show_rt(r);
                    683:   WALK_LIST(r, c->iface_routes)
                    684:     static_show_rt(r);
                    685:   cli_msg(0, "");
                    686: }

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