From: florian Date: Tue, 11 Jul 2017 12:51:05 +0000 (+0000) Subject: Purging is at last at hand. Day of Doom is here. All that is evil X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=4a2f474d14c160dc7829cce0149ead09d473ece9;p=openbsd Purging is at last at hand. Day of Doom is here. All that is evil shall all be cleansed. Remove sending of router solicitations and processing of router advertisements from the kernel. It's handled by slaacd(8) these days. Input & OK bluhm@, mpi@ --- diff --git a/sys/net/if.c b/sys/net/if.c index 6354484e477..7477fa5606a 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if.c,v 1.504 2017/06/23 11:18:12 bluhm Exp $ */ +/* $OpenBSD: if.c,v 1.505 2017/07/11 12:51:05 florian Exp $ */ /* $NetBSD: if.c,v 1.35 1996/05/07 05:26:04 thorpej Exp $ */ /* @@ -1952,16 +1952,6 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct proc *p) if (error != 0) return (error); } - - if ((ifr->ifr_flags & IFXF_AUTOCONF6) && - !(ifp->if_xflags & IFXF_AUTOCONF6)) { - nd6_rs_attach(ifp); - } - - if ((ifp->if_xflags & IFXF_AUTOCONF6) && - !(ifr->ifr_flags & IFXF_AUTOCONF6)) { - nd6_rs_detach(ifp); - } #endif /* INET6 */ #ifdef MPLS diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 8b619f6a78e..45a28663d65 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6.c,v 1.206 2017/05/29 20:28:57 florian Exp $ */ +/* $OpenBSD: in6.c,v 1.207 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: in6.c,v 1.372 2004/06/14 08:14:21 itojun Exp $ */ /* @@ -902,18 +902,10 @@ in6_unlink_ifa(struct in6_ifaddr *ia6, struct ifnet *ifp) NET_ASSERT_LOCKED(); /* Release the reference to the base prefix. */ - if (ia6->ia6_ndpr == NULL) { - plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); - if ((ifp->if_flags & IFF_LOOPBACK) == 0 && plen != 128) { - rt_ifa_del(ifa, RTF_CLONING | RTF_CONNECTED, - ifa->ifa_addr); - } - } else { - KASSERT(ia6->ia6_flags & IN6_IFF_AUTOCONF); - ia6->ia6_flags &= ~IN6_IFF_AUTOCONF; - if (--ia6->ia6_ndpr->ndpr_refcnt == 0) - prelist_remove(ia6->ia6_ndpr); - ia6->ia6_ndpr = NULL; + plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); + if ((ifp->if_flags & IFF_LOOPBACK) == 0 && plen != 128) { + rt_ifa_del(ifa, RTF_CLONING | RTF_CONNECTED, + ifa->ifa_addr); } rt_ifa_purge(ifa); diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index fad1eb130b6..89acde9c6a4 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_ifattach.c,v 1.102 2017/05/16 12:24:02 mpi Exp $ */ +/* $OpenBSD: in6_ifattach.c,v 1.103 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: in6_ifattach.c,v 1.124 2001/07/18 08:32:51 jinmei Exp $ */ /* @@ -504,9 +504,6 @@ in6_ifattach(struct ifnet *ifp) } } - if (ifp->if_xflags & IFXF_AUTOCONF6) - nd6_rs_attach(ifp); - return (0); } @@ -563,8 +560,6 @@ in6_ifdetach(struct ifnet *ifp) rtfree(rt); } - if (ifp->if_xflags & IFXF_AUTOCONF6) { - nd6_rs_detach(ifp); + if (ifp->if_xflags & IFXF_AUTOCONF6) ifp->if_xflags &= ~IFXF_AUTOCONF6; - } } diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index a193e2df396..3b2e0d1f684 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_var.h,v 1.66 2017/03/06 08:59:07 mpi Exp $ */ +/* $OpenBSD: in6_var.h,v 1.67 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: in6_var.h,v 1.55 2001/02/16 12:49:45 itojun Exp $ */ /* @@ -112,9 +112,6 @@ struct in6_ifaddr { */ time_t ia6_updatetime; - /* back pointer to the prefix (for autoconf) */ - struct nd_prefix *ia6_ndpr; - /* multicast addresses joined from the kernel */ LIST_HEAD(, in6_multi_mship) ia6_memberships; }; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 7b32caec770..1a0cffea012 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6.c,v 1.209 2017/05/16 12:24:02 mpi Exp $ */ +/* $OpenBSD: nd6.c,v 1.210 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */ /* @@ -87,9 +87,6 @@ TAILQ_HEAD(llinfo_nd6_head, llinfo_nd6) nd6_list; struct pool nd6_pool; /* pool for llinfo_nd6 structures */ int nd6_inuse, nd6_allocated; -struct nd_drhead nd_defrouter; -struct nd_prhead nd_prefix = { 0 }; - int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; void nd6_slowtimo(void *); @@ -103,9 +100,6 @@ struct timeout nd6_slowtimo_ch; struct timeout nd6_timer_ch; struct task nd6_timer_task; -int fill_drlist(void *, size_t *, size_t); -int fill_prlist(void *, size_t *, size_t); - void nd6_init(void) { @@ -121,7 +115,6 @@ nd6_init(void) IPL_SOFTNET, 0, "nd6", NULL); /* initialization of the default router list */ - TAILQ_INIT(&nd_defrouter); task_set(&nd6_timer_task, nd6_timer_work, NULL); @@ -133,7 +126,6 @@ nd6_init(void) timeout_set(&nd6_timer_ch, nd6_timer, NULL); timeout_add_sec(&nd6_timer_ch, nd6_prune); - nd6_rs_init(); } struct nd_ifinfo * @@ -433,8 +425,6 @@ nd6_llinfo_timer(void *arg) void nd6_timer_work(void *null) { - struct nd_defrouter *dr, *ndr; - struct nd_prefix *pr, *npr; struct ifnet *ifp; int s; @@ -442,11 +432,6 @@ nd6_timer_work(void *null) timeout_add_sec(&nd6_timer_ch, nd6_prune); - /* expire default router list */ - TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) - if (dr->expire && dr->expire < time_uptime) - defrtrlist_del(dr); - /* * expire interface addresses. * in the past the loop was inside prefix expiry processing. @@ -476,23 +461,6 @@ nd6_timer_work(void *null) } } - /* expire prefix list */ - LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { - /* - * check prefix lifetime. - * since pltime is just for autoconf, pltime processing for - * prefix is not necessary. - */ - if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME && - time_uptime - pr->ndpr_lastupdate > pr->ndpr_vltime) { - /* - * address expiration and prefix expiration are - * separate. NEVER perform in6_purgeaddr here. - */ - - prelist_remove(pr); - } - } NET_UNLOCK(s); } @@ -510,43 +478,9 @@ void nd6_purge(struct ifnet *ifp) { struct llinfo_nd6 *ln, *nln; - struct nd_defrouter *dr, *ndr; - struct nd_prefix *pr, *npr; NET_ASSERT_LOCKED(); - /* - * Nuke default router list entries toward ifp. - * We defer removal of default router list entries that is installed - * in the routing table, in order to keep additional side effects as - * small as possible. - */ - TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { - if (dr->installed) - continue; - - if (dr->ifp == ifp) - defrtrlist_del(dr); - } - TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) { - if (!dr->installed) - continue; - - if (dr->ifp == ifp) - defrtrlist_del(dr); - } - - /* Nuke prefix list entries toward ifp */ - LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { - if (pr->ndpr_ifp == ifp) - prelist_remove(pr); - } - - if (ifp->if_xflags & IFXF_AUTOCONF6) { - /* refresh default router list */ - defrouter_select(); - } - /* * Nuke neighbor cache entries for the ifp. */ @@ -661,7 +595,6 @@ nd6_lookup(struct in6_addr *addr6, int create, struct ifnet *ifp, int nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) { - struct nd_prefix *pr; struct in6_ifaddr *ia6; struct ifaddr *ifa; struct rtentry *rt; @@ -692,22 +625,6 @@ nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) return (1); } - /* - * If the address matches one of our on-link prefixes, it should be a - * neighbor. - */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (pr->ndpr_ifp != ifp) - continue; - - if (!(pr->ndpr_stateflags & NDPRF_ONLINK)) - continue; - - if (IN6_ARE_MASKED_ADDR_EQUAL(&pr->ndpr_prefix.sin6_addr, - &addr->sin6_addr, &pr->ndpr_mask)) - return (1); - } - /* * Even if the address matches none of our addresses, it might be * in the neighbor cache. @@ -743,7 +660,6 @@ nd6_free(struct rtentry *rt, int gc) { struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct in6_addr in6 = satosin6(rt_key(rt))->sin6_addr; - struct nd_defrouter *dr; struct ifnet *ifp; NET_ASSERT_LOCKED(); @@ -751,33 +667,7 @@ nd6_free(struct rtentry *rt, int gc) ifp = if_get(rt->rt_ifidx); if (!ip6_forwarding) { - dr = defrouter_lookup(&satosin6(rt_key(rt))->sin6_addr, - rt->rt_ifidx); - - if (dr != NULL && dr->expire && - ln->ln_state == ND6_LLINFO_STALE && gc) { - /* - * If the reason for the deletion is just garbage - * collection, and the neighbor is an active default - * router, do not delete it. Instead, reset the GC - * timer using the router's lifetime. - * Simply deleting the entry would affect default - * router selection, which is not necessarily a good - * thing, especially when we're using router preference - * values. - * XXX: the check for ln_state would be redundant, - * but we intentionally keep it just in case. - */ - if (dr->expire > time_uptime) { - nd6_llinfo_settimer(ln, - dr->expire - time_uptime); - } else - nd6_llinfo_settimer(ln, nd6_gctimer); - if_put(ifp); - return (TAILQ_NEXT(ln, ln_list)); - } - - if (ln->ln_router || dr) { + if (ln->ln_router) { /* * rt6_flush must be called whether or not the neighbor * is in the Default Router List. @@ -785,43 +675,11 @@ nd6_free(struct rtentry *rt, int gc) */ rt6_flush(&in6, ifp); } - - if (dr) { - /* - * Unreachability of a router might affect the default - * router selection and on-link detection of advertised - * prefixes. - */ - - /* - * Temporarily fake the state to choose a new default - * router and to perform on-link determination of - * prefixes correctly. - * Below the state will be set correctly, - * or the entry itself will be deleted. - */ - ln->ln_state = ND6_LLINFO_INCOMPLETE; - - /* - * Since defrouter_select() does not affect the - * on-link determination and MIP6 needs the check - * before the default router selection, we perform - * the check now. - */ - pfxlist_onlink_check(); - - /* - * refresh default router list - */ - defrouter_select(); - } } /* * Before deleting the entry, remember the next entry as the - * return value. We need this because pfxlist_onlink_check() above - * might have freed other entries (particularly the old next entry) as - * a side effect (XXX). + * return value. */ next = TAILQ_NEXT(ln, ln_list); @@ -888,16 +746,6 @@ nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) struct sockaddr *gate = rt->rt_gateway; struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo; struct ifaddr *ifa; - struct nd_defrouter *dr; - - if (req == RTM_DELETE && (rt->rt_flags & RTF_GATEWAY) && - (IN6_ARE_ADDR_EQUAL(&(satosin6(rt_key(rt)))->sin6_addr, - &in6addr_any) && rt_plen(rt) == 0)) { - dr = defrouter_lookup(&satosin6(gate)->sin6_addr, - ifp->if_index); - if (dr) - dr->installed = 0; - } if (ISSET(rt->rt_flags, RTF_GATEWAY|RTF_MULTICAST)) return; @@ -1144,63 +992,18 @@ nd6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp) break; case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ /* sync kernel routing table with the default router list */ - defrouter_reset(); - defrouter_select(); + error = ENOTSUP; break; case SIOCSPFXFLUSH_IN6: { /* flush all the prefix advertised by routers */ - struct nd_prefix *pr, *npr; - - /* First purge the addresses referenced by a prefix. */ - LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { - struct ifnet *ifp; - struct ifaddr *ifa, *nifa; - struct in6_ifaddr *ia6; - - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) - continue; /* XXX */ - - /* do we really have to remove addresses as well? */ - TAILQ_FOREACH(ifp, &ifnet, if_list) { - TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, - ifa_list, nifa) { - if (ifa->ifa_addr->sa_family != - AF_INET6) - continue; - - ia6 = ifatoia6(ifa); - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) - == 0) - continue; - - if (ia6->ia6_ndpr == pr) - in6_purgeaddr(&ia6->ia_ifa); - } - } - } - /* - * Purging the addresses might remove the prefix as well. - * So run the loop again to access only prefixes that have - * not been freed already. - */ - LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, npr) { - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) - continue; /* XXX */ - - prelist_remove(pr); - } + error = ENOTSUP; break; } case SIOCSRTRFLUSH_IN6: { /* flush all the default routers */ - struct nd_defrouter *dr, *ndr; - - defrouter_reset(); - TAILQ_FOREACH_SAFE(dr, &nd_defrouter, dr_entry, ndr) - defrtrlist_del(dr); - defrouter_select(); + error = ENOTSUP; break; } case SIOCGNBRINFO_IN6: @@ -1471,21 +1274,6 @@ fail: break; } - /* - * When the link-layer address of a router changes, select the - * best router again. In particular, when the neighbor entry is newly - * created, it might affect the selection policy. - * Question: can we restrict the first condition to the "is_newentry" - * case? - * XXX: when we hear an RA from a new router with the link-layer - * address option, defrouter_select() is called twice, since - * defrtrlist_update called the function as well. However, I believe - * we can compromise the overhead, since it only happens the first - * time. - */ - if (do_update && ln->ln_router && (ifp->if_xflags & IFXF_AUTOCONF6)) - defrouter_select(); - rtfree(rt); } @@ -1647,7 +1435,7 @@ nd6_need_cache(struct ifnet *ifp) * oldp - syscall arg, need copyout * newp - syscall arg, need copyin */ - +/* XXXDEL? */ int nd6_sysctl(int name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { @@ -1673,15 +1461,11 @@ nd6_sysctl(int name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) p = NULL; switch (name) { case ICMPV6CTL_ND6_DRLIST: - error = fill_drlist(p, oldlenp, ol); - if (!error && p && oldp) - error = copyout(p, oldp, *oldlenp); + error = ENOTSUP; /* XXXDEL? can we delete more? */ break; case ICMPV6CTL_ND6_PRLIST: - error = fill_prlist(p, oldlenp, ol); - if (!error && p && oldp) - error = copyout(p, oldp, *oldlenp); + error = ENOTSUP; /* XXXDEL? can we delete more? */ break; default: @@ -1692,142 +1476,3 @@ nd6_sysctl(int name, void *oldp, size_t *oldlenp, void *newp, size_t newlen) return (error); } - -int -fill_drlist(void *oldp, size_t *oldlenp, size_t ol) -{ - int error = 0; - struct in6_defrouter *d = NULL, *de = NULL; - struct nd_defrouter *dr; - time_t expire; - size_t l; - - if (oldp) { - d = (struct in6_defrouter *)oldp; - de = (struct in6_defrouter *)((caddr_t)oldp + *oldlenp); - } - l = 0; - - TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { - if (oldp && d + 1 <= de) { - bzero(d, sizeof(*d)); - d->rtaddr.sin6_family = AF_INET6; - d->rtaddr.sin6_len = sizeof(struct sockaddr_in6); - in6_recoverscope(&d->rtaddr, &dr->rtaddr); - d->flags = dr->flags; - d->rtlifetime = dr->rtlifetime; - expire = dr->expire; - if (expire != 0) { - expire -= time_uptime; - expire += time_second; - } - d->expire = expire; - d->if_index = dr->ifp->if_index; - } - - l += sizeof(*d); - if (d) - d++; - } - - if (oldp) { - *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ - if (l > ol) - error = ENOMEM; - } else - *oldlenp = l; - - return (error); -} - -int -fill_prlist(void *oldp, size_t *oldlenp, size_t ol) -{ - int error = 0; - struct nd_prefix *pr; - char *p = NULL, *ps = NULL; - char *pe = NULL; - size_t l; - - if (oldp) { - ps = p = (char *)oldp; - pe = (char *)oldp + *oldlenp; - } - l = 0; - - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - u_short advrtrs; - struct sockaddr_in6 sin6; - struct nd_pfxrouter *pfr; - struct in6_prefix pfx; - - if (oldp && p + sizeof(struct in6_prefix) <= pe) { - memset(&pfx, 0, sizeof(pfx)); - ps = p; - - pfx.prefix = pr->ndpr_prefix; - in6_recoverscope(&pfx.prefix, - &pfx.prefix.sin6_addr); - pfx.raflags = pr->ndpr_raf; - pfx.prefixlen = pr->ndpr_plen; - pfx.vltime = pr->ndpr_vltime; - pfx.pltime = pr->ndpr_pltime; - pfx.if_index = pr->ndpr_ifp->if_index; - if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME) - pfx.expire = 0; - else { - time_t maxexpire; - - /* XXX: we assume time_t is signed. */ - maxexpire = (time_t)~(1ULL << - ((sizeof(maxexpire) * 8) - 1)); - if (pr->ndpr_vltime < - maxexpire - pr->ndpr_lastupdate) { - pfx.expire = pr->ndpr_lastupdate + - pr->ndpr_vltime; - } else - pfx.expire = maxexpire; - } - pfx.refcnt = pr->ndpr_refcnt; - pfx.flags = pr->ndpr_stateflags; - pfx.origin = PR_ORIG_RA; - - p += sizeof(pfx); l += sizeof(pfx); - - advrtrs = 0; - LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { - if (p + sizeof(sin6) > pe) { - advrtrs++; - continue; - } - bzero(&sin6, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_len = sizeof(struct sockaddr_in6); - in6_recoverscope(&sin6, &pfr->router->rtaddr); - advrtrs++; - memcpy(p, &sin6, sizeof(sin6)); - p += sizeof(sin6); - l += sizeof(sin6); - } - pfx.advrtrs = advrtrs; - memcpy(ps, &pfx, sizeof(pfx)); - } - else { - l += sizeof(pfx); - advrtrs = 0; - LIST_FOREACH(pfr, &pr->ndpr_advrtrs, pfr_entry) { - advrtrs++; - l += sizeof(sin6); - } - } - } - - if (oldp) { - *oldlenp = l; /* (caddr_t)d - (caddr_t)oldp */ - if (l > ol) - error = ENOMEM; - } else - *oldlenp = l; - - return (error); -} diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 4274cd4dd07..660c681e333 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6.h,v 1.66 2016/12/27 18:45:01 bluhm Exp $ */ +/* $OpenBSD: nd6.h,v 1.67 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $ */ /* @@ -165,60 +165,12 @@ struct llinfo_nd6 { (((MIN_RANDOM_FACTOR * (x >> 10)) + (arc4random() & \ ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) -TAILQ_HEAD(nd_drhead, nd_defrouter); -struct nd_defrouter { - TAILQ_ENTRY(nd_defrouter) dr_entry; - struct in6_addr rtaddr; - struct ifnet *ifp; - time_t expire; - int installed; /* is installed into kernel routing table */ - u_short rtlifetime; - u_char flags; /* flags on RA message */ -}; - -struct nd_prefix { - struct ifnet *ndpr_ifp; - LIST_ENTRY(nd_prefix) ndpr_entry; - struct sockaddr_in6 ndpr_prefix; /* prefix */ - struct in6_addr ndpr_mask; /* netmask derived from the prefix */ - - struct task ndpr_task; - - time_t ndpr_expire; /* expiration time of the prefix */ - time_t ndpr_preferred; /* preferred time of the prefix */ - time_t ndpr_lastupdate; /* reception time of last advertisement */ - - u_int32_t ndpr_vltime; /* advertised valid lifetime */ - u_int32_t ndpr_pltime; /* advertised preferred lifetime */ - - struct prf_ra ndpr_flags; - u_int32_t ndpr_stateflags; /* actual state flags */ - /* list of routers that advertise the prefix: */ - LIST_HEAD(pr_rtrhead, nd_pfxrouter) ndpr_advrtrs; - u_char ndpr_plen; - int ndpr_refcnt; /* reference counter from addresses */ -}; - -#define ndpr_raf ndpr_flags -#define ndpr_raf_onlink ndpr_flags.onlink -#define ndpr_raf_auto ndpr_flags.autonomous -#define ndpr_raf_router ndpr_flags.router - -struct nd_pfxrouter { - LIST_ENTRY(nd_pfxrouter) pfr_entry; - struct nd_defrouter *router; -}; - -LIST_HEAD(nd_prhead, nd_prefix); - extern int nd6_prune; extern int nd6_delay; extern int nd6_umaxtries; extern int nd6_mmaxtries; extern int nd6_maxnudhint; extern int nd6_gctimer; -extern struct nd_drhead nd_defrouter; -extern struct nd_prhead nd_prefix; extern int nd6_debug; #define nd6log(x) do { if (nd6_debug) log x; } while (0) @@ -279,22 +231,9 @@ void nd6_dad_start(struct ifaddr *); void nd6_dad_stop(struct ifaddr *); void nd6_ra_input(struct mbuf *, int, int); -void nd6_rs_init(void); -void nd6_rs_attach(struct ifnet *); -void nd6_rs_detach(struct ifnet *); void nd6_rs_input(struct mbuf *, int, int); -void prelist_del(struct nd_prefix *); -void defrouter_reset(void); -void defrouter_select(void); -void defrtrlist_del(struct nd_defrouter *); -void prelist_remove(struct nd_prefix *); -void pfxlist_onlink_check(void); -struct nd_defrouter *defrouter_lookup(struct in6_addr *, unsigned int); - -struct nd_prefix *nd6_prefix_lookup(struct nd_prefix *); int in6_ifdel(struct ifnet *, struct in6_addr *); -int in6_init_prefix_ltimes(struct nd_prefix *ndpr); void rt6_flush(struct in6_addr *, struct ifnet *); #endif /* _KERNEL */ diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index b9c985a31b9..9c6fa4bf163 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_nbr.c,v 1.118 2017/07/05 09:51:37 florian Exp $ */ +/* $OpenBSD: nd6_nbr.c,v 1.119 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */ /* @@ -737,7 +737,6 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) * non-reachable to probably reachable, and might * affect the status of associated prefixes.. */ - pfxlist_onlink_check(); if ((rt->rt_flags & RTF_LLINFO) == 0) goto freeit; /* ln is gone */ } @@ -827,29 +826,9 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) } if (ln->ln_router && !is_router) { - /* - * The peer dropped the router flag. - * Remove the sender from the Default Router List and - * update the Destination Cache entries. - */ - struct nd_defrouter *dr; - struct in6_addr *in6; - - in6 = &satosin6(rt_key(rt))->sin6_addr; - - /* - * Lock to protect the default router list. - * XXX: this might be unnecessary, since this function - * is only called under the network software interrupt - * context. However, we keep it just for safety. - */ - dr = defrouter_lookup(in6, rt->rt_ifidx); - if (dr) - defrtrlist_del(dr); - else if (!ip6_forwarding) { + if (!ip6_forwarding) { /* - * Even if the neighbor is not in the default - * router list, the neighbor may be used + * The neighbor may be used * as a next hop for some destinations * (e.g. redirect case). So we must * call rt6_flush explicitly. diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 4879c9643dd..886436ae863 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nd6_rtr.c,v 1.162 2017/07/08 15:45:11 florian Exp $ */ +/* $OpenBSD: nd6_rtr.c,v 1.163 2017/07/11 12:51:05 florian Exp $ */ /* $KAME: nd6_rtr.c,v 1.97 2001/02/07 11:09:13 itojun Exp $ */ /* @@ -57,47 +57,10 @@ #include #include -int rtpref(struct nd_defrouter *); -struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); -struct in6_ifaddr *in6_ifadd(struct nd_prefix *, int); -struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, struct nd_defrouter *); -void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); -void pfxrtr_del(struct nd_pfxrouter *); -struct nd_pfxrouter *find_pfxlist_reachable_router(struct nd_prefix *); -void defrouter_delreq(struct nd_defrouter *); -void purge_detached(struct ifnet *); -int nd6_prefix_onlink(struct nd_prefix *); -int nd6_prefix_offlink(struct nd_prefix *); -void in6_init_address_ltimes(struct nd_prefix *, struct in6_addrlifetime *); -int prelist_update(struct nd_prefix *, struct nd_defrouter *, struct mbuf *); -int nd6_prelist_add(struct nd_prefix *, struct nd_defrouter *, - struct nd_prefix **); -void defrouter_addreq(struct nd_defrouter *); int rt6_deleteroute(struct rtentry *, void *, unsigned int); -void nd6_addr_add(void *); - -void nd6_rs_output_timo(void *); -void nd6_rs_output_set_timo(int); -void nd6_rs_output(struct ifnet *, struct in6_ifaddr *); -void nd6_rs_dev_state(void *); - extern int nd6_recalc_reachtm_interval; -#define ND6_RS_OUTPUT_INTERVAL 60 -#define ND6_RS_OUTPUT_QUICK_INTERVAL 1 - -struct timeout nd6_rs_output_timer; -int nd6_rs_output_timeout = ND6_RS_OUTPUT_INTERVAL; -int nd6_rs_timeout_count = 0; - -void -nd6_rs_init(void) -{ - timeout_set_proc(&nd6_rs_output_timer, nd6_rs_output_timo, NULL); -} - - /* * Receive Router Solicitation Message - just for routers. * Router solicitation/advertisement is mostly managed by userland program @@ -194,173 +157,6 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len) m_freem(m); } -void -nd6_rs_output(struct ifnet* ifp, struct in6_ifaddr *ia6) -{ - struct mbuf *m; - struct ip6_hdr *ip6; - struct nd_router_solicit *rs; - struct ip6_moptions im6o; - caddr_t mac; - int icmp6len, maxlen; - - NET_ASSERT_LOCKED(); - - KASSERT(ia6 != NULL); - KASSERT(ifp->if_flags & IFF_RUNNING); - KASSERT(ifp->if_xflags & IFXF_AUTOCONF6); - KASSERT(!(ia6->ia6_flags & IN6_IFF_TENTATIVE)); - - maxlen = sizeof(*ip6) + sizeof(*rs); - maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m && max_linkhdr + maxlen >= MHLEN) { - MCLGET(m, M_DONTWAIT); - if ((m->m_flags & M_EXT) == 0) { - m_free(m); - m = NULL; - } - } - if (m == NULL) - return; - - m->m_pkthdr.ph_ifidx = 0; - m->m_pkthdr.ph_rtableid = ifp->if_rdomain; - m->m_flags |= M_MCAST; - m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; - - im6o.im6o_ifidx = ifp->if_index; - im6o.im6o_hlim = 255; - im6o.im6o_loop = 0; - - icmp6len = sizeof(*rs); - m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; - m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ - - /* fill neighbor solicitation packet */ - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = 0; - ip6->ip6_vfc &= ~IPV6_VERSION_MASK; - ip6->ip6_vfc |= IPV6_VERSION; - /* ip6->ip6_plen will be set later */ - ip6->ip6_nxt = IPPROTO_ICMPV6; - ip6->ip6_hlim = 255; - - ip6->ip6_dst = in6addr_linklocal_allrouters; - - ip6->ip6_src = ia6->ia_addr.sin6_addr; - - rs = (struct nd_router_solicit *)(ip6 + 1); - rs->nd_rs_type = ND_ROUTER_SOLICIT; - rs->nd_rs_code = 0; - rs->nd_rs_cksum = 0; - rs->nd_rs_reserved = 0; - - if ((mac = nd6_ifptomac(ifp))) { - int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; - struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(rs + 1); - /* 8 byte alignments... */ - optlen = (optlen + 7) & ~7; - - m->m_pkthdr.len += optlen; - m->m_len += optlen; - icmp6len += optlen; - bzero((caddr_t)nd_opt, optlen); - nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; - nd_opt->nd_opt_len = optlen >> 3; - bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); - } - - ip6->ip6_plen = htons((u_short)icmp6len); - - ip6_output(m, NULL, NULL, 0, &im6o, NULL); - - icmp6stat_inc(icp6s_outhist + ND_ROUTER_SOLICIT); -} - -void -nd6_rs_output_set_timo(int timeout) -{ - return; - nd6_rs_output_timeout = timeout; - timeout_add_sec(&nd6_rs_output_timer, nd6_rs_output_timeout); -} - -void -nd6_rs_output_timo(void *ignored_arg) -{ - struct ifnet *ifp; - struct in6_ifaddr *ia6; - int s; - - if (nd6_rs_timeout_count == 0) - return; - - if (nd6_rs_output_timeout < ND6_RS_OUTPUT_INTERVAL) - /* exponential backoff if running quick timeouts */ - nd6_rs_output_timeout *= 2; - if (nd6_rs_output_timeout > ND6_RS_OUTPUT_INTERVAL) - nd6_rs_output_timeout = ND6_RS_OUTPUT_INTERVAL; - - NET_LOCK(s); - TAILQ_FOREACH(ifp, &ifnet, if_list) { - if (ISSET(ifp->if_flags, IFF_RUNNING) && - ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) { - ia6 = in6ifa_ifpforlinklocal(ifp, IN6_IFF_TENTATIVE); - if (ia6 != NULL) - nd6_rs_output(ifp, ia6); - } - } - NET_UNLOCK(s); - nd6_rs_output_set_timo(nd6_rs_output_timeout); -} - -void -nd6_rs_attach(struct ifnet *ifp) -{ - if (!ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) { - /* - * We are being called from net/if.c, autoconf is not yet - * enabled on the interface. - */ - nd6_rs_timeout_count++; - RS_LHCOOKIE(ifp) = hook_establish(ifp->if_linkstatehooks, 1, - nd6_rs_dev_state, ifp); - } - - /* - * (re)send solicitation regardless if we are enabling autoconf - * for the first time or if the link comes up - */ - nd6_rs_output_set_timo(ND6_RS_OUTPUT_QUICK_INTERVAL); -} - -void -nd6_rs_detach(struct ifnet *ifp) -{ - if (ISSET(ifp->if_xflags, IFXF_AUTOCONF6)) { - nd6_rs_timeout_count--; - hook_disestablish(ifp->if_linkstatehooks, RS_LHCOOKIE(ifp)); - } - - if (nd6_rs_timeout_count == 0) - timeout_del(&nd6_rs_output_timer); -} - -void -nd6_rs_dev_state(void *arg) -{ - struct ifnet *ifp; - - ifp = (struct ifnet *) arg; - - if (LINK_STATE_IS_UP(ifp->if_link_state) && - ifp->if_flags & IFF_RUNNING) - /* start quick timer, will exponentially back off */ - nd6_rs_output_set_timo(ND6_RS_OUTPUT_QUICK_INTERVAL); -} - /* * Receive Router Advertisement Message. * @@ -370,39 +166,22 @@ void nd6_ra_input(struct mbuf *m, int off, int icmp6len) { struct ifnet *ifp; - struct nd_ifinfo *ndi; struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); struct nd_router_advert *nd_ra; struct in6_addr saddr6 = ip6->ip6_src; + char *lladdr = NULL; + int lladdrlen = 0; union nd_opts ndopts; - struct nd_defrouter *dr; char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; - ifp = if_get(m->m_pkthdr.ph_ifidx); - if (ifp == NULL) - goto freeit; - - goto freeit; - - /* We accept RAs only if inet6 autoconf is enabled */ - if (!(ifp->if_xflags & IFXF_AUTOCONF6)) - goto freeit; - - ndi = ND_IFINFO(ifp); - if (!(ndi->flags & ND6_IFF_ACCEPT_RTADV)) - goto freeit; - - if (nd6_rs_output_timeout != ND6_RS_OUTPUT_INTERVAL) - /* we saw a RA, stop quick timer */ - nd6_rs_output_set_timo(ND6_RS_OUTPUT_INTERVAL); - + /* Sanity checks */ if (ip6->ip6_hlim != 255) { nd6log((LOG_ERR, - "nd6_ra_input: invalid hlim (%d) from %s to %s on %s\n", + "nd6_ra_input: invalid hlim (%d) from %s to %s on %u\n", ip6->ip6_hlim, inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)), inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)), - ifp->if_xname)); + m->m_pkthdr.ph_ifidx)); goto bad; } @@ -416,7 +195,6 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) IP6_EXTHDR_GET(nd_ra, struct nd_router_advert *, m, off, icmp6len); if (nd_ra == NULL) { icmp6stat_inc(icp6s_tooshort); - if_put(ifp); return; } @@ -429,1590 +207,37 @@ nd6_ra_input(struct mbuf *m, int off, int icmp6len) goto freeit; } - { - struct nd_defrouter dr0; - u_int32_t advreachable = nd_ra->nd_ra_reachable; - - memset(&dr0, 0, sizeof(dr0)); - dr0.rtaddr = saddr6; - dr0.flags = nd_ra->nd_ra_flags_reserved; - dr0.rtlifetime = ntohs(nd_ra->nd_ra_router_lifetime); - dr0.expire = time_uptime + dr0.rtlifetime; - dr0.ifp = ifp; - /* unspecified or not? (RFC 2461 6.3.4) */ - if (advreachable) { - advreachable = ntohl(advreachable); - if (advreachable <= MAX_REACHABLE_TIME && - ndi->basereachable != advreachable) { - ndi->basereachable = advreachable; - ndi->reachable = ND_COMPUTE_RTIME(ndi->basereachable); - ndi->recalctm = nd6_recalc_reachtm_interval; /* reset */ - } - } - if (nd_ra->nd_ra_retransmit) - ndi->retrans = ntohl(nd_ra->nd_ra_retransmit); - if (nd_ra->nd_ra_curhoplimit) { - /* - * Ignore it. The router doesn't know the diameter of - * the Internet better than this source code. - */ - } - dr = defrtrlist_update(&dr0); - } - - /* - * prefix - */ - if (ndopts.nd_opts_pi) { - struct nd_opt_hdr *pt; - struct nd_opt_prefix_info *pi = NULL; - struct nd_prefix pr; - - for (pt = (struct nd_opt_hdr *)ndopts.nd_opts_pi; - pt <= (struct nd_opt_hdr *)ndopts.nd_opts_pi_end; - pt = (struct nd_opt_hdr *)((caddr_t)pt + - (pt->nd_opt_len << 3))) { - if (pt->nd_opt_type != ND_OPT_PREFIX_INFORMATION) - continue; - pi = (struct nd_opt_prefix_info *)pt; - - if (pi->nd_opt_pi_len != 4) { - nd6log((LOG_INFO, - "nd6_ra_input: invalid option " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_len)); - continue; - } - - if (128 < pi->nd_opt_pi_prefix_len) { - nd6log((LOG_INFO, - "nd6_ra_input: invalid prefix " - "len %d for prefix information option, " - "ignored\n", pi->nd_opt_pi_prefix_len)); - continue; - } - - if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) - || IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) { - nd6log((LOG_INFO, - "nd6_ra_input: invalid prefix " - "%s, ignored\n", - inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix, - src, sizeof(src)))); - continue; - } - - /* aggregatable unicast address, rfc2374 */ - if ((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) == 0x20 - && pi->nd_opt_pi_prefix_len != 64) { - nd6log((LOG_INFO, - "nd6_ra_input: invalid prefixlen " - "%d for rfc2374 prefix %s, ignored\n", - pi->nd_opt_pi_prefix_len, - inet_ntop(AF_INET6, &pi->nd_opt_pi_prefix, - src, sizeof(src)))); - continue; - } - - bzero(&pr, sizeof(pr)); - pr.ndpr_prefix.sin6_family = AF_INET6; - pr.ndpr_prefix.sin6_len = sizeof(pr.ndpr_prefix); - pr.ndpr_prefix.sin6_addr = pi->nd_opt_pi_prefix; - pr.ndpr_ifp = ifp; - - pr.ndpr_raf_onlink = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_ONLINK) ? 1 : 0; - pr.ndpr_raf_auto = (pi->nd_opt_pi_flags_reserved & - ND_OPT_PI_FLAG_AUTO) ? 1 : 0; - pr.ndpr_plen = pi->nd_opt_pi_prefix_len; - pr.ndpr_vltime = ntohl(pi->nd_opt_pi_valid_time); - pr.ndpr_pltime = ntohl(pi->nd_opt_pi_preferred_time); - pr.ndpr_lastupdate = time_uptime; - - if (in6_init_prefix_ltimes(&pr)) - continue; /* prefix lifetime init failed */ - - (void)prelist_update(&pr, dr, m); - } - } - - /* - * Source link layer address - */ - { - char *lladdr = NULL; - int lladdrlen = 0; - if (ndopts.nd_opts_src_lladdr) { lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } + ifp = if_get(m->m_pkthdr.ph_ifidx); + if (ifp == NULL) + goto freeit; + if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ra_input: lladdrlen mismatch for %s " "(if %d, RA packet %d)\n", inet_ntop(AF_INET6, &saddr6, src, sizeof(src)), ifp->if_addrlen, lladdrlen - 2)); + if_put(ifp); goto bad; } nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_ROUTER_ADVERT, 0); - - /* - * Installing a link-layer address might change the state of the - * router's neighbor cache, which might also affect our on-link - * detection of advertised prefixes. - */ - pfxlist_onlink_check(); - } + if_put(ifp); freeit: - if_put(ifp); m_freem(m); return; bad: icmp6stat_inc(icp6s_badra); - if_put(ifp); m_freem(m); } -/* - * default router list processing sub routines - */ -void -defrouter_addreq(struct nd_defrouter *new) -{ - struct rt_addrinfo info; - struct sockaddr_in6 def, mask, gate; - struct rtentry *rt; - int error; - - memset(&def, 0, sizeof(def)); - memset(&mask, 0, sizeof(mask)); - memset(&gate, 0, sizeof(gate)); /* for safety */ - memset(&info, 0, sizeof(info)); - - def.sin6_len = mask.sin6_len = gate.sin6_len = - sizeof(struct sockaddr_in6); - def.sin6_family = mask.sin6_family = gate.sin6_family = AF_INET6; - gate.sin6_addr = new->rtaddr; - gate.sin6_scope_id = 0; /* XXX */ - - info.rti_ifa = NULL; - info.rti_flags = RTF_GATEWAY; - info.rti_info[RTAX_DST] = sin6tosa(&def); - info.rti_info[RTAX_GATEWAY] = sin6tosa(&gate); - info.rti_info[RTAX_NETMASK] = sin6tosa(&mask); - - error = rtrequest(RTM_ADD, &info, RTP_DEFAULT, &rt, - new->ifp->if_rdomain); - if (error == 0) { - KERNEL_LOCK(); - rtm_send(rt, RTM_ADD, new->ifp->if_rdomain); - KERNEL_UNLOCK(); - rtfree(rt); - new->installed = 1; - } -} - -struct nd_defrouter * -defrouter_lookup(struct in6_addr *addr, unsigned int ifidx) -{ - struct nd_defrouter *dr; - - TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) - if (dr->ifp->if_index == ifidx && - IN6_ARE_ADDR_EQUAL(addr, &dr->rtaddr)) - return (dr); - - return (NULL); /* search failed */ -} - -void -defrtrlist_del(struct nd_defrouter *dr) -{ - struct nd_defrouter *deldr = NULL; - struct in6_ifextra *ext = dr->ifp->if_afdata[AF_INET6]; - struct nd_prefix *pr; - - /* - * Flush all the routing table entries that use the router - * as a next hop. - */ - /* XXX: better condition? */ - if (!ip6_forwarding) - rt6_flush(&dr->rtaddr, dr->ifp); - - if (dr->installed) { - deldr = dr; - defrouter_delreq(dr); - } - TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); - - /* - * Also delete all the pointers to the router in each prefix lists. - */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - struct nd_pfxrouter *pfxrtr; - if ((pfxrtr = pfxrtr_lookup(pr, dr)) != NULL) - pfxrtr_del(pfxrtr); - } - pfxlist_onlink_check(); - - /* - * If the router is the primary one, choose a new one. - * Note that defrouter_select() will remove the current gateway - * from the routing table. - */ - if (deldr) - defrouter_select(); - - ext->ndefrouters--; - if (ext->ndefrouters < 0) { - log(LOG_WARNING, "%s: negative count on %s\n", __func__, - dr->ifp->if_xname); - } - - free(dr, M_IP6NDP, sizeof(*dr)); -} - -/* - * Remove the default route for a given router. - * This is just a subroutine function for defrouter_select(), and should - * not be called from anywhere else. - */ -void -defrouter_delreq(struct nd_defrouter *dr) -{ - struct rt_addrinfo info; - struct sockaddr_in6 def, mask, gw; - struct rtentry *rt; - int error; - -#ifdef DIAGNOSTIC - if (!dr) - panic("dr == NULL in defrouter_delreq"); -#endif - - memset(&info, 0, sizeof(info)); - memset(&def, 0, sizeof(def)); - memset(&mask, 0, sizeof(mask)); - memset(&gw, 0, sizeof(gw)); /* for safety */ - - def.sin6_len = mask.sin6_len = gw.sin6_len = - sizeof(struct sockaddr_in6); - def.sin6_family = mask.sin6_family = gw.sin6_family = AF_INET6; - gw.sin6_addr = dr->rtaddr; - gw.sin6_scope_id = 0; /* XXX */ - - info.rti_flags = RTF_GATEWAY; - info.rti_info[RTAX_DST] = sin6tosa(&def); - info.rti_info[RTAX_GATEWAY] = sin6tosa(&gw); - info.rti_info[RTAX_NETMASK] = sin6tosa(&mask); - - error = rtrequest_delete(&info, RTP_DEFAULT, dr->ifp, &rt, - dr->ifp->if_rdomain); - if (error == 0) { - KERNEL_LOCK(); - rtm_send(rt, RTM_DELETE, dr->ifp->if_rdomain); - KERNEL_UNLOCK(); - rtfree(rt); - } - - dr->installed = 0; -} - -/* - * remove all default routes from default router list - */ -void -defrouter_reset(void) -{ - struct nd_defrouter *dr; - - TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) - defrouter_delreq(dr); - - /* - * XXX should we also nuke any default routers in the kernel, by - * going through them by rtalloc()? - */ -} - -/* - * Default Router Selection according to Section 6.3.6 of RFC 2461 and - * draft-ietf-ipngwg-router-selection: - * 1) Routers that are reachable or probably reachable should be preferred. - * If we have more than one (probably) reachable router, prefer ones - * with the highest router preference. - * 2) When no routers on the list are known to be reachable or - * probably reachable, routers SHOULD be selected in a round-robin - * fashion, regardless of router preference values. - * 3) If the Default Router List is empty, assume that all - * destinations are on-link. - * - * We assume nd_defrouter is sorted by router preference value. - * Since the code below covers both with and without router preference cases, - * we do not need to classify the cases by ifdef. - * - * At this moment, we do not try to install more than one default router, - * even when the multipath routing is available, because we're not sure about - * the benefits for stub hosts comparing to the risk of making the code - * complicated and the possibility of introducing bugs. - */ -void -defrouter_select(void) -{ - struct nd_defrouter *dr, *selected_dr = NULL, *installed_dr = NULL; - struct rtentry *rt = NULL; - struct llinfo_nd6 *ln = NULL; - - /* - * Let's handle easy case (3) first: - * If default router list is empty, there's nothing to be done. - */ - if (TAILQ_EMPTY(&nd_defrouter)) { - return; - } - - /* - * Search for a (probably) reachable router from the list. - * We just pick up the first reachable one (if any), assuming that - * the ordering rule of the list described in defrtrlist_update(). - */ - TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) { - if (!(dr->ifp->if_xflags & IFXF_AUTOCONF6)) - continue; - if (!selected_dr) { - rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp, - dr->ifp->if_rdomain); - if ((rt != NULL) && - (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && - ND6_IS_LLINFO_PROBREACH(ln)) { - selected_dr = dr; - } - rtfree(rt); - } - - if (dr->installed && !installed_dr) - installed_dr = dr; - else if (dr->installed && installed_dr) { - /* this should not happen. warn for diagnosis. */ - log(LOG_ERR, "defrouter_select: more than one router" - " is installed\n"); - } - } - /* - * If none of the default routers was found to be reachable, - * round-robin the list regardless of preference. - * Otherwise, if we have an installed router, check if the selected - * (reachable) router should really be preferred to the installed one. - * We only prefer the new router when the old one is not reachable - * or when the new one has a really higher preference value. - */ - if (!selected_dr) { - if (!installed_dr || !TAILQ_NEXT(installed_dr, dr_entry)) - selected_dr = TAILQ_FIRST(&nd_defrouter); - else - selected_dr = TAILQ_NEXT(installed_dr, dr_entry); - } else if (installed_dr) { - rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp, - installed_dr->ifp->if_rdomain); - if ((rt != NULL) && (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && - ND6_IS_LLINFO_PROBREACH(ln) && - rtpref(selected_dr) <= rtpref(installed_dr)) { - selected_dr = installed_dr; - } - rtfree(rt); - } - - /* - * If the selected router is different than the installed one, - * remove the installed router and install the selected one. - * Note that the selected router is never NULL here. - */ - if (installed_dr != selected_dr) { - if (installed_dr) - defrouter_delreq(installed_dr); - defrouter_addreq(selected_dr); - } -} - -/* - * for default router selection - * regards router-preference field as a 2-bit signed integer - */ -int -rtpref(struct nd_defrouter *dr) -{ -#ifdef RTPREF - switch (dr->flags & ND_RA_FLAG_RTPREF_MASK) { - case ND_RA_FLAG_RTPREF_HIGH: - return RTPREF_HIGH; - case ND_RA_FLAG_RTPREF_MEDIUM: - case ND_RA_FLAG_RTPREF_RSV: - return RTPREF_MEDIUM; - case ND_RA_FLAG_RTPREF_LOW: - return RTPREF_LOW; - default: - /* - * This case should never happen. If it did, it would mean a - * serious bug of kernel internal. We thus always bark here. - * Or, can we even panic? - */ - log(LOG_ERR, "rtpref: impossible RA flag %x", dr->flags); - return RTPREF_INVALID; - } - /* NOTREACHED */ -#else - return 0; -#endif -} - -struct nd_defrouter * -defrtrlist_update(struct nd_defrouter *new) -{ - struct nd_defrouter *dr, *n; - struct in6_ifextra *ext = new->ifp->if_afdata[AF_INET6]; - - NET_ASSERT_LOCKED(); - - if ((dr = defrouter_lookup(&new->rtaddr, new->ifp->if_index)) != NULL) { - /* entry exists */ - if (new->rtlifetime == 0) { - defrtrlist_del(dr); - dr = NULL; - } else { - int oldpref = rtpref(dr); - - /* override */ - dr->flags = new->flags; /* xxx flag check */ - dr->rtlifetime = new->rtlifetime; - dr->expire = new->expire; - - if (!dr->installed) - defrouter_select(); - - /* - * If the preference does not change, there's no need - * to sort the entries. - */ - if (rtpref(new) == oldpref) { - return (dr); - } - - /* - * preferred router may be changed, so relocate - * this router. - * XXX: calling TAILQ_REMOVE directly is a bad manner. - * However, since defrtrlist_del() has many side - * effects, we intentionally do so here. - * defrouter_select() below will handle routing - * changes later. - */ - TAILQ_REMOVE(&nd_defrouter, dr, dr_entry); - n = dr; - goto insert; - } - return (dr); - } - - /* entry does not exist */ - if (new->rtlifetime == 0) { - /* flush all possible redirects */ - if (new->ifp->if_xflags & IFXF_AUTOCONF6) - rt6_flush(&new->rtaddr, new->ifp); - return (NULL); - } - - if (ip6_maxifdefrouters >= 0 && - ext->ndefrouters >= ip6_maxifdefrouters) { - return (NULL); - } - - n = malloc(sizeof(*n), M_IP6NDP, M_NOWAIT | M_ZERO); - if (n == NULL) { - return (NULL); - } - *n = *new; - -insert: - /* - * Insert the new router in the Default Router List; - * The Default Router List should be in the descending order - * of router-preference. Routers with the same preference are - * sorted in the arriving time order. - */ - - /* insert at the end of the group */ - TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) - if (rtpref(n) > rtpref(dr)) - break; - if (dr) - TAILQ_INSERT_BEFORE(dr, n, dr_entry); - else - TAILQ_INSERT_TAIL(&nd_defrouter, n, dr_entry); - - defrouter_select(); - - ext->ndefrouters++; - - return (n); -} - -struct nd_pfxrouter * -pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) -{ - struct nd_pfxrouter *search; - - LIST_FOREACH(search, &pr->ndpr_advrtrs, pfr_entry) { - if (search->router == dr) - break; - } - - return (search); -} - -void -pfxrtr_add(struct nd_prefix *pr, struct nd_defrouter *dr) -{ - struct nd_pfxrouter *new; - - new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); - if (new == NULL) - return; - new->router = dr; - - LIST_INSERT_HEAD(&pr->ndpr_advrtrs, new, pfr_entry); - - pfxlist_onlink_check(); -} - -void -pfxrtr_del(struct nd_pfxrouter *pfr) -{ - LIST_REMOVE(pfr, pfr_entry); - free(pfr, M_IP6NDP, sizeof(*pfr)); -} - -struct nd_prefix * -nd6_prefix_lookup(struct nd_prefix *pr) -{ - struct nd_prefix *search; - - LIST_FOREACH(search, &nd_prefix, ndpr_entry) { - if (pr->ndpr_ifp == search->ndpr_ifp && - pr->ndpr_plen == search->ndpr_plen && - in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, - &search->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { - break; - } - } - - return (search); -} - -void -purge_detached(struct ifnet *ifp) -{ - struct nd_prefix *pr, *pr_next; - struct in6_ifaddr *ia6; - struct ifaddr *ifa, *ifa_next; - - NET_ASSERT_LOCKED(); - - LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, pr_next) { - /* - * This function is called when we need to make more room for - * new prefixes rather than keeping old, possibly stale ones. - * Detached prefixes would be a good candidate; if all routers - * that advertised the prefix expired, the prefix is also - * probably stale. - */ - if (pr->ndpr_ifp != ifp || - IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr) || - ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && - !LIST_EMPTY(&pr->ndpr_advrtrs))) - continue; - - TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrlist, ifa_list, ifa_next) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - ia6 = ifatoia6(ifa); - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == - IN6_IFF_AUTOCONF && ia6->ia6_ndpr == pr) { - in6_purgeaddr(ifa); - } - } - } -} - -int -nd6_prelist_add(struct nd_prefix *pr, struct nd_defrouter *dr, - struct nd_prefix **newp) -{ - struct nd_prefix *new = NULL; - struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6]; - int i; - - NET_ASSERT_LOCKED(); - - if (ip6_maxifprefixes >= 0) { - if (ext->nprefixes >= ip6_maxifprefixes / 2) { - purge_detached(pr->ndpr_ifp); - } - if (ext->nprefixes >= ip6_maxifprefixes) - return(ENOMEM); - } - - new = malloc(sizeof(*new), M_IP6NDP, M_NOWAIT | M_ZERO); - if (new == NULL) - return ENOMEM; - *new = *pr; - if (newp != NULL) - *newp = new; - - /* initialization */ - LIST_INIT(&new->ndpr_advrtrs); - in6_prefixlen2mask(&new->ndpr_mask, new->ndpr_plen); - /* make prefix in the canonical form */ - for (i = 0; i < 4; i++) - new->ndpr_prefix.sin6_addr.s6_addr32[i] &= - new->ndpr_mask.s6_addr32[i]; - - task_set(&new->ndpr_task, nd6_addr_add, new); - - /* link ndpr_entry to nd_prefix list */ - LIST_INSERT_HEAD(&nd_prefix, new, ndpr_entry); - - /* ND_OPT_PI_FLAG_ONLINK processing */ - if (new->ndpr_raf_onlink) { - char addr[INET6_ADDRSTRLEN]; - int e; - - if ((e = nd6_prefix_onlink(new)) != 0) { - nd6log((LOG_ERR, "%s: failed to make the prefix %s/%d" - " on-link on %s (errno=%d)\n", __func__, - inet_ntop(AF_INET6, &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); - /* proceed anyway. XXX: is it correct? */ - } - } - - if (dr) - pfxrtr_add(new, dr); - - ext->nprefixes++; - - return 0; -} - -void -prelist_remove(struct nd_prefix *pr) -{ - struct nd_pfxrouter *pfr, *next; - struct in6_ifextra *ext = pr->ndpr_ifp->if_afdata[AF_INET6]; - int e; - - NET_ASSERT_LOCKED(); - - /* make sure to invalidate the prefix until it is really freed. */ - pr->ndpr_vltime = 0; - pr->ndpr_pltime = 0; - - if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0 && - (e = nd6_prefix_offlink(pr)) != 0) { - char addr[INET6_ADDRSTRLEN]; - nd6log((LOG_ERR, "%s: failed to make %s/%d offlink " - "on %s, errno=%d\n", __func__, - inet_ntop(AF_INET6, &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); - /* what should we do? */ - } - - if (pr->ndpr_refcnt > 0) - return; /* notice here? */ - - /* unlink ndpr_entry from nd_prefix list */ - LIST_REMOVE(pr, ndpr_entry); - - /* free list of routers that adversed the prefix */ - LIST_FOREACH_SAFE(pfr, &pr->ndpr_advrtrs, pfr_entry, next) - free(pfr, M_IP6NDP, sizeof(*pfr)); - - ext->nprefixes--; - if (ext->nprefixes < 0) { - log(LOG_WARNING, "%s: negative count on %s\n", __func__, - pr->ndpr_ifp->if_xname); - } - - free(pr, M_IP6NDP, sizeof(*pr)); - - pfxlist_onlink_check(); -} - -/* - * dr - may be NULL - */ - -int -prelist_update(struct nd_prefix *new, struct nd_defrouter *dr, struct mbuf *m) -{ - struct in6_ifaddr *ia6_match = NULL; - struct ifaddr *ifa; - struct ifnet *ifp = new->ndpr_ifp; - struct nd_prefix *pr; - int error = 0; - int tempaddr_preferred = 0, autoconf = 0, statique = 0; - int auth; - struct in6_addrlifetime lt6_tmp; - char addr[INET6_ADDRSTRLEN]; - - NET_ASSERT_LOCKED(); - - auth = 0; - if (m) { - /* - * Authenticity for NA consists authentication for - * both IP header and IP datagrams, doesn't it ? - */ - auth = (m->m_flags & M_AUTH); - } - - if ((pr = nd6_prefix_lookup(new)) != NULL) { - /* - * nd6_prefix_lookup() ensures that pr and new have the same - * prefix on a same interface. - */ - - /* - * Update prefix information. Note that the on-link (L) bit - * and the autonomous (A) bit should NOT be changed from 1 - * to 0. - */ - if (new->ndpr_raf_onlink == 1) - pr->ndpr_raf_onlink = 1; - if (new->ndpr_raf_auto == 1) - pr->ndpr_raf_auto = 1; - if (new->ndpr_raf_onlink) { - pr->ndpr_vltime = new->ndpr_vltime; - pr->ndpr_pltime = new->ndpr_pltime; - pr->ndpr_preferred = new->ndpr_preferred; - pr->ndpr_expire = new->ndpr_expire; - pr->ndpr_lastupdate = new->ndpr_lastupdate; - } - - if (new->ndpr_raf_onlink && - (pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { - int e; - - if ((e = nd6_prefix_onlink(pr)) != 0) { - nd6log((LOG_ERR, "%s: failed to make the prefix" - " %s/%d on-link on %s (errno=%d)\n", - __func__, inet_ntop(AF_INET6, - &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, pr->ndpr_ifp->if_xname, e)); - /* proceed anyway. XXX: is it correct? */ - } - } - - if (dr && pfxrtr_lookup(pr, dr) == NULL) - pfxrtr_add(pr, dr); - } else { - struct nd_prefix *newpr = NULL; - - if (new->ndpr_vltime == 0) - goto end; - if (new->ndpr_raf_onlink == 0 && new->ndpr_raf_auto == 0) - goto end; - - error = nd6_prelist_add(new, dr, &newpr); - if (error != 0 || newpr == NULL) { - nd6log((LOG_NOTICE, "%s: nd6_prelist_add failed for" - " %s/%d on %s errno=%d, returnpr=%p\n", __func__, - inet_ntop(AF_INET6, &new->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - new->ndpr_plen, new->ndpr_ifp->if_xname, - error, newpr)); - goto end; /* we should just give up in this case. */ - } - - pr = newpr; - } - - /* - * Address autoconfiguration based on Section 5.5.3 of RFC 2462. - * Note that pr must be non NULL at this point. - */ - - /* 5.5.3 (a). Ignore the prefix without the A bit set. */ - if (!new->ndpr_raf_auto) - goto end; - - /* - * 5.5.3 (b). the link-local prefix should have been ignored in - * nd6_ra_input. - */ - - /* - * 5.5.3 (c). Consistency check on lifetimes: pltime <= vltime. - * This should have been done in nd6_ra_input. - */ - - /* - * 5.5.3 (d). If the prefix advertised does not match the prefix of an - * address already in the list, and the Valid Lifetime is not 0, - * form an address. Note that even a manually configured address - * should reject autoconfiguration of a new address. - */ - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { - struct in6_ifaddr *ia6; - int ifa_plen; - u_int32_t storedlifetime; - - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - - ia6 = ifatoia6(ifa); - - /* - * Spec is not clear here, but I believe we should concentrate - * on unicast (i.e. not anycast) addresses. - * XXX: other ia6_flags? detached or duplicated? - */ - if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - - ifa_plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); - if (ifa_plen != new->ndpr_plen || - !in6_are_prefix_equal(&ia6->ia_addr.sin6_addr, - &new->ndpr_prefix.sin6_addr, ifa_plen)) - continue; - - if (ia6_match == NULL) /* remember the first one */ - ia6_match = ia6; - - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) { - statique = 1; - continue; - } - - /* - * An already autoconfigured address matched. Now that we - * are sure there is at least one matched address, we can - * proceed to 5.5.3. (e): update the lifetimes according to the - * "two hours" rule and the privacy extension. - */ -#define TWOHOUR (120*60) - /* - * RFC2462 introduces the notion of StoredLifetime to the - * "two hours" rule as follows: - * the Lifetime associated with the previously autoconfigured - * address. - * Our interpretation of this definition is "the remaining - * lifetime to expiration at the evaluation time". One might - * be wondering if this interpretation is really conform to the - * RFC, because the text can read that "Lifetimes" are never - * decreased, and our definition of the "storedlifetime" below - * essentially reduces the "Valid Lifetime" advertised in the - * previous RA. But, this is due to the wording of the text, - * and our interpretation is the same as an author's intention. - * See the discussion in the IETF ipngwg ML in August 2001, - * with the Subject "StoredLifetime in RFC 2462". - */ - lt6_tmp = ia6->ia6_lifetime; - - /* RFC 4941 temporary addresses (privacy extension). */ - if (ia6->ia6_flags & IN6_IFF_PRIVACY) { - /* Do we still have a non-deprecated address? */ - if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) == 0) - tempaddr_preferred = 1; - /* Don't extend lifetime for temporary addresses. */ - if (new->ndpr_vltime >= lt6_tmp.ia6t_vltime) - continue; - if (new->ndpr_pltime >= lt6_tmp.ia6t_pltime) - continue; - } else if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) == 0) - /* We have a regular SLAAC address. */ - autoconf = 1; - - if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME) - storedlifetime = ND6_INFINITE_LIFETIME; - else if (time_uptime - ia6->ia6_updatetime > - lt6_tmp.ia6t_vltime) { - /* - * The case of "invalid" address. We should usually - * not see this case. - */ - storedlifetime = 0; - } else - storedlifetime = lt6_tmp.ia6t_vltime - - (time_uptime - ia6->ia6_updatetime); - if (TWOHOUR < new->ndpr_vltime || - storedlifetime < new->ndpr_vltime) { - lt6_tmp.ia6t_vltime = new->ndpr_vltime; - } else if (storedlifetime <= TWOHOUR -#if 0 - /* - * This condition is logically redundant, so we just - * omit it. - * See IPng 6712, 6717, and 6721. - */ - && new->ndpr_vltime <= storedlifetime -#endif - ) { - if (auth) { - lt6_tmp.ia6t_vltime = new->ndpr_vltime; - } - } else { - /* - * new->ndpr_vltime <= TWOHOUR && - * TWOHOUR < storedlifetime - */ - lt6_tmp.ia6t_vltime = TWOHOUR; - } - - /* The 2 hour rule is not imposed for preferred lifetime. */ - lt6_tmp.ia6t_pltime = new->ndpr_pltime; - - in6_init_address_ltimes(pr, <6_tmp); - - ia6->ia6_lifetime = lt6_tmp; - ia6->ia6_updatetime = time_uptime; - } - - if ((!autoconf || ((ifp->if_xflags & IFXF_INET6_NOPRIVACY) == 0 && - !tempaddr_preferred)) && - new->ndpr_vltime != 0 && new->ndpr_pltime != 0 && - !((ifp->if_xflags & IFXF_INET6_NOPRIVACY) && statique)) { - /* - * There is no SLAAC address and/or there is no preferred RFC - * 4941 temporary address. And prefix lifetimes are non-zero. - * And there is no static address in the same prefix. - * Create new addresses in process context. - * Increment prefix refcount to ensure the prefix is not - * removed before the task is done. - */ - pr->ndpr_refcnt++; - if (task_add(systq, &pr->ndpr_task) == 0) - pr->ndpr_refcnt--; - } - - end: - return error; -} - -void -nd6_addr_add(void *prptr) -{ - struct nd_prefix *pr = (struct nd_prefix *)prptr; - struct in6_ifaddr *ia6; - struct ifaddr *ifa; - int ifa_plen, autoconf, privacy, s; - - NET_LOCK(s); - - autoconf = 1; - privacy = (pr->ndpr_ifp->if_xflags & IFXF_INET6_NOPRIVACY) == 0; - - /* - * Check again if a non-deprecated address has already - * been autoconfigured for this prefix. - */ - TAILQ_FOREACH(ifa, &pr->ndpr_ifp->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - - ia6 = ifatoia6(ifa); - - /* - * Spec is not clear here, but I believe we should concentrate - * on unicast (i.e. not anycast) addresses. - * XXX: other ia6_flags? detached or duplicated? - */ - if ((ia6->ia6_flags & IN6_IFF_ANYCAST) != 0) - continue; - - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) - continue; - - if ((ia6->ia6_flags & IN6_IFF_DEPRECATED) != 0) - continue; - - ifa_plen = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); - if (ifa_plen == pr->ndpr_plen && - in6_are_prefix_equal(&ia6->ia_addr.sin6_addr, - &pr->ndpr_prefix.sin6_addr, ifa_plen)) { - if ((ia6->ia6_flags & IN6_IFF_PRIVACY) == 0) - autoconf = 0; - else - privacy = 0; - if (!autoconf && !privacy) - break; - } - } - - if (autoconf && (ia6 = in6_ifadd(pr, 0)) != NULL) { - ia6->ia6_ndpr = pr; - pr->ndpr_refcnt++; - } else - autoconf = 0; - - if (privacy && (ia6 = in6_ifadd(pr, 1)) != NULL) { - ia6->ia6_ndpr = pr; - pr->ndpr_refcnt++; - } else - privacy = 0; - - /* - * A newly added address might affect the status - * of other addresses, so we check and update it. - * XXX: what if address duplication happens? - */ - if (autoconf || privacy) - pfxlist_onlink_check(); - - /* Decrement prefix refcount now that the task is done. */ - if (--pr->ndpr_refcnt == 0) - prelist_remove(pr); - - NET_UNLOCK(s); -} - -/* - * A supplement function used in the on-link detection below; - * detect if a given prefix has a (probably) reachable advertising router. - * XXX: lengthy function name... - */ -struct nd_pfxrouter * -find_pfxlist_reachable_router(struct nd_prefix *pr) -{ - struct nd_pfxrouter *pfxrtr; - struct rtentry *rt = NULL; - struct llinfo_nd6 *ln; - - LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) { - if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0, - pfxrtr->router->ifp, pfxrtr->router->ifp->if_rdomain)) && - (ln = (struct llinfo_nd6 *)rt->rt_llinfo) && - ND6_IS_LLINFO_PROBREACH(ln)) { - rtfree(rt); - break; /* found */ - } - rtfree(rt); - } - - return (pfxrtr); -} - -/* - * Check if each prefix in the prefix list has at least one available router - * that advertised the prefix (a router is "available" if its neighbor cache - * entry is reachable or probably reachable). - * If the check fails, the prefix may be off-link, because, for example, - * we have moved from the network but the lifetime of the prefix has not - * expired yet. So we should not use the prefix if there is another prefix - * that has an available router. - * But, if there is no prefix that has an available router, we still regards - * all the prefixes as on-link. This is because we can't tell if all the - * routers are simply dead or if we really moved from the network and there - * is no router around us. - */ -void -pfxlist_onlink_check(void) -{ - struct ifnet *ifp; - struct ifaddr *ifa; - struct nd_prefix *pr; - struct in6_ifaddr *ia6, *pia6 = NULL; - char addr[INET6_ADDRSTRLEN]; - - /* - * Check if there is a prefix that has a reachable advertising - * router. - */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr)) - break; - } - if (pr != NULL || !TAILQ_EMPTY(&nd_defrouter)) { - /* - * There is at least one prefix that has a reachable router, - * or at least a router which probably does not advertise - * any prefixes. The latter would be the case when we move - * to a new link where we have a router that does not provide - * prefixes and we configure an address by hand. - * Detach prefixes which have no reachable advertising - * router, and attach other prefixes. - */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - /* XXX: a link-local prefix should never be detached */ - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) - continue; - - /* - * we aren't interested in prefixes without the L bit - * set. - */ - if (pr->ndpr_raf_onlink == 0) - continue; - - if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && - find_pfxlist_reachable_router(pr) == NULL) - pr->ndpr_stateflags |= NDPRF_DETACHED; - if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && - find_pfxlist_reachable_router(pr) != 0) - pr->ndpr_stateflags &= ~NDPRF_DETACHED; - } - } else { - /* there is no prefix that has a reachable router */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) - continue; - - if (pr->ndpr_raf_onlink == 0) - continue; - - if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0) - pr->ndpr_stateflags &= ~NDPRF_DETACHED; - } - } - - /* - * Remove each interface route associated with a (just) detached - * prefix, and reinstall the interface route for a (just) attached - * prefix. Note that all attempt of reinstallation does not - * necessarily success, when a same prefix is shared among multiple - * interfaces. Such cases will be handled in nd6_prefix_onlink, - * so we don't have to care about them. - */ - LIST_FOREACH(pr, &nd_prefix, ndpr_entry) { - int e; - - if (IN6_IS_ADDR_LINKLOCAL(&pr->ndpr_prefix.sin6_addr)) - continue; - - if (pr->ndpr_raf_onlink == 0) - continue; - - if ((pr->ndpr_stateflags & NDPRF_DETACHED) != 0 && - (pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { - if ((e = nd6_prefix_offlink(pr)) != 0) { - nd6log((LOG_ERR, "%s: failed to make %s/%d " - "offlink, errno=%d\n", __func__, - inet_ntop(AF_INET6, - &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, e)); - } - } - if ((pr->ndpr_stateflags & NDPRF_DETACHED) == 0 && - (pr->ndpr_stateflags & NDPRF_ONLINK) == 0 && - pr->ndpr_raf_onlink) { - if ((e = nd6_prefix_onlink(pr)) != 0) { - nd6log((LOG_ERR, "%s: failed to make %s/%d " - "offlink, errno=%d\n", __func__, - inet_ntop(AF_INET6, - &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, e)); - } - } - } - - /* - * Changes on the prefix status might affect address status as well. - * Make sure that all addresses derived from an attached prefix are - * attached, and that all addresses derived from a detached prefix are - * detached. Note, however, that a manually configured address should - * always be attached. - * The precise detection logic is same as the one for prefixes. - */ - TAILQ_FOREACH(ifp, &ifnet, if_list) { - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - - ia6 = ifatoia6(ifa); - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) - continue; - - /* - * This can happen when we first configure the address - * (i.e. the address exists, but the prefix does not). - * XXX: complicated relationships... - */ - if (ia6->ia6_ndpr == NULL) - continue; - - if (find_pfxlist_reachable_router(ia6->ia6_ndpr)) { - pia6 = ia6; - break; - } - } - - if (pia6 != NULL) - break; - } - - TAILQ_FOREACH(ifp, &ifnet, if_list) { - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - - ia6 = ifatoia6(ifa); - if ((ia6->ia6_flags & IN6_IFF_AUTOCONF) == 0) - continue; - - if (pia6 != NULL) { - /* XXX: see above. */ - if (ia6->ia6_ndpr == NULL) - continue; - - if (find_pfxlist_reachable_router(ia6->ia6_ndpr)) - ia6->ia6_flags &= ~IN6_IFF_DETACHED; - else - ia6->ia6_flags |= IN6_IFF_DETACHED; - } else { - ia6->ia6_flags &= ~IN6_IFF_DETACHED; - } - } - } -} - -int -nd6_prefix_onlink(struct nd_prefix *pr) -{ - struct ifnet *ifp = pr->ndpr_ifp; - struct ifaddr *ifa; - struct nd_prefix *opr; - char addr[INET6_ADDRSTRLEN]; - int error, rtflags = 0; - - /* sanity check */ - if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) - return (EEXIST); - - /* - * Add the interface route associated with the prefix. Before - * installing the route, check if there's the same prefix on another - * interface, and the prefix has already installed the interface route. - * Although such a configuration is expected to be rare, we explicitly - * allow it. - */ - LIST_FOREACH(opr, &nd_prefix, ndpr_entry) { - if (opr == pr) - continue; - - if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) - continue; - - if (opr->ndpr_plen == pr->ndpr_plen && - in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, - &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) - return (0); - } - - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (ifatoia6(ifa)->ia6_ndpr == pr) - break; - } - if (ifa == NULL) { - /* - * This can still happen, when, for example, we receive an RA - * containing a prefix with the L bit set and the A bit clear, - * after removing all IPv6 addresses on the receiving - * interface. This should, of course, be rare though. - */ - nd6log((LOG_NOTICE, - "nd6_prefix_onlink: failed to find any ifaddr" - " to add route for a prefix(%s/%d) on %s\n", - inet_ntop(AF_INET6, &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen, ifp->if_xname)); - return (0); - } - - if (nd6_need_cache(ifp)) - rtflags = RTF_CLONING | RTF_CONNECTED; - - error = rt_ifa_add(ifa, rtflags, sin6tosa(&pr->ndpr_prefix)); - if (error == 0) - pr->ndpr_stateflags |= NDPRF_ONLINK; - - return (error); -} - -int -nd6_prefix_offlink(struct nd_prefix *pr) -{ - struct ifnet *ifp = pr->ndpr_ifp; - struct ifaddr *ifa; - struct nd_prefix *opr; - char addr[INET6_ADDRSTRLEN]; - int error, rtflags = 0; - - /* sanity check */ - if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) { - nd6log((LOG_ERR, - "nd6_prefix_offlink: %s/%d is already off-link\n", - inet_ntop(AF_INET6, &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - pr->ndpr_plen)); - return (EEXIST); - } - - TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - if (ifatoia6(ifa)->ia6_ndpr == pr) - break; - } - if (ifa == NULL) - return (EINVAL); - - if (nd6_need_cache(ifp)) - rtflags = RTF_CLONING | RTF_CONNECTED; - - error = rt_ifa_del(ifa, rtflags, sin6tosa(&pr->ndpr_prefix)); - if (error == 0) { - pr->ndpr_stateflags &= ~NDPRF_ONLINK; - - /* - * There might be the same prefix on another interface, - * the prefix which could not be on-link just because we have - * the interface route (see comments in nd6_prefix_onlink). - * If there's one, try to make the prefix on-link on the - * interface. - */ - LIST_FOREACH(opr, &nd_prefix, ndpr_entry) { - if (opr == pr) - continue; - - if ((opr->ndpr_stateflags & NDPRF_ONLINK) != 0) - continue; - - /* - * KAME specific: detached prefixes should not be - * on-link. - */ - if ((opr->ndpr_stateflags & NDPRF_DETACHED) != 0) - continue; - - if (opr->ndpr_plen == pr->ndpr_plen && - in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, - &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { - int e; - - if ((e = nd6_prefix_onlink(opr)) != 0) { - nd6log((LOG_ERR, - "nd6_prefix_offlink: failed to " - "recover a prefix %s/%d from %s " - "to %s (errno = %d)\n", - inet_ntop(AF_INET6, - &pr->ndpr_prefix.sin6_addr, - addr, sizeof(addr)), - opr->ndpr_plen, ifp->if_xname, - opr->ndpr_ifp->if_xname, e)); - } - } - } - } - - return (error); -} - -struct in6_ifaddr * -in6_ifadd(struct nd_prefix *pr, int privacy) -{ - struct ifnet *ifp = pr->ndpr_ifp; - struct ifaddr *ifa; - struct in6_aliasreq ifra; - struct in6_ifaddr *ia6; - int error, plen0; - struct in6_addr mask, rand_ifid; - int prefixlen = pr->ndpr_plen; - - NET_ASSERT_LOCKED(); - - in6_prefixlen2mask(&mask, prefixlen); - - /* - * find a link-local address (will be interface ID). - * Is it really mandatory? Theoretically, a global or a site-local - * address can be configured without a link-local address, if we - * have a unique interface identifier... - * - * it is not mandatory to have a link-local address, we can generate - * interface identifier on the fly. we do this because: - * (1) it should be the easiest way to find interface identifier. - * (2) RFC2462 5.4 suggesting the use of the same interface identifier - * for multiple addresses on a single interface, and possible shortcut - * of DAD. we omitted DAD for this reason in the past. - * (3) a user can prevent autoconfiguration of global address - * by removing link-local address by hand (this is partly because we - * don't have other way to control the use of IPv6 on a interface. - * this has been our design choice - cf. NRL's "ifconfig auto"). - * (4) it is easier to manage when an interface has addresses - * with the same interface identifier, than to have multiple addresses - * with different interface identifiers. - */ - ifa = &in6ifa_ifpforlinklocal(ifp, 0)->ia_ifa; /* 0 is OK? */ - if (ifa) - ia6 = ifatoia6(ifa); - else - return NULL; - - /* prefixlen + ifidlen must be equal to 128 */ - plen0 = in6_mask2len(&ia6->ia_prefixmask.sin6_addr, NULL); - if (prefixlen != plen0) { - nd6log((LOG_INFO, "%s: wrong prefixlen for %s " - "(prefix=%d ifid=%d)\n", __func__, - ifp->if_xname, prefixlen, 128 - plen0)); - return NULL; - } - - /* make ifaddr */ - bzero(&ifra, sizeof(ifra)); - strncpy(ifra.ifra_name, ifp->if_xname, sizeof(ifra.ifra_name)); - ifra.ifra_addr.sin6_family = AF_INET6; - ifra.ifra_addr.sin6_len = sizeof(struct sockaddr_in6); - /* prefix */ - bcopy(&pr->ndpr_prefix.sin6_addr, &ifra.ifra_addr.sin6_addr, - sizeof(ifra.ifra_addr.sin6_addr)); - ifra.ifra_addr.sin6_addr.s6_addr32[0] &= mask.s6_addr32[0]; - ifra.ifra_addr.sin6_addr.s6_addr32[1] &= mask.s6_addr32[1]; - ifra.ifra_addr.sin6_addr.s6_addr32[2] &= mask.s6_addr32[2]; - ifra.ifra_addr.sin6_addr.s6_addr32[3] &= mask.s6_addr32[3]; - - /* interface ID */ - if (privacy) { - ifra.ifra_flags |= IN6_IFF_PRIVACY; - bcopy(&pr->ndpr_prefix.sin6_addr, &rand_ifid, - sizeof(rand_ifid)); - in6_get_rand_ifid(ifp, &rand_ifid); - ifra.ifra_addr.sin6_addr.s6_addr32[0] |= - (rand_ifid.s6_addr32[0] & ~mask.s6_addr32[0]); - ifra.ifra_addr.sin6_addr.s6_addr32[1] |= - (rand_ifid.s6_addr32[1] & ~mask.s6_addr32[1]); - ifra.ifra_addr.sin6_addr.s6_addr32[2] |= - (rand_ifid.s6_addr32[2] & ~mask.s6_addr32[2]); - ifra.ifra_addr.sin6_addr.s6_addr32[3] |= - (rand_ifid.s6_addr32[3] & ~mask.s6_addr32[3]); - } else { - ifra.ifra_addr.sin6_addr.s6_addr32[0] |= - (ia6->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]); - ifra.ifra_addr.sin6_addr.s6_addr32[1] |= - (ia6->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]); - ifra.ifra_addr.sin6_addr.s6_addr32[2] |= - (ia6->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); - ifra.ifra_addr.sin6_addr.s6_addr32[3] |= - (ia6->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); - } - - /* new prefix mask. */ - ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); - ifra.ifra_prefixmask.sin6_family = AF_INET6; - bcopy(&mask, &ifra.ifra_prefixmask.sin6_addr, - sizeof(ifra.ifra_prefixmask.sin6_addr)); - - /* - * lifetime. - * XXX: in6_init_address_ltimes would override these values later. - * We should reconsider this logic. - */ - ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime; - ifra.ifra_lifetime.ia6t_pltime = pr->ndpr_pltime; - - if (privacy) { - if (ifra.ifra_lifetime.ia6t_vltime > ND6_PRIV_VALID_LIFETIME) - ifra.ifra_lifetime.ia6t_vltime = ND6_PRIV_VALID_LIFETIME; - if (ifra.ifra_lifetime.ia6t_pltime > ND6_PRIV_PREFERRED_LIFETIME) - ifra.ifra_lifetime.ia6t_pltime = ND6_PRIV_PREFERRED_LIFETIME - - arc4random_uniform(ND6_PRIV_MAX_DESYNC_FACTOR); - } - - /* XXX: scope zone ID? */ - - ifra.ifra_flags |= IN6_IFF_AUTOCONF|IN6_IFF_TENTATIVE; - - /* If this address already exists, update it. */ - ia6 = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - - error = in6_update_ifa(ifp, &ifra, ia6); - - if (error != 0) { - char addr[INET6_ADDRSTRLEN]; - - nd6log((LOG_ERR, - "%s: failed to make ifaddr %s on %s (errno=%d)\n", __func__, - inet_ntop(AF_INET6, &ifra.ifra_addr.sin6_addr, - addr, sizeof(addr)), - ifp->if_xname, error)); - return (NULL); /* ifaddr must not have been allocated. */ - } - - ia6 = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); - - /* Perform DAD, if needed. */ - if (ia6 != NULL && ia6->ia6_flags & IN6_IFF_TENTATIVE) - nd6_dad_start(&ia6->ia_ifa); - - return (ia6); -} - -int -in6_init_prefix_ltimes(struct nd_prefix *ndpr) -{ - - /* check if preferred lifetime > valid lifetime. RFC2462 5.5.3 (c) */ - if (ndpr->ndpr_pltime > ndpr->ndpr_vltime) { - nd6log((LOG_INFO, "in6_init_prefix_ltimes: preferred lifetime" - "(%d) is greater than valid lifetime(%d)\n", - (u_int)ndpr->ndpr_pltime, (u_int)ndpr->ndpr_vltime)); - return (EINVAL); - } - if (ndpr->ndpr_pltime == ND6_INFINITE_LIFETIME) - ndpr->ndpr_preferred = 0; - else - ndpr->ndpr_preferred = time_uptime + ndpr->ndpr_pltime; - if (ndpr->ndpr_vltime == ND6_INFINITE_LIFETIME) - ndpr->ndpr_expire = 0; - else - ndpr->ndpr_expire = time_uptime + ndpr->ndpr_vltime; - - return 0; -} - -void -in6_init_address_ltimes(struct nd_prefix *new, struct in6_addrlifetime *lt6) -{ - - /* Valid lifetime must not be updated unless explicitly specified. */ - /* init ia6t_expire */ - if (lt6->ia6t_vltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_expire = 0; - else { - lt6->ia6t_expire = time_uptime; - lt6->ia6t_expire += lt6->ia6t_vltime; - } - - /* init ia6t_preferred */ - if (lt6->ia6t_pltime == ND6_INFINITE_LIFETIME) - lt6->ia6t_preferred = 0; - else { - lt6->ia6t_preferred = time_uptime; - lt6->ia6t_preferred += lt6->ia6t_pltime; - } -} - /* * Delete all the routing table entries that use the specified gateway. * XXX: this function causes search through all entries of routing table, so