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>