Return to radv.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird2 / proto / radv |
1.1 ! misho 1: /* ! 2: * BIRD -- Router Advertisement ! 3: * ! 4: * (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org> ! 5: * (c) 2011--2019 CZ.NIC z.s.p.o. ! 6: * ! 7: * Can be freely distributed and used under the terms of the GNU GPL. ! 8: */ ! 9: ! 10: ! 11: #include <stdlib.h> ! 12: #include "radv.h" ! 13: ! 14: /** ! 15: * DOC: Router Advertisements ! 16: * ! 17: * The RAdv protocol is implemented in two files: |radv.c| containing the ! 18: * interface with BIRD core and the protocol logic and |packets.c| handling low ! 19: * level protocol stuff (RX, TX and packet formats). The protocol does not ! 20: * export any routes. ! 21: * ! 22: * The RAdv is structured in the usual way - for each handled interface there is ! 23: * a structure &radv_iface that contains a state related to that interface ! 24: * together with its resources (a socket, a timer). There is also a prepared RA ! 25: * stored in a TX buffer of the socket associated with an iface. These iface ! 26: * structures are created and removed according to iface events from BIRD core ! 27: * handled by radv_if_notify() callback. ! 28: * ! 29: * The main logic of RAdv consists of two functions: radv_iface_notify(), which ! 30: * processes asynchronous events (specified by RA_EV_* codes), and radv_timer(), ! 31: * which triggers sending RAs and computes the next timeout. ! 32: * ! 33: * The RAdv protocol could receive routes (through radv_preexport() and ! 34: * radv_rt_notify()), but only the configured trigger route is tracked (in ! 35: * &active var). When a radv protocol is reconfigured, the connected routing ! 36: * table is examined (in radv_check_active()) to have proper &active value in ! 37: * case of the specified trigger prefix was changed. ! 38: * ! 39: * Supported standards: ! 40: * RFC 4861 - main RA standard ! 41: * RFC 4191 - Default Router Preferences and More-Specific Routes ! 42: * RFC 6106 - DNS extensions (RDDNS, DNSSL) ! 43: */ ! 44: ! 45: static void radv_prune_prefixes(struct radv_iface *ifa); ! 46: static void radv_prune_routes(struct radv_proto *p); ! 47: ! 48: static void ! 49: radv_timer(timer *tm) ! 50: { ! 51: struct radv_iface *ifa = tm->data; ! 52: struct radv_proto *p = ifa->ra; ! 53: btime now = current_time(); ! 54: ! 55: RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name); ! 56: ! 57: if (ifa->prune_time <= now) ! 58: radv_prune_prefixes(ifa); ! 59: ! 60: if (p->prune_time <= now) ! 61: radv_prune_routes(p); ! 62: ! 63: radv_send_ra(ifa, IPA_NONE); ! 64: ! 65: /* Update timer */ ! 66: ifa->last = now; ! 67: btime t = ifa->cf->min_ra_int S; ! 68: btime r = (ifa->cf->max_ra_int - ifa->cf->min_ra_int) S; ! 69: t += random() % (r + 1); ! 70: ! 71: if (ifa->initial) ! 72: { ! 73: t = MIN(t, MAX_INITIAL_RTR_ADVERT_INTERVAL); ! 74: ifa->initial--; ! 75: } ! 76: ! 77: tm_start(ifa->timer, t); ! 78: } ! 79: ! 80: static struct radv_prefix_config default_prefix = { ! 81: .onlink = 1, ! 82: .autonomous = 1, ! 83: .valid_lifetime = DEFAULT_VALID_LIFETIME, ! 84: .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME ! 85: }; ! 86: ! 87: static struct radv_prefix_config dead_prefix = { ! 88: }; ! 89: ! 90: /* Find a corresponding config for the given prefix */ ! 91: static struct radv_prefix_config * ! 92: radv_prefix_match(struct radv_iface *ifa, net_addr_ip6 *px) ! 93: { ! 94: struct radv_proto *p = ifa->ra; ! 95: struct radv_config *cf = (struct radv_config *) (p->p.cf); ! 96: struct radv_prefix_config *pc; ! 97: ! 98: WALK_LIST(pc, ifa->cf->pref_list) ! 99: if (net_in_net_ip6(px, &pc->prefix)) ! 100: return pc; ! 101: ! 102: WALK_LIST(pc, cf->pref_list) ! 103: if (net_in_net_ip6(px, &pc->prefix)) ! 104: return pc; ! 105: ! 106: return &default_prefix; ! 107: } ! 108: ! 109: /* ! 110: * Go through the list of prefixes, compare them with configs and decide if we ! 111: * want them or not. ! 112: */ ! 113: static void ! 114: radv_prepare_prefixes(struct radv_iface *ifa) ! 115: { ! 116: struct radv_proto *p = ifa->ra; ! 117: struct radv_prefix *pfx, *next; ! 118: btime now = current_time(); ! 119: ! 120: /* First mark all the prefixes as unused */ ! 121: WALK_LIST(pfx, ifa->prefixes) ! 122: pfx->mark = 0; ! 123: ! 124: /* Find all the prefixes we want to use and make sure they are in the list. */ ! 125: struct ifa *addr; ! 126: WALK_LIST(addr, ifa->iface->addrs) ! 127: { ! 128: if ((addr->prefix.type != NET_IP6) || ! 129: (addr->scope <= SCOPE_LINK)) ! 130: continue; ! 131: ! 132: net_addr_ip6 *prefix = (void *) &addr->prefix; ! 133: struct radv_prefix_config *pc = radv_prefix_match(ifa, prefix); ! 134: ! 135: if (!pc || pc->skip) ! 136: continue; ! 137: ! 138: /* Do we have it already? */ ! 139: struct radv_prefix *existing = NULL; ! 140: WALK_LIST(pfx, ifa->prefixes) ! 141: if (net_equal_ip6(&pfx->prefix, prefix)) ! 142: { ! 143: existing = pfx; ! 144: break; ! 145: } ! 146: ! 147: if (!existing) ! 148: { ! 149: RADV_TRACE(D_EVENTS, "Adding new prefix %N on %s", ! 150: prefix, ifa->iface->name); ! 151: ! 152: existing = mb_allocz(ifa->pool, sizeof *existing); ! 153: net_copy_ip6(&existing->prefix, prefix); ! 154: add_tail(&ifa->prefixes, NODE existing); ! 155: } ! 156: ! 157: /* ! 158: * Update the information (it may have changed, or even bring a prefix back ! 159: * to life). ! 160: */ ! 161: existing->valid = 1; ! 162: existing->changed = now; ! 163: existing->mark = 1; ! 164: existing->cf = pc; ! 165: } ! 166: ! 167: WALK_LIST_DELSAFE(pfx, next, ifa->prefixes) ! 168: { ! 169: if (pfx->valid && !pfx->mark) ! 170: { ! 171: RADV_TRACE(D_EVENTS, "Invalidating prefix %N on %s", ! 172: &pfx->prefix, ifa->iface->name); ! 173: ! 174: pfx->valid = 0; ! 175: pfx->changed = now; ! 176: pfx->cf = &dead_prefix; ! 177: } ! 178: } ! 179: } ! 180: ! 181: static void ! 182: radv_prune_prefixes(struct radv_iface *ifa) ! 183: { ! 184: struct radv_proto *p = ifa->ra; ! 185: btime now = current_time(); ! 186: btime next = TIME_INFINITY; ! 187: btime expires = 0; ! 188: ! 189: struct radv_prefix *px, *pxn; ! 190: WALK_LIST_DELSAFE(px, pxn, ifa->prefixes) ! 191: { ! 192: if (!px->valid) ! 193: { ! 194: expires = px->changed + ifa->cf->prefix_linger_time S; ! 195: ! 196: if (expires <= now) ! 197: { ! 198: RADV_TRACE(D_EVENTS, "Removing prefix %N on %s", ! 199: &px->prefix, ifa->iface->name); ! 200: ! 201: rem_node(NODE px); ! 202: mb_free(px); ! 203: } ! 204: else ! 205: next = MIN(next, expires); ! 206: } ! 207: } ! 208: ! 209: ifa->prune_time = next; ! 210: } ! 211: ! 212: static char* ev_name[] = { NULL, "Init", "Change", "RS" }; ! 213: ! 214: void ! 215: radv_iface_notify(struct radv_iface *ifa, int event) ! 216: { ! 217: struct radv_proto *p = ifa->ra; ! 218: ! 219: if (!ifa->sk) ! 220: return; ! 221: ! 222: RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name); ! 223: ! 224: switch (event) ! 225: { ! 226: case RA_EV_CHANGE: ! 227: radv_invalidate(ifa); ! 228: /* fallthrough */ ! 229: case RA_EV_INIT: ! 230: ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS; ! 231: radv_prepare_prefixes(ifa); ! 232: radv_prune_prefixes(ifa); ! 233: break; ! 234: ! 235: case RA_EV_RS: ! 236: break; ! 237: } ! 238: ! 239: /* Update timer */ ! 240: btime t = ifa->last + ifa->cf->min_delay S - current_time(); ! 241: tm_start(ifa->timer, t); ! 242: } ! 243: ! 244: static void ! 245: radv_iface_notify_all(struct radv_proto *p, int event) ! 246: { ! 247: struct radv_iface *ifa; ! 248: ! 249: WALK_LIST(ifa, p->iface_list) ! 250: radv_iface_notify(ifa, event); ! 251: } ! 252: ! 253: static struct radv_iface * ! 254: radv_iface_find(struct radv_proto *p, struct iface *what) ! 255: { ! 256: struct radv_iface *ifa; ! 257: ! 258: WALK_LIST(ifa, p->iface_list) ! 259: if (ifa->iface == what) ! 260: return ifa; ! 261: ! 262: return NULL; ! 263: } ! 264: ! 265: static void ! 266: radv_iface_add(struct object_lock *lock) ! 267: { ! 268: struct radv_iface *ifa = lock->data; ! 269: struct radv_proto *p = ifa->ra; ! 270: ! 271: if (! radv_sk_open(ifa)) ! 272: { ! 273: log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name); ! 274: return; ! 275: } ! 276: ! 277: radv_iface_notify(ifa, RA_EV_INIT); ! 278: } ! 279: ! 280: static void ! 281: radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf) ! 282: { ! 283: struct radv_iface *ifa; ! 284: ! 285: RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name); ! 286: ! 287: pool *pool = rp_new(p->p.pool, iface->name); ! 288: ifa = mb_allocz(pool, sizeof(struct radv_iface)); ! 289: ifa->pool = pool; ! 290: ifa->ra = p; ! 291: ifa->cf = cf; ! 292: ifa->iface = iface; ! 293: ifa->addr = iface->llv6; ! 294: init_list(&ifa->prefixes); ! 295: ifa->prune_time = TIME_INFINITY; ! 296: ! 297: add_tail(&p->iface_list, NODE ifa); ! 298: ! 299: ifa->timer = tm_new_init(pool, radv_timer, ifa, 0, 0); ! 300: ! 301: struct object_lock *lock = olock_new(pool); ! 302: lock->type = OBJLOCK_IP; ! 303: lock->port = ICMPV6_PROTO; ! 304: lock->iface = iface; ! 305: lock->data = ifa; ! 306: lock->hook = radv_iface_add; ! 307: ifa->lock = lock; ! 308: ! 309: olock_acquire(lock); ! 310: } ! 311: ! 312: static void ! 313: radv_iface_remove(struct radv_iface *ifa) ! 314: { ! 315: struct radv_proto *p = ifa->ra; ! 316: RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); ! 317: ! 318: rem_node(NODE ifa); ! 319: ! 320: rfree(ifa->pool); ! 321: } ! 322: ! 323: static void ! 324: radv_if_notify(struct proto *P, unsigned flags, struct iface *iface) ! 325: { ! 326: struct radv_proto *p = (struct radv_proto *) P; ! 327: struct radv_config *cf = (struct radv_config *) (P->cf); ! 328: struct radv_iface *ifa = radv_iface_find(p, iface); ! 329: ! 330: if (iface->flags & IF_IGNORE) ! 331: return; ! 332: ! 333: /* Add, remove or restart interface */ ! 334: if (flags & (IF_CHANGE_UPDOWN | IF_CHANGE_LLV6)) ! 335: { ! 336: if (ifa) ! 337: radv_iface_remove(ifa); ! 338: ! 339: if (!(iface->flags & IF_UP)) ! 340: return; ! 341: ! 342: /* Ignore non-multicast ifaces */ ! 343: if (!(iface->flags & IF_MULTICAST)) ! 344: return; ! 345: ! 346: /* Ignore ifaces without link-local address */ ! 347: if (!iface->llv6) ! 348: return; ! 349: ! 350: struct radv_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); ! 351: if (ic) ! 352: radv_iface_new(p, iface, ic); ! 353: ! 354: return; ! 355: } ! 356: ! 357: if (!ifa) ! 358: return; ! 359: ! 360: if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP)) ! 361: radv_iface_notify(ifa, RA_EV_INIT); ! 362: } ! 363: ! 364: static void ! 365: radv_ifa_notify(struct proto *P, unsigned flags UNUSED, struct ifa *a) ! 366: { ! 367: struct radv_proto *p = (struct radv_proto *) P; ! 368: ! 369: if (a->flags & IA_SECONDARY) ! 370: return; ! 371: ! 372: if (a->scope <= SCOPE_LINK) ! 373: return; ! 374: ! 375: struct radv_iface *ifa = radv_iface_find(p, a->iface); ! 376: ! 377: if (ifa) ! 378: radv_iface_notify(ifa, RA_EV_CHANGE); ! 379: } ! 380: ! 381: static inline int ! 382: radv_trigger_valid(struct radv_config *cf) ! 383: { ! 384: return cf->trigger.type != 0; ! 385: } ! 386: ! 387: static inline int ! 388: radv_net_match_trigger(struct radv_config *cf, net *n) ! 389: { ! 390: return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger); ! 391: } ! 392: ! 393: int ! 394: radv_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED) ! 395: { ! 396: // struct radv_proto *p = (struct radv_proto *) P; ! 397: struct radv_config *cf = (struct radv_config *) (P->cf); ! 398: ! 399: if (radv_net_match_trigger(cf, (*new)->net)) ! 400: return RIC_PROCESS; ! 401: ! 402: if (cf->propagate_routes) ! 403: return RIC_PROCESS; ! 404: else ! 405: return RIC_DROP; ! 406: } ! 407: ! 408: static void ! 409: radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED) ! 410: { ! 411: struct radv_proto *p = (struct radv_proto *) P; ! 412: struct radv_config *cf = (struct radv_config *) (P->cf); ! 413: struct radv_route *rt; ! 414: eattr *ea; ! 415: ! 416: if (radv_net_match_trigger(cf, n)) ! 417: { ! 418: u8 old_active = p->active; ! 419: p->active = !!new; ! 420: ! 421: if (p->active == old_active) ! 422: return; ! 423: ! 424: if (p->active) ! 425: RADV_TRACE(D_EVENTS, "Triggered"); ! 426: else ! 427: RADV_TRACE(D_EVENTS, "Suppressed"); ! 428: ! 429: radv_iface_notify_all(p, RA_EV_CHANGE); ! 430: return; ! 431: } ! 432: ! 433: if (!cf->propagate_routes) ! 434: return; ! 435: ! 436: /* ! 437: * Some other route we want to send (or stop sending). Update the cache, ! 438: * with marking a removed one as dead or creating a new one as needed. ! 439: * ! 440: * And yes, we exclude the trigger route on purpose. ! 441: */ ! 442: ! 443: if (new) ! 444: { ! 445: /* Update */ ! 446: ! 447: ea = ea_find(new->attrs->eattrs, EA_RA_PREFERENCE); ! 448: uint preference = ea ? ea->u.data : RA_PREF_MEDIUM; ! 449: uint preference_set = !!ea; ! 450: ! 451: ea = ea_find(new->attrs->eattrs, EA_RA_LIFETIME); ! 452: uint lifetime = ea ? ea->u.data : 0; ! 453: uint lifetime_set = !!ea; ! 454: ! 455: if ((preference != RA_PREF_LOW) && ! 456: (preference != RA_PREF_MEDIUM) && ! 457: (preference != RA_PREF_HIGH)) ! 458: { ! 459: log(L_WARN "%s: Invalid ra_preference value %u on route %N", ! 460: p->p.name, preference, n->n.addr); ! 461: preference = RA_PREF_MEDIUM; ! 462: preference_set = 1; ! 463: lifetime = 0; ! 464: lifetime_set = 1; ! 465: } ! 466: ! 467: rt = fib_get(&p->routes, n->n.addr); ! 468: ! 469: /* Ignore update if nothing changed */ ! 470: if (rt->valid && ! 471: (rt->preference == preference) && ! 472: (rt->preference_set == preference_set) && ! 473: (rt->lifetime == lifetime) && ! 474: (rt->lifetime_set == lifetime_set)) ! 475: return; ! 476: ! 477: if (p->routes.entries == 18) ! 478: log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name); ! 479: ! 480: rt->valid = 1; ! 481: rt->changed = current_time(); ! 482: rt->preference = preference; ! 483: rt->preference_set = preference_set; ! 484: rt->lifetime = lifetime; ! 485: rt->lifetime_set = lifetime_set; ! 486: } ! 487: else ! 488: { ! 489: /* Withdraw */ ! 490: rt = fib_find(&p->routes, n->n.addr); ! 491: ! 492: if (!rt || !rt->valid) ! 493: return; ! 494: ! 495: /* Invalidate the route */ ! 496: rt->valid = 0; ! 497: rt->changed = current_time(); ! 498: ! 499: /* Invalidated route will be pruned eventually */ ! 500: btime expires = rt->changed + cf->max_linger_time S; ! 501: p->prune_time = MIN(p->prune_time, expires); ! 502: } ! 503: ! 504: radv_iface_notify_all(p, RA_EV_CHANGE); ! 505: } ! 506: ! 507: /* ! 508: * Cleans up all the dead routes that expired and schedules itself to be run ! 509: * again if there are more routes waiting for expiration. ! 510: */ ! 511: static void ! 512: radv_prune_routes(struct radv_proto *p) ! 513: { ! 514: struct radv_config *cf = (struct radv_config *) (p->p.cf); ! 515: btime now = current_time(); ! 516: btime next = TIME_INFINITY; ! 517: btime expires = 0; ! 518: ! 519: /* Should not happen */ ! 520: if (!p->fib_up) ! 521: return; ! 522: ! 523: struct fib_iterator fit; ! 524: FIB_ITERATE_INIT(&fit, &p->routes); ! 525: ! 526: again: ! 527: FIB_ITERATE_START(&p->routes, &fit, struct radv_route, rt) ! 528: { ! 529: if (!rt->valid) ! 530: { ! 531: expires = rt->changed + cf->max_linger_time S; ! 532: ! 533: /* Delete expired nodes */ ! 534: if (expires <= now) ! 535: { ! 536: FIB_ITERATE_PUT(&fit); ! 537: fib_delete(&p->routes, rt); ! 538: goto again; ! 539: } ! 540: else ! 541: next = MIN(next, expires); ! 542: } ! 543: } ! 544: FIB_ITERATE_END; ! 545: ! 546: p->prune_time = next; ! 547: } ! 548: ! 549: static int ! 550: radv_check_active(struct radv_proto *p) ! 551: { ! 552: struct radv_config *cf = (struct radv_config *) (p->p.cf); ! 553: ! 554: if (!radv_trigger_valid(cf)) ! 555: return 1; ! 556: ! 557: struct channel *c = p->p.main_channel; ! 558: return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter); ! 559: } ! 560: ! 561: static void ! 562: radv_postconfig(struct proto_config *CF) ! 563: { ! 564: // struct radv_config *cf = (void *) CF; ! 565: ! 566: /* Define default channel */ ! 567: if (EMPTY_LIST(CF->channels)) ! 568: channel_config_new(NULL, net_label[NET_IP6], NET_IP6, CF); ! 569: } ! 570: ! 571: static struct proto * ! 572: radv_init(struct proto_config *CF) ! 573: { ! 574: struct proto *P = proto_new(CF); ! 575: ! 576: P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); ! 577: ! 578: P->preexport = radv_preexport; ! 579: P->rt_notify = radv_rt_notify; ! 580: P->if_notify = radv_if_notify; ! 581: P->ifa_notify = radv_ifa_notify; ! 582: ! 583: return P; ! 584: } ! 585: ! 586: static void ! 587: radv_set_fib(struct radv_proto *p, int up) ! 588: { ! 589: if (up == p->fib_up) ! 590: return; ! 591: ! 592: if (up) ! 593: fib_init(&p->routes, p->p.pool, NET_IP6, sizeof(struct radv_route), ! 594: OFFSETOF(struct radv_route, n), 4, NULL); ! 595: else ! 596: fib_free(&p->routes); ! 597: ! 598: p->fib_up = up; ! 599: p->prune_time = TIME_INFINITY; ! 600: } ! 601: ! 602: static int ! 603: radv_start(struct proto *P) ! 604: { ! 605: struct radv_proto *p = (struct radv_proto *) P; ! 606: struct radv_config *cf = (struct radv_config *) (P->cf); ! 607: ! 608: init_list(&(p->iface_list)); ! 609: p->valid = 1; ! 610: p->active = !radv_trigger_valid(cf); ! 611: ! 612: p->fib_up = 0; ! 613: radv_set_fib(p, cf->propagate_routes); ! 614: p->prune_time = TIME_INFINITY; ! 615: ! 616: return PS_UP; ! 617: } ! 618: ! 619: static inline void ! 620: radv_iface_shutdown(struct radv_iface *ifa) ! 621: { ! 622: if (ifa->sk) ! 623: { ! 624: radv_invalidate(ifa); ! 625: radv_send_ra(ifa, IPA_NONE); ! 626: } ! 627: } ! 628: ! 629: static int ! 630: radv_shutdown(struct proto *P) ! 631: { ! 632: struct radv_proto *p = (struct radv_proto *) P; ! 633: ! 634: p->valid = 0; ! 635: ! 636: struct radv_iface *ifa; ! 637: WALK_LIST(ifa, p->iface_list) ! 638: radv_iface_shutdown(ifa); ! 639: ! 640: return PS_DOWN; ! 641: } ! 642: ! 643: static int ! 644: radv_reconfigure(struct proto *P, struct proto_config *CF) ! 645: { ! 646: struct radv_proto *p = (struct radv_proto *) P; ! 647: struct radv_config *old = (struct radv_config *) (P->cf); ! 648: struct radv_config *new = (struct radv_config *) CF; ! 649: ! 650: if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) ! 651: return 0; ! 652: ! 653: P->cf = CF; /* radv_check_active() requires proper P->cf */ ! 654: p->active = radv_check_active(p); ! 655: ! 656: /* Allocate or free FIB */ ! 657: radv_set_fib(p, new->propagate_routes); ! 658: ! 659: /* We started to accept routes so we need to refeed them */ ! 660: if (!old->propagate_routes && new->propagate_routes) ! 661: channel_request_feeding(p->p.main_channel); ! 662: ! 663: struct iface *iface; ! 664: WALK_LIST(iface, iface_list) ! 665: { ! 666: if (!(iface->flags & IF_UP)) ! 667: continue; ! 668: ! 669: /* Ignore non-multicast ifaces */ ! 670: if (!(iface->flags & IF_MULTICAST)) ! 671: continue; ! 672: ! 673: /* Ignore ifaces without link-local address */ ! 674: if (!iface->llv6) ! 675: continue; ! 676: ! 677: struct radv_iface *ifa = radv_iface_find(p, iface); ! 678: struct radv_iface_config *ic = (struct radv_iface_config *) ! 679: iface_patt_find(&new->patt_list, iface, NULL); ! 680: ! 681: if (ifa && ic) ! 682: { ! 683: ifa->cf = ic; ! 684: ! 685: /* We cheat here - always notify the change even if there isn't ! 686: any. That would leads just to a few unnecessary RAs. */ ! 687: radv_iface_notify(ifa, RA_EV_CHANGE); ! 688: } ! 689: ! 690: if (ifa && !ic) ! 691: { ! 692: radv_iface_shutdown(ifa); ! 693: radv_iface_remove(ifa); ! 694: } ! 695: ! 696: if (!ifa && ic) ! 697: radv_iface_new(p, iface, ic); ! 698: } ! 699: ! 700: return 1; ! 701: } ! 702: ! 703: static void ! 704: radv_copy_config(struct proto_config *dest, struct proto_config *src) ! 705: { ! 706: struct radv_config *d = (struct radv_config *) dest; ! 707: struct radv_config *s = (struct radv_config *) src; ! 708: ! 709: /* We clean up patt_list, ifaces are non-sharable */ ! 710: init_list(&d->patt_list); ! 711: ! 712: /* We copy pref_list, shallow copy suffices */ ! 713: cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config)); ! 714: } ! 715: ! 716: static void ! 717: radv_get_status(struct proto *P, byte *buf) ! 718: { ! 719: struct radv_proto *p = (struct radv_proto *) P; ! 720: ! 721: if (!p->active) ! 722: strcpy(buf, "Suppressed"); ! 723: } ! 724: ! 725: static const char * ! 726: radv_pref_str(u32 pref) ! 727: { ! 728: switch (pref) ! 729: { ! 730: case RA_PREF_LOW: ! 731: return "low"; ! 732: case RA_PREF_MEDIUM: ! 733: return "medium"; ! 734: case RA_PREF_HIGH: ! 735: return "high"; ! 736: default: ! 737: return "??"; ! 738: } ! 739: } ! 740: ! 741: /* The buffer has some minimal size */ ! 742: static int ! 743: radv_get_attr(eattr *a, byte *buf, int buflen UNUSED) ! 744: { ! 745: switch (a->id) ! 746: { ! 747: case EA_RA_PREFERENCE: ! 748: bsprintf(buf, "preference: %s", radv_pref_str(a->u.data)); ! 749: return GA_FULL; ! 750: case EA_RA_LIFETIME: ! 751: bsprintf(buf, "lifetime"); ! 752: return GA_NAME; ! 753: default: ! 754: return GA_UNKNOWN; ! 755: } ! 756: } ! 757: ! 758: struct protocol proto_radv = { ! 759: .name = "RAdv", ! 760: .template = "radv%d", ! 761: .class = PROTOCOL_RADV, ! 762: .channel_mask = NB_IP6, ! 763: .proto_size = sizeof(struct radv_proto), ! 764: .config_size = sizeof(struct radv_config), ! 765: .postconfig = radv_postconfig, ! 766: .init = radv_init, ! 767: .start = radv_start, ! 768: .shutdown = radv_shutdown, ! 769: .reconfigure = radv_reconfigure, ! 770: .copy_config = radv_copy_config, ! 771: .get_status = radv_get_status, ! 772: .get_attr = radv_get_attr ! 773: };