File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / radv / radv.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Aug 22 12:33:54 2017 UTC (6 years, 11 months ago) by misho
Branches: bird, MAIN
CVS tags: v1_6_3p0, v1_6_3, HEAD
bird 1.6.3

    1: /*
    2:  *	BIRD -- Router Advertisement
    3:  *
    4:  *
    5:  *	Can be freely distributed and used under the terms of the GNU GPL.
    6:  */
    7: 
    8: 
    9: #include <stdlib.h>
   10: #include "radv.h"
   11: 
   12: /**
   13:  * DOC: Router Advertisements
   14:  *
   15:  * The RAdv protocol is implemented in two files: |radv.c| containing
   16:  * the interface with BIRD core and the protocol logic and |packets.c|
   17:  * handling low level protocol stuff (RX, TX and packet formats).
   18:  * The protocol does not export any routes.
   19:  *
   20:  * The RAdv is structured in the usual way - for each handled interface
   21:  * there is a structure &radv_iface that contains a state related to
   22:  * that interface together with its resources (a socket, a timer).
   23:  * There is also a prepared RA stored in a TX buffer of the socket
   24:  * associated with an iface. These iface structures are created
   25:  * and removed according to iface events from BIRD core handled by
   26:  * radv_if_notify() callback.
   27:  *
   28:  * The main logic of RAdv consists of two functions:
   29:  * radv_iface_notify(), which processes asynchronous events (specified
   30:  * by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
   31:  * computes the next timeout.
   32:  *
   33:  * The RAdv protocol could receive routes (through
   34:  * radv_import_control() and radv_rt_notify()), but only the
   35:  * configured trigger route is tracked (in &active var).  When a radv
   36:  * protocol is reconfigured, the connected routing table is examined
   37:  * (in radv_check_active()) to have proper &active value in case of
   38:  * the specified trigger prefix was changed.
   39:  *
   40:  * Supported standards:
   41:  * - RFC 4861 - main RA standard
   42:  * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
   43:  * - RFC 4191 (partial) - Default Router Preference
   44:  */
   45: 
   46: static void
   47: radv_timer(timer *tm)
   48: {
   49:   struct radv_iface *ifa = tm->data;
   50:   struct proto_radv *ra = ifa->ra;
   51: 
   52:   RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
   53: 
   54:   radv_send_ra(ifa, 0);
   55: 
   56:   /* Update timer */
   57:   ifa->last = now;
   58:   unsigned after = ifa->cf->min_ra_int;
   59:   after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
   60: 
   61:   if (ifa->initial)
   62:     ifa->initial--;
   63: 
   64:   if (ifa->initial)
   65:     after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
   66: 
   67:   tm_start(ifa->timer, after);
   68: }
   69: 
   70: static char* ev_name[] = { NULL, "Init", "Change", "RS" };
   71: 
   72: void
   73: radv_iface_notify(struct radv_iface *ifa, int event)
   74: {
   75:   struct proto_radv *ra = ifa->ra;
   76: 
   77:   if (!ifa->sk)
   78:     return;
   79: 
   80:   RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
   81: 
   82:   switch (event)
   83:   {
   84:   case RA_EV_CHANGE:
   85:     ifa->plen = 0;
   86:   case RA_EV_INIT:
   87:     ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
   88:     break;
   89: 
   90:   case RA_EV_RS:
   91:     break;
   92:   }
   93: 
   94:   /* Update timer */
   95:   unsigned delta = now - ifa->last;
   96:   unsigned after = 0;
   97: 
   98:   if (delta < ifa->cf->min_delay)
   99:     after = ifa->cf->min_delay - delta;
  100: 
  101:   tm_start(ifa->timer, after);
  102: }
  103: 
  104: static void
  105: radv_iface_notify_all(struct proto_radv *ra, int event)
  106: {
  107:   struct radv_iface *ifa;
  108: 
  109:   WALK_LIST(ifa, ra->iface_list)
  110:     radv_iface_notify(ifa, event);
  111: }
  112: 
  113: 
  114: static struct radv_iface *
  115: radv_iface_find(struct proto_radv *ra, struct iface *what)
  116: {
  117:   struct radv_iface *ifa;
  118: 
  119:   WALK_LIST(ifa, ra->iface_list)
  120:     if (ifa->iface == what)
  121:       return ifa;
  122: 
  123:   return NULL;
  124: }
  125: 
  126: static void
  127: radv_iface_add(struct object_lock *lock)
  128: {
  129:   struct radv_iface *ifa = lock->data;
  130:   struct proto_radv *ra = ifa->ra;
  131: 
  132:   if (! radv_sk_open(ifa))
  133:   {
  134:     log(L_ERR "%s: Socket open failed on interface %s", ra->p.name, ifa->iface->name);
  135:     return;
  136:   }
  137: 
  138:   radv_iface_notify(ifa, RA_EV_INIT);
  139: }
  140: 
  141: static inline struct ifa *
  142: find_lladdr(struct iface *iface)
  143: {
  144:   struct ifa *a;
  145:   WALK_LIST(a, iface->addrs)
  146:     if (a->scope == SCOPE_LINK)
  147:       return a;
  148: 
  149:   return NULL;
  150: }
  151: 
  152: static void
  153: radv_iface_new(struct proto_radv *ra, struct iface *iface, struct radv_iface_config *cf)
  154: {
  155:   pool *pool = ra->p.pool;
  156:   struct radv_iface *ifa;
  157: 
  158:   RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
  159: 
  160:   ifa = mb_allocz(pool, sizeof(struct radv_iface));
  161:   ifa->ra = ra;
  162:   ifa->cf = cf;
  163:   ifa->iface = iface;
  164: 
  165:   add_tail(&ra->iface_list, NODE ifa);
  166: 
  167:   ifa->addr = find_lladdr(iface);
  168:   if (!ifa->addr)
  169:   {
  170:     log(L_ERR "%s: Cannot find link-locad addr on interface %s", ra->p.name, iface->name);
  171:     return;
  172:   }
  173: 
  174:   timer *tm = tm_new(pool);
  175:   tm->hook = radv_timer;
  176:   tm->data = ifa;
  177:   tm->randomize = 0;
  178:   tm->recurrent = 0;
  179:   ifa->timer = tm;
  180: 
  181:   struct object_lock *lock = olock_new(pool);
  182:   lock->addr = IPA_NONE;
  183:   lock->type = OBJLOCK_IP;
  184:   lock->port = ICMPV6_PROTO;
  185:   lock->iface = iface;
  186:   lock->data = ifa;
  187:   lock->hook = radv_iface_add;
  188:   ifa->lock = lock;
  189: 
  190:   olock_acquire(lock);
  191: }
  192: 
  193: static void
  194: radv_iface_remove(struct radv_iface *ifa)
  195: {
  196:   struct proto_radv *ra = ifa->ra;
  197:   RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
  198: 
  199:   rem_node(NODE ifa);
  200: 
  201:   rfree(ifa->sk);
  202:   rfree(ifa->timer);
  203:   rfree(ifa->lock);
  204: 
  205:   mb_free(ifa);
  206: }
  207: 
  208: static void
  209: radv_if_notify(struct proto *p, unsigned flags, struct iface *iface)
  210: {
  211:   struct proto_radv *ra = (struct proto_radv *) p;
  212:   struct radv_config *cf = (struct radv_config *) (p->cf);
  213: 
  214:   if (iface->flags & IF_IGNORE)
  215:     return;
  216: 
  217:   if (flags & IF_CHANGE_UP)
  218:   {
  219:     struct radv_iface_config *ic = (struct radv_iface_config *)
  220:       iface_patt_find(&cf->patt_list, iface, NULL);
  221: 
  222:     if (ic)
  223:       radv_iface_new(ra, iface, ic);
  224: 
  225:     return;
  226:   }
  227: 
  228:   struct radv_iface *ifa = radv_iface_find(ra, iface);
  229:   if (!ifa)
  230:     return;
  231: 
  232:   if (flags & IF_CHANGE_DOWN)
  233:   {
  234:     radv_iface_remove(ifa);
  235:     return;
  236:   }
  237: 
  238:   if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
  239:     radv_iface_notify(ifa, RA_EV_INIT);
  240: }
  241: 
  242: static void
  243: radv_ifa_notify(struct proto *p, unsigned flags UNUSED, struct ifa *a)
  244: {
  245:   struct proto_radv *ra = (struct proto_radv *) p;
  246: 
  247:   if (a->flags & IA_SECONDARY)
  248:     return;
  249: 
  250:   if (a->scope <= SCOPE_LINK)
  251:     return;
  252: 
  253:   struct radv_iface *ifa = radv_iface_find(ra, a->iface);
  254: 
  255:   if (ifa)
  256:     radv_iface_notify(ifa, RA_EV_CHANGE);
  257: }
  258: 
  259: static inline int radv_net_match_trigger(struct radv_config *cf, net *n)
  260: {
  261:   return cf->trigger_valid &&
  262:     (n->n.pxlen == cf->trigger_pxlen) &&
  263:     ipa_equal(n->n.prefix, cf->trigger_prefix);
  264: }
  265: 
  266: int
  267: radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
  268: {
  269:   // struct proto_radv *ra = (struct proto_radv *) p;
  270:   struct radv_config *cf = (struct radv_config *) (p->cf);
  271: 
  272:   if (radv_net_match_trigger(cf, (*new)->net))
  273:     return RIC_PROCESS;
  274: 
  275:   return RIC_DROP;
  276: }
  277: 
  278: static void
  279: radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
  280: {
  281:   struct proto_radv *ra = (struct proto_radv *) p;
  282:   struct radv_config *cf = (struct radv_config *) (p->cf);
  283: 
  284:   if (radv_net_match_trigger(cf, n))
  285:   {
  286:     u8 old_active = ra->active;
  287:     ra->active = !!new;
  288: 
  289:     if (ra->active == old_active)
  290:       return;
  291: 
  292:     if (ra->active)
  293:       RADV_TRACE(D_EVENTS, "Triggered");
  294:     else
  295:       RADV_TRACE(D_EVENTS, "Suppressed");
  296: 
  297:     radv_iface_notify_all(ra, RA_EV_CHANGE);
  298:   }
  299: }
  300: 
  301: static int
  302: radv_check_active(struct proto_radv *ra)
  303: {
  304:   struct radv_config *cf = (struct radv_config *) (ra->p.cf);
  305: 
  306:   if (! cf->trigger_valid)
  307:     return 1;
  308: 
  309:   return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen,
  310: 		    &(ra->p), ra->p.cf->out_filter);
  311: }
  312: 
  313: static struct proto *
  314: radv_init(struct proto_config *c)
  315: {
  316:   struct proto *p = proto_new(c, sizeof(struct proto_radv));
  317: 
  318:   p->accept_ra_types = RA_OPTIMAL;
  319:   p->import_control = radv_import_control;
  320:   p->rt_notify = radv_rt_notify;
  321:   p->if_notify = radv_if_notify;
  322:   p->ifa_notify = radv_ifa_notify;
  323:   return p;
  324: }
  325: 
  326: static int
  327: radv_start(struct proto *p)
  328: {
  329:   struct proto_radv *ra = (struct proto_radv *) p;
  330:   struct radv_config *cf = (struct radv_config *) (p->cf);
  331: 
  332:   init_list(&(ra->iface_list));
  333:   ra->active = !cf->trigger_valid;
  334: 
  335:   return PS_UP;
  336: }
  337: 
  338: static inline void
  339: radv_iface_shutdown(struct radv_iface *ifa)
  340: {
  341:   if (ifa->sk)
  342:     radv_send_ra(ifa, 1);
  343: }
  344: 
  345: static int
  346: radv_shutdown(struct proto *p)
  347: {
  348:   struct proto_radv *ra = (struct proto_radv *) p;
  349: 
  350:   struct radv_iface *ifa;
  351:   WALK_LIST(ifa, ra->iface_list)
  352:     radv_iface_shutdown(ifa);
  353: 
  354:   return PS_DOWN;
  355: }
  356: 
  357: static int
  358: radv_reconfigure(struct proto *p, struct proto_config *c)
  359: {
  360:   struct proto_radv *ra = (struct proto_radv *) p;
  361:   // struct radv_config *old = (struct radv_config *) (p->cf);
  362:   struct radv_config *new = (struct radv_config *) c;
  363: 
  364:   /*
  365:    * The question is why there is a reconfigure function for RAdv if
  366:    * it has almost none internal state so restarting the protocol
  367:    * would probably suffice. One small reason is that restarting the
  368:    * protocol would lead to sending a RA with Router Lifetime 0
  369:    * causing nodes to temporary remove their default routes.
  370:    */
  371: 
  372:   p->cf = c; /* radv_check_active() requires proper p->cf */
  373:   ra->active = radv_check_active(ra);
  374: 
  375:   struct iface *iface;
  376:   WALK_LIST(iface, iface_list)
  377:   {
  378:     struct radv_iface *ifa = radv_iface_find(ra, iface);
  379:     struct radv_iface_config *ic = (struct radv_iface_config *)
  380:       iface_patt_find(&new->patt_list, iface, NULL);
  381: 
  382:     if (ifa && ic)
  383:     {
  384:       ifa->cf = ic;
  385: 
  386:       /* We cheat here - always notify the change even if there isn't
  387: 	 any. That would leads just to a few unnecessary RAs. */
  388:       radv_iface_notify(ifa, RA_EV_CHANGE);
  389:     }
  390: 
  391:     if (ifa && !ic)
  392:     {
  393:       radv_iface_shutdown(ifa);
  394:       radv_iface_remove(ifa);
  395:     }
  396: 
  397:     if (!ifa && ic)
  398:       radv_iface_new(ra, iface, ic);
  399:   }
  400: 
  401:   return 1;
  402: }
  403: 
  404: static void
  405: radv_copy_config(struct proto_config *dest, struct proto_config *src)
  406: {
  407:   struct radv_config *d = (struct radv_config *) dest;
  408:   struct radv_config *s = (struct radv_config *) src;
  409: 
  410:   /* We clean up patt_list, ifaces are non-sharable */
  411:   init_list(&d->patt_list);
  412: 
  413:   /* We copy pref_list, shallow copy suffices */
  414:   cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
  415: }
  416: 
  417: static void
  418: radv_get_status(struct proto *p, byte *buf)
  419: {
  420:   struct proto_radv *ra = (struct proto_radv *) p;
  421: 
  422:   if (!ra->active)
  423:     strcpy(buf, "Suppressed");
  424: }
  425: 
  426: struct protocol proto_radv = {
  427:   .name =		"RAdv",
  428:   .template =		"radv%d",
  429:   .config_size =	sizeof(struct radv_config),
  430:   .init =		radv_init,
  431:   .start =		radv_start,
  432:   .shutdown =		radv_shutdown,
  433:   .reconfigure =	radv_reconfigure,
  434:   .copy_config =	radv_copy_config,
  435:   .get_status =		radv_get_status
  436: };

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