File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / static / static.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 19:50:23 2021 UTC (3 years, 3 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_8p3, HEAD
bird 1.6.8

    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;
  153:     r->bfd_req = bfd_request_session(p->pool, r->via, nb->ifa->ip, nb->iface, p->vrf,
  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: {
  501:   /* Note that i_same() requires arguments in (new, old) order */
  502:   return static_same_dest(x, y) && i_same(y->cmds, x->cmds);
  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: 
  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: 
  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>