📄 nd6_rtr.c
字号:
* 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.
*/
#if defined(__bsdi__) || (defined(__FreeBSD__) && __FreeBSD__ < 3)
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next)
#elif defined(__FreeBSD__) && __FreeBSD__ >= 4
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list)
#else
for (ifa = ifp->if_addrlist.tqh_first; ifa; ifa = ifa->ifa_list.tqe_next)
#endif
{
struct in6_ifaddr *ifa6;
int ifa_plen;
u_int32_t storedlifetime;
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
long time_second = time.tv_sec;
#endif
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ifa6 = (struct in6_ifaddr *)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 ((ifa6->ia6_flags & IN6_IFF_ANYCAST) != 0)
continue;
ifa_plen = in6_mask2len(&ifa6->ia_prefixmask.sin6_addr, NULL);
if (ifa_plen != new->ndpr_plen ||
!in6_are_prefix_equal(&ifa6->ia_addr.sin6_addr,
&new->ndpr_prefix.sin6_addr,
ifa_plen))
continue;
if (ia6_match == NULL) /* remember the first one */
ia6_match = ifa6;
if ((ifa6->ia6_flags & IN6_IFF_AUTOCONF) == 0)
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 = ifa6->ia6_lifetime;
if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME)
storedlifetime = ND6_INFINITE_LIFETIME;
else if (time_second - ifa6->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_second - ifa6->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);
/*
* We need to treat lifetimes for temporary addresses
* differently, according to
* draft-ietf-ipngwg-temp-addresses-v2-00.txt 3.3 (1);
* we only update the lifetimes when they are in the maximum
* intervals.
*/
if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
u_int32_t maxvltime, maxpltime;
if (ip6_temp_valid_lifetime >
(u_int32_t)(time_second - ifa6->ia6_createtime)) {
maxvltime = ip6_temp_valid_lifetime -
(time_second - ifa6->ia6_createtime);
} else
maxvltime = 0;
if (ip6_temp_preferred_lifetime >
(u_int32_t)((time_second - ifa6->ia6_createtime) +
ip6_desync_factor)) {
maxpltime = ip6_temp_preferred_lifetime -
(time_second - ifa6->ia6_createtime) -
ip6_desync_factor;
} else
maxpltime = 0;
if (lt6_tmp.ia6t_vltime == ND6_INFINITE_LIFETIME ||
lt6_tmp.ia6t_vltime > maxvltime) {
lt6_tmp.ia6t_vltime = maxvltime;
}
if (lt6_tmp.ia6t_pltime == ND6_INFINITE_LIFETIME ||
lt6_tmp.ia6t_pltime > maxpltime) {
lt6_tmp.ia6t_pltime = maxpltime;
}
}
ifa6->ia6_lifetime = lt6_tmp;
ifa6->ia6_updatetime = time_second;
}
if (ia6_match == NULL && new->ndpr_vltime) {
/*
* No address matched and the valid lifetime is non-zero.
* Create a new address.
*/
if ((ia6 = in6_ifadd(new)) != NULL) {
/*
* note that we should use pr (not new) for reference.
*/
pr->ndpr_refcnt++;
ia6->ia6_ndpr = pr;
/*
* draft-ietf-ipngwg-temp-addresses-v2-00 3.3 (2).
* When a new public address is created as described
* in RFC2462, also create a new temporary address.
*
* draft-ietf-ipngwg-temp-addresses-v2-00 3.5.
* When an interface connects to a new link, a new
* randomized interface identifier should be generated
* immediately together with a new set of temporary
* addresses. Thus, we specifiy 1 as the 2nd arg of
* in6_tmpifadd().
*/
if (ip6_use_tempaddr) {
int e;
if ((e = in6_tmpifadd(ia6, 1)) != 0) {
nd6log((LOG_NOTICE, "prelist_update: "
"failed to create a temporary "
"address, errno=%d\n",
e));
}
}
/*
* A newly added address might affect the status
* of other addresses, so we check and update it.
* XXX: what if address duplication happens?
*/
pfxlist_onlink_check();
} else {
/* just set an error. do not bark here. */
error = EADDRNOTAVAIL; /* XXX: might be unused. */
}
}
end:
splx(s);
return error;
}
/*
* 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...
*/
static struct nd_pfxrouter *
find_pfxlist_reachable_router(pr)
struct nd_prefix *pr;
{
struct nd_pfxrouter *pfxrtr;
struct rtentry *rt;
struct llinfo_nd6 *ln;
for (pfxrtr = LIST_FIRST(&pr->ndpr_advrtrs); pfxrtr;
pfxrtr = LIST_NEXT(pfxrtr, pfr_entry)) {
if ((rt = nd6_lookup(&pfxrtr->router->rtaddr, 0,
pfxrtr->router->ifp)) &&
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
ND6_IS_LLINFO_PROBREACH(ln))
break; /* found */
}
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()
{
struct nd_prefix *pr;
struct in6_ifaddr *ifa;
/*
* Check if there is a prefix that has a reachable advertising
* router.
*/
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
if (pr->ndpr_raf_onlink && find_pfxlist_reachable_router(pr))
break;
}
if (pr != NULL || TAILQ_FIRST(&nd_defrouter) != NULL) {
/*
* 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.
*/
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
/* 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 */
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
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.
*/
for (pr = nd_prefix.lh_first; pr; pr = pr->ndpr_next) {
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,
"pfxlist_onlink_check: failed to "
"make %s/%d offlink, errno=%d\n",
ip6_sprintf(&pr->ndpr_prefix.sin6_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,
"pfxlist_onlink_check: failed to "
"make %s/%d offlink, errno=%d\n",
ip6_sprintf(&pr->ndpr_prefix.sin6_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.
*/
for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
if (!(ifa->ia6_flags & IN6_IFF_AUTOCONF))
continue;
if (ifa->ia6_ndpr == NULL) {
/*
* This can happen when we first configure the address
* (i.e. the address exists, but the prefix does not).
* XXX: complicated relationships...
*/
continue;
}
if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
break;
}
if (ifa) {
for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
if (ifa->ia6_ndpr == NULL) /* XXX: see above. */
continue;
if (find_pfxlist_reachable_router(ifa->ia6_ndpr))
ifa->ia6_flags &= ~IN6_IFF_DETACHED;
else
ifa->ia6_flags |= IN6_IFF_DETACHED;
}
}
else {
for (ifa = in6_ifaddr; ifa; ifa = ifa->ia_next) {
if ((ifa->ia6_flags & IN6_IFF_AUTOCONF) == 0)
continue;
ifa->ia6_flags &= ~IN6_IFF_DETACHED;
}
}
#ifdef MIP6
if (MIP6_IS_MN) {
int coa_changed = 0;
hif_save_location();
coa_changed = mip6_select_coa2();
(void)mip6_process_pfxlist_status_change(&hif_coa);
if (coa_changed == 1)
mip6_process_movement();
else
hif_restore_location();
}
#endif /* MIP6 */
}
int
nd6_prefix_onlink(pr)
struct nd_prefix *pr;
{
struct ifaddr *ifa;
struct ifnet *ifp = pr->ndpr_ifp;
struct sockaddr_in6 mask6;
struct nd_prefix *opr;
u_long rtflags;
int error = 0;
struct rtentry *rt = NULL;
/* sanity check */
if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) {
nd6log((LOG_ERR,
"nd6_prefix_onlink: %s/%d is already on-link\n",
ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen);
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.
*/
for (opr = nd_prefix.lh_first; opr; opr = opr->ndpr_next) {
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);
}
/*
* We prefer link-local addresses as the associated interface address.
*/
/* search for a link-local addr */
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -