Annotation of embedaddon/bird/proto/radv/radv.c, revision 1.1

1.1     ! misho       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>