diff options
Diffstat (limited to 'freebsd/sys/netinet6/in6.c')
-rw-r--r-- | freebsd/sys/netinet6/in6.c | 101 |
1 files changed, 54 insertions, 47 deletions
diff --git a/freebsd/sys/netinet6/in6.c b/freebsd/sys/netinet6/in6.c index ef59203e..f3306bc3 100644 --- a/freebsd/sys/netinet6/in6.c +++ b/freebsd/sys/netinet6/in6.c @@ -1392,13 +1392,15 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifaddr *ia, * if this is its first address, */ if (hostIsNew != 0) { - IF_ADDR_RLOCK(ifp); + struct epoch_tracker et; + + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ifacount++; } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); } if (ifacount <= 1 && ifp->if_ioctl) { @@ -1476,9 +1478,10 @@ done: struct in6_ifaddr * in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) { + struct epoch_tracker et; struct ifaddr *ifa; - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1490,7 +1493,7 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int ignoreflags) break; } } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return ((struct in6_ifaddr *)ifa); } @@ -1527,9 +1530,10 @@ in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) struct in6_ifaddr * in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr) { + struct epoch_tracker et; struct ifaddr *ifa; - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1538,7 +1542,7 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr) break; } } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return ((struct in6_ifaddr *)ifa); } @@ -1549,12 +1553,13 @@ in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr) struct in6_ifaddr * in6ifa_llaonifp(struct ifnet *ifp) { + struct epoch_tracker et; struct sockaddr_in6 *sin6; struct ifaddr *ifa; if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) return (NULL); - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1564,7 +1569,7 @@ in6ifa_llaonifp(struct ifnet *ifp) IN6_IS_ADDR_MC_NODELOCAL(&sin6->sin6_addr)) break; } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return ((struct in6_ifaddr *)ifa); } @@ -1701,6 +1706,7 @@ int in6_ifhasaddr(struct ifnet *ifp, struct in6_addr *addr) { struct in6_addr in6; + struct epoch_tracker et; struct ifaddr *ifa; struct in6_ifaddr *ia6; @@ -1709,17 +1715,17 @@ in6_ifhasaddr(struct ifnet *ifp, struct in6_addr *addr) return (0); in6_setscope(&in6, ifp, NULL); - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; ia6 = (struct in6_ifaddr *)ifa; if (IN6_ARE_ADDR_EQUAL(&ia6->ia_addr.sin6_addr, &in6)) { - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return (1); } } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return (0); } @@ -1823,6 +1829,7 @@ in6_prefixlen2mask(struct in6_addr *maskp, int len) struct in6_ifaddr * in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) { + struct epoch_tracker et; int dst_scope = in6_addrscope(dst), blen = -1, tlen; struct ifaddr *ifa; struct in6_ifaddr *besta = NULL; @@ -1836,7 +1843,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) * If two or more, return one which matches the dst longest. * If none, return one of global addresses assigned other ifs. */ - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1870,7 +1877,7 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) } if (besta) { ifa_ref(&besta->ia_ifa); - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return (besta); } @@ -1891,23 +1898,23 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) if (ifa != NULL) ifa_ref(ifa); - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return (struct in6_ifaddr *)ifa; } /* use the last-resort values, that are, deprecated addresses */ if (dep[0]) { ifa_ref((struct ifaddr *)dep[0]); - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return dep[0]; } if (dep[1]) { ifa_ref((struct ifaddr *)dep[1]); - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return dep[1]; } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); return NULL; } @@ -1917,10 +1924,11 @@ in6_ifawithifp(struct ifnet *ifp, struct in6_addr *dst) void in6_if_up(struct ifnet *ifp) { + struct epoch_tracker et; struct ifaddr *ifa; struct in6_ifaddr *ia; - IF_ADDR_RLOCK(ifp); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1936,7 +1944,7 @@ in6_if_up(struct ifnet *ifp) arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz)); } } - IF_ADDR_RUNLOCK(ifp); + NET_EPOCH_EXIT(et); /* * special cases, like 6to4, are handled in in6_ifattach @@ -1947,26 +1955,14 @@ in6_if_up(struct ifnet *ifp) int in6if_do_dad(struct ifnet *ifp) { + if ((ifp->if_flags & IFF_LOOPBACK) != 0) return (0); - - if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) || - (ND_IFINFO(ifp)->flags & ND6_IFF_NO_DAD)) + if ((ifp->if_flags & IFF_MULTICAST) == 0) + return (0); + if ((ND_IFINFO(ifp)->flags & + (ND6_IFF_IFDISABLED | ND6_IFF_NO_DAD)) != 0) return (0); - - /* - * Our DAD routine requires the interface up and running. - * However, some interfaces can be up before the RUNNING - * status. Additionally, users may try to assign addresses - * before the interface becomes up (or running). - * This function returns EAGAIN in that case. - * The caller should mark "tentative" on the address instead of - * performing DAD immediately. - */ - if (!((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING))) - return (EAGAIN); - return (1); } @@ -1977,10 +1973,11 @@ in6if_do_dad(struct ifnet *ifp) void in6_setmaxmtu(void) { + struct epoch_tracker et; unsigned long maxmtu = 0; struct ifnet *ifp; - IFNET_RLOCK_NOSLEEP(); + NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) @@ -1989,7 +1986,7 @@ in6_setmaxmtu(void) IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } - IFNET_RUNLOCK_NOSLEEP(); + NET_EPOCH_EXIT(et); if (maxmtu) /* update only when maxmtu is positive */ V_in6_maxmtu = maxmtu; } @@ -2167,18 +2164,19 @@ in6_lltable_rtcheck(struct ifnet *ifp, fibnum = V_rt_add_addr_allfibs ? RT_DEFAULT_FIB : ifp->if_fib; error = fib6_lookup_nh_basic(fibnum, &dst, scopeid, 0, 0, &nh6); if (error != 0 || (nh6.nh_flags & NHF_GATEWAY) || nh6.nh_ifp != ifp) { + struct epoch_tracker et; struct ifaddr *ifa; /* * Create an ND6 cache for an IPv6 neighbor * that is not covered by our own prefix. */ - NET_EPOCH_ENTER(); + NET_EPOCH_ENTER(et); ifa = ifaof_ifpforaddr(l3addr, ifp); if (ifa != NULL) { - NET_EPOCH_EXIT(); + NET_EPOCH_EXIT(et); return 0; } - NET_EPOCH_EXIT(); + NET_EPOCH_EXIT(et); log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", ip6_sprintf(ip6buf, &sin6->sin6_addr)); return EINVAL; @@ -2319,16 +2317,13 @@ in6_lltable_lookup(struct lltable *llt, u_int flags, IF_AFDATA_LOCK_ASSERT(llt->llt_ifp); KASSERT(l3addr->sa_family == AF_INET6, ("sin_family %d", l3addr->sa_family)); + KASSERT((flags & (LLE_UNLOCKED | LLE_EXCLUSIVE)) != + (LLE_UNLOCKED | LLE_EXCLUSIVE), + ("wrong lle request flags: %#x", flags)); lle = in6_lltable_find_dst(llt, &sin6->sin6_addr); - if (lle == NULL) return (NULL); - - KASSERT((flags & (LLE_UNLOCKED|LLE_EXCLUSIVE)) != - (LLE_UNLOCKED|LLE_EXCLUSIVE),("wrong lle request flags: 0x%X", - flags)); - if (flags & LLE_UNLOCKED) return (lle); @@ -2336,6 +2331,18 @@ in6_lltable_lookup(struct lltable *llt, u_int flags, LLE_WLOCK(lle); else LLE_RLOCK(lle); + + /* + * If the afdata lock is not held, the LLE may have been unlinked while + * we were blocked on the LLE lock. Check for this case. + */ + if (__predict_false((lle->la_flags & LLE_LINKED) == 0)) { + if (flags & LLE_EXCLUSIVE) + LLE_WUNLOCK(lle); + else + LLE_RUNLOCK(lle); + return (NULL); + } return (lle); } |