Return to radv.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / radv |
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: };