Annotation of embedaddon/bird2/proto/radv/radv.c, revision 1.1
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: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>