commit 2120b82f48585d4c65f185cbf50194f81364a4f1 Author: Ryota Ozaki Date: Tue Jul 26 16:35:37 2016 +0900 Apply psz/psref to ia6 diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 9beb3b9..5442d36 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -160,7 +160,7 @@ static struct mbuf *ni6_input(struct mbuf *, int); static struct mbuf *ni6_nametodns(const char *, int, int); static int ni6_dnsmatch(const char *, int, const char *, int); static int ni6_addrs(struct icmp6_nodeinfo *, struct mbuf *, - struct ifnet **, char *); + struct ifnet **, char *, struct psref *); static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int); static int icmp6_notify_error(struct mbuf *, int, int, int); @@ -1200,8 +1200,10 @@ ni6_input(struct mbuf *m, int off) struct ip6_hdr *ip6; int oldfqdn = 0; /* if 1, return pascal string (03 draft) */ char *subj = NULL; - int s; struct ifnet *rcvif; + int s, ss; + struct ifaddr *ifa; + struct psref psref; ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(ni6, struct icmp6_nodeinfo *, m, off, sizeof(*ni6)); @@ -1220,12 +1222,17 @@ ni6_input(struct mbuf *m, int off) */ sockaddr_in6_init(&sin6, &ip6->ip6_dst, 0, 0, 0); /* XXX scopeid */ - if (ifa_ifwithaddr(sin6tosa(&sin6))) + ss = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sin6tosa(&sin6)); + if (ifa != NULL) ; /* unicast/anycast, fine */ else if (IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) ; /* link-local multicast, fine */ - else + else { + pserialize_read_exit(ss); goto bad; + } + pserialize_read_exit(ss); /* validate query Subject field. */ qtype = ntohs(ni6->ni_qtype); @@ -1359,7 +1366,7 @@ ni6_input(struct mbuf *m, int off) replylen += offsetof(struct ni_reply_fqdn, ni_fqdn_namelen); break; case NI_QTYPE_NODEADDR: - addrs = ni6_addrs(ni6, m, &ifp, subj); + addrs = ni6_addrs(ni6, m, &ifp, subj, &psref); if ((replylen += addrs * (sizeof(struct in6_addr) + sizeof(u_int32_t))) > MCLBYTES) replylen = MCLBYTES; /* XXX: will truncate pkt later */ @@ -1387,6 +1394,7 @@ ni6_input(struct mbuf *m, int off) /* allocate an mbuf to reply. */ MGETHDR(n, M_DONTWAIT, m->m_type); if (n == NULL) { + if_put(ifp, &psref); m_freem(m); return (NULL); } @@ -1454,6 +1462,8 @@ ni6_input(struct mbuf *m, int off) sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); lenlim = M_TRAILINGSPACE(n); copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); + if_put(ifp, &psref); + ifp = NULL; /* XXX: reset mbuf length */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo) + copied; @@ -1468,6 +1478,7 @@ ni6_input(struct mbuf *m, int off) return (n); bad: + if_put(ifp, &psref); m_freem(m); if (n) m_freem(n); @@ -1657,7 +1668,7 @@ ni6_dnsmatch(const char *a, int alen, const char *b, int blen) */ static int ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, - struct ifnet **ifpp, char *subj) + struct ifnet **ifpp, char *subj, struct psref *psref) { struct ifnet *ifp; struct in6_ifaddr *ia6; @@ -1665,6 +1676,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */ int addrs = 0, addrsofif, iffound = 0; int niflags = ni6->ni_flags; + int s; if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { switch (ni6->ni_code) { @@ -1682,6 +1694,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, } } + s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { addrsofif = 0; IFADDR_READER_FOREACH(ifa, ifp) { @@ -1733,12 +1746,15 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, addrsofif++; /* count the address */ } if (iffound) { + if_acquire_NOMPSAFE(ifp, psref); + pserialize_read_exit(s); *ifpp = ifp; return (addrsofif); } addrs += addrsofif; } + pserialize_read_exit(s); return (addrs); } @@ -1748,7 +1764,7 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, struct icmp6_nodeinfo *nni6, struct ifnet *ifp0, int resid) { - struct ifnet *ifp = ifp0 ? ifp0 : IFNET_READER_FIRST(); + struct ifnet *ifp; struct in6_ifaddr *ia6; struct ifaddr *ifa; struct ifnet *ifp_dep = NULL; @@ -1756,10 +1772,13 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, u_char *cp = (u_char *)(nni6 + 1); int niflags = ni6->ni_flags; u_int32_t ltime; + int s; if (ifp0 == NULL && !(niflags & NI_NODEADDR_FLAG_ALL)) return (0); /* needless to copy */ + s = pserialize_read_enter(); + ifp = ifp0 ? ifp0 : IFNET_READER_FIRST(); again: for (; ifp; ifp = IFNET_READER_NEXT(ifp)) @@ -1820,7 +1839,7 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, * Set the truncate flag and return. */ nni6->ni_flags |= NI_NODEADDR_FLAG_TRUNCATE; - return (copied); + goto out; } /* @@ -1876,7 +1895,8 @@ ni6_store_addrs(struct icmp6_nodeinfo *ni6, goto again; } - +out: + pserialize_read_exit(s); return (copied); } @@ -2067,16 +2087,21 @@ icmp6_reflect(struct mbuf *m, size_t off) struct sockaddr_in6 sin6; struct sockaddr sa; } u; + int _s; + struct ifaddr *ifa; sockaddr_in6_init(&u.sin6, &origdst, 0, 0, 0); - ia = ifatoia6(ifa_ifwithaddr(&u.sa)); + _s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(&u.sa); - if (ia == NULL) - ; - else if ((ia->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) - src = &ia->ia_addr.sin6_addr; + if (ifa != NULL) { + ia = ifatoia6(ifa); + if ((ia->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) + src = &ia->ia_addr.sin6_addr; + } + pserialize_read_exit(_s); } if (src == NULL) { @@ -2438,11 +2463,15 @@ icmp6_redirect_output(struct mbuf *m0, struct rtentry *rt) { /* get ip6 linklocal address for ifp(my outgoing interface). */ struct in6_ifaddr *ia; + int s = pserialize_read_enter(); if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY| - IN6_IFF_ANYCAST)) == NULL) + IN6_IFF_ANYCAST)) == NULL) { + pserialize_read_exit(s); goto fail; + } ifp_ll6 = &ia->ia_addr.sin6_addr; + pserialize_read_exit(s); } /* get ip6 linklocal address for the router. */ diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index eaaad1b..f61235a 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -143,6 +143,7 @@ const struct sockaddr_in6 sa6_any = {sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0}; struct pslist_head in6_ifaddr_list; +kmutex_t in6_ifaddr_lock; static int in6_lifaddr_ioctl(struct socket *, u_long, void *, struct ifnet *); @@ -155,6 +156,7 @@ in6_init(void) { PSLIST_INIT(&in6_ifaddr_list); + mutex_init(&in6_ifaddr_lock, MUTEX_DEFAULT, IPL_NONE); } /* @@ -189,6 +191,8 @@ in6_ifremlocal(struct ifaddr *ifa) struct in6_ifaddr *ia; struct ifaddr *alt_ifa = NULL; int ia_count = 0; + struct psref psref; + int s; /* * Some of BSD variants do not remove cloned routes @@ -218,6 +222,7 @@ in6_ifremlocal(struct ifaddr *ifa) * XXX agree, especially now that I have fixed the dangling * XXX ifp-pointers bug. */ + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { if (!IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia->ia_addr.sin6_addr)) continue; @@ -226,11 +231,17 @@ in6_ifremlocal(struct ifaddr *ifa) if (++ia_count > 1 && alt_ifa != NULL) break; } + if (ia_count > 1 && alt_ifa != NULL) + ifa_acquire(alt_ifa, &psref); + pserialize_read_exit(s); if (ia_count == 0) return; rt_ifa_remlocal(ifa, ia_count == 1 ? NULL : alt_ifa); + + if (ia_count > 1 && alt_ifa != NULL) + ifa_release(alt_ifa, &psref); } int @@ -279,7 +290,9 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) struct in6_ifaddr *ia = NULL; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct sockaddr_in6 *sa6; - int error; + int error, bound; + struct psref psref; + bool run_hooks = false; switch (cmd) { case SIOCAADDRCTL_POLICY: @@ -395,14 +408,17 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) sa6 = NULL; break; } + + error = 0; + bound = curlwp_bind(); if (sa6 && sa6->sin6_family == AF_INET6) { if (sa6->sin6_scope_id != 0) error = sa6_embedscope(sa6, 0); else error = in6_setscope(&sa6->sin6_addr, ifp, NULL); if (error != 0) - return error; - ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); + goto out; + ia = in6ifa_ifpwithaddr_psref(ifp, &sa6->sin6_addr, &psref); } else ia = NULL; @@ -414,7 +430,8 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * Since IPv6 allows a node to assign multiple addresses * on a single interface, SIOCSIFxxx ioctls are deprecated. */ - return EINVAL; + error = EINVAL; + goto release; case SIOCDIFADDR_IN6: /* @@ -424,8 +441,10 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * interface address from the day one, we consider "remove the * first one" semantics to be not preferable. */ - if (ia == NULL) - return EADDRNOTAVAIL; + if (ia == NULL) { + error = EADDRNOTAVAIL; + goto out; + } /* FALLTHROUGH */ #ifdef OSIOCAIFADDR_IN6 case OSIOCAIFADDR_IN6: @@ -436,8 +455,10 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * the corresponding operation. */ if (ifra->ifra_addr.sin6_family != AF_INET6 || - ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) - return EAFNOSUPPORT; + ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) { + error = EAFNOSUPPORT; + goto release; + } /* Privileged. */ break; @@ -453,8 +474,10 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) case OSIOCGIFALIFETIME_IN6: #endif /* must think again about its semantics */ - if (ia == NULL) - return EADDRNOTAVAIL; + if (ia == NULL) { + error = EADDRNOTAVAIL; + goto out; + } break; } @@ -463,19 +486,21 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0) - return error; + break; break; case SIOCGIFDSTADDR_IN6: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return EINVAL; + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + error = EINVAL; + break; + } /* * XXX: should we check if ifa_dstaddr is NULL and return * an error? */ ifr->ifr_dstaddr = ia->ia_dstaddr; if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0) - return error; + break; break; case SIOCGIFNETMASK_IN6: @@ -487,8 +512,10 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCGIFSTAT_IN6: - if (ifp == NULL) - return EINVAL; + if (ifp == NULL) { + error = EINVAL; + break; + } memset(&ifr->ifr_ifru.ifru_stat, 0, sizeof(ifr->ifr_ifru.ifru_stat)); ifr->ifr_ifru.ifru_stat = @@ -496,8 +523,10 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCGIFSTAT_ICMP6: - if (ifp == NULL) - return EINVAL; + if (ifp == NULL) { + error = EINVAL; + break; + } memset(&ifr->ifr_ifru.ifru_icmp6stat, 0, sizeof(ifr->ifr_ifru.ifru_icmp6stat)); ifr->ifr_ifru.ifru_icmp6stat = @@ -569,6 +598,8 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) struct nd_prefixctl prc0; struct nd_prefix *pr; struct in6_addrlifetime *lt; + struct in6_ifaddr *_ia; + int s; /* reject read-only flags */ if ((ifra->ifra_flags & IN6_IFF_DUPLICATED) != 0 || @@ -576,7 +607,8 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) (ifra->ifra_flags & IN6_IFF_TENTATIVE) != 0 || (ifra->ifra_flags & IN6_IFF_NODAD) != 0 || (ifra->ifra_flags & IN6_IFF_AUTOCONF) != 0) { - return EINVAL; + error = EINVAL; + break; } /* * ia6t_expire and ia6t_preferred won't be used for now, @@ -589,19 +621,31 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) lt->ia6t_preferred = time_wall_to_mono(lt->ia6t_preferred); /* - * first, make or update the interface address structure, - * and link it to the list. + * first, make (ia == NULL) or update (ia != NULL) the interface + * address structure, and link it to the list. */ if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0) - return error; - if ((ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr)) - == NULL) { + break; + s = pserialize_read_enter(); + _ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); + if (_ia == NULL) { + pserialize_read_exit(s); /* * this can happen when the user specify the 0 valid * lifetime. */ break; } + /* + * If ia == NULL, _ia has been created and we need to acquire + * a reference. Otherwise, a reference is already taken. + */ + if (ia == NULL) { + ia6_acquire(_ia, &psref); + ia = _ia; + } else + KASSERT(_ia == ia); + pserialize_read_exit(s); /* * then, make the prefix on-link on the interface. @@ -647,11 +691,12 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * interface route. */ if ((error = nd6_prelist_add(&prc0, NULL, &pr)) != 0) - return error; + break; if (pr == NULL) { log(LOG_ERR, "nd6_prelist_add succeeded but " "no prefix\n"); - return EINVAL; /* XXX panic here? */ + error = EINVAL; /* XXX panic here? */ + break; } } @@ -682,8 +727,7 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) */ pfxlist_onlink_check(); - (void)pfil_run_hooks(if_pfil, (struct mbuf **)SIOCAIFADDR_IN6, - ifp, PFIL_IFADDR); + run_hooks = true; break; } @@ -702,19 +746,26 @@ in6_control1(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * Note that in6_purgeaddr() will decrement ndpr_refcnt. */ pr = ia->ia6_ndpr; + ia6_release(ia, &psref); in6_purgeaddr(&ia->ia_ifa); + ia = NULL; if (pr && pr->ndpr_refcnt == 0) prelist_remove(pr); - (void)pfil_run_hooks(if_pfil, (struct mbuf **)SIOCDIFADDR_IN6, - ifp, PFIL_IFADDR); + run_hooks = true; break; } default: - return ENOTTY; + error = ENOTTY; } +release: + ia6_release(ia, &psref); - return 0; + if (run_hooks) + pfil_run_hooks(if_pfil, (struct mbuf **)cmd, ifp, PFIL_IFADDR); +out: + curlwp_bindx(bound); + return error; } int @@ -1037,7 +1088,9 @@ in6_update_ifa1(struct ifnet *ifp, struct in6_aliasreq *ifra, /* * Insert ia to the global list and ifa to the interface's list. */ + mutex_enter(&in6_ifaddr_lock); IN6_ADDRLIST_WRITER_INSERT_TAIL(ia); + mutex_exit(&in6_ifaddr_lock); /* gain a refcnt for the link from in6_ifaddr */ ifaref(&ia->ia_ifa); @@ -1346,11 +1399,10 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) { int s = splnet(); - ifa_remove(ifp, &ia->ia_ifa); - + mutex_enter(&in6_ifaddr_lock); IN6_ADDRLIST_WRITER_REMOVE(ia); - /* TODO psref_target_destroy */ - IN6_ADDRLIST_ENTRY_DESTROY(ia); + ifa_remove(ifp, &ia->ia_ifa); + mutex_exit(&in6_ifaddr_lock); /* * XXX thorpej@NetBSD.org -- if the interface is going @@ -1389,6 +1441,8 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp) if ((ia->ia6_flags & IN6_IFF_AUTOCONF) != 0) pfxlist_onlink_check(); + IN6_ADDRLIST_ENTRY_DESTROY(ia); + /* * release another refcnt for the link from in6_ifaddr. * Note that we should decrement the refcnt at least once for all *BSD. @@ -1481,6 +1535,8 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, struct in6_aliasreq ifra; struct in6_addr *xhostid = NULL; int prefixlen; + int bound = curlwp_bind(); + struct psref psref; if ((iflr->flags & IFLR_PREFIX) != 0) { struct sockaddr_in6 *sin6; @@ -1490,20 +1546,27 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, * address. xhostid points to the first link-local * address attached to the interface. */ - ia = in6ifa_ifpforlinklocal(ifp, 0); - if (ia == NULL) + ia = in6ifa_ifpforlinklocal_psref(ifp, 0, &psref); + if (ia == NULL) { + curlwp_bindx(bound); return EADDRNOTAVAIL; + } xhostid = IFA_IN6(&ia->ia_ifa); /* prefixlen must be <= 64. */ - if (64 < iflr->prefixlen) + if (64 < iflr->prefixlen) { + ia6_release(ia, &psref); + curlwp_bindx(bound); return EINVAL; + } prefixlen = iflr->prefixlen; /* hostid part must be zero. */ sin6 = (struct sockaddr_in6 *)&iflr->addr; if (sin6->sin6_addr.s6_addr32[2] != 0 || sin6->sin6_addr.s6_addr32[3] != 0) { + ia6_release(ia, &psref); + curlwp_bindx(bound); return EINVAL; } } else @@ -1533,6 +1596,11 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, xhostid->s6_addr32[3]; } } + if (xhostid) { + ia6_release(ia, &psref); + ia = NULL; + } + curlwp_bindx(bound); ifra.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6); in6_prefixlen2mask(&ifra.ifra_prefixmask.sin6_addr, prefixlen); @@ -1548,6 +1616,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, struct in6_addr mask, candidate, match; struct sockaddr_in6 *sin6; int cmp; + int error, s; memset(&mask, 0, sizeof(mask)); if (iflr->flags & IFLR_PREFIX) { @@ -1580,6 +1649,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, } } + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1600,19 +1670,19 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, if (IN6_ARE_ADDR_EQUAL(&candidate, &match)) break; } - if (!ifa) - return EADDRNOTAVAIL; + if (!ifa) { + error = EADDRNOTAVAIL; + goto error; + } ia = ifa2ia6(ifa); if (cmd == SIOCGLIFADDR) { - int error; - /* fill in the if_laddrreq structure */ memcpy(&iflr->addr, &ia->ia_addr, ia->ia_addr.sin6_len); error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->addr); if (error != 0) - return error; + goto error; if ((ifp->if_flags & IFF_POINTOPOINT) != 0) { memcpy(&iflr->dstaddr, &ia->ia_dstaddr, @@ -1620,7 +1690,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, error = sa6_recoverscope( (struct sockaddr_in6 *)&iflr->dstaddr); if (error != 0) - return error; + goto error; } else memset(&iflr->dstaddr, 0, sizeof(iflr->dstaddr)); @@ -1629,7 +1699,7 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, iflr->flags = ia->ia6_flags; /* XXX */ - return 0; + error = 0; } else { struct in6_aliasreq ifra; @@ -1651,8 +1721,13 @@ in6_lifaddr_ioctl(struct socket *so, u_long cmd, void *data, ia->ia_prefixmask.sin6_len); ifra.ifra_flags = ia->ia6_flags; + pserialize_read_exit(s); + return in6_control(so, SIOCDIFADDR_IN6, &ifra, ifp); } + error: + pserialize_read_exit(s); + return error; } } @@ -1757,6 +1832,21 @@ in6ifa_ifpforlinklocal(const struct ifnet *ifp, const int ignoreflags) return (struct in6_ifaddr *)best_ifa; } +struct in6_ifaddr * +in6ifa_ifpforlinklocal_psref(const struct ifnet *ifp, const int ignoreflags, + struct psref *psref) +{ + struct in6_ifaddr *ia; + int s = pserialize_read_enter(); + + ia = in6ifa_ifpforlinklocal(ifp, ignoreflags); + if (ia != NULL) + ia6_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} + /* * find the internet address corresponding to a given address. * ifaddr is returned referenced. @@ -1765,13 +1855,10 @@ struct in6_ifaddr * in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) { struct in6_ifaddr *ia; + int s; -#ifdef __FreeBSD__ - IN6_IFADDR_RLOCK(); - LIST_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) { -#else + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { -#endif if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), addr)) { if (zoneid != 0 && zoneid != ia->ia_addr.sin6_scope_id) @@ -1780,9 +1867,8 @@ in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) break; } } -#ifdef __FreeBSD__ - IN6_IFADDR_RUNLOCK(); -#endif + pserialize_read_exit(s); + return ia; } @@ -1805,6 +1891,21 @@ in6ifa_ifpwithaddr(const struct ifnet *ifp, const struct in6_addr *addr) return (struct in6_ifaddr *)best_ifa; } +struct in6_ifaddr * +in6ifa_ifpwithaddr_psref(const struct ifnet *ifp, const struct in6_addr *addr, + struct psref *psref) +{ + struct in6_ifaddr *ia; + int s = pserialize_read_enter(); + + ia = in6ifa_ifpwithaddr(ifp, addr); + if (ia != NULL) + ia6_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} + static struct in6_ifaddr * bestia(struct in6_ifaddr *best_ia, struct in6_ifaddr *ia) { @@ -2044,6 +2145,7 @@ in6_if_link_up(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; + int s, bound; /* Ensure it's sane to run DAD */ if (ifp->if_link_state == LINK_STATE_DOWN) @@ -2051,9 +2153,16 @@ in6_if_link_up(struct ifnet *ifp) if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) return; + bound = curlwp_bind(); + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref; + if (ifa->ifa_addr->sa_family != AF_INET6) continue; + + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); ia = (struct in6_ifaddr *)ifa; /* If detached then mark as tentative */ @@ -2084,7 +2193,12 @@ in6_if_link_up(struct ifnet *ifp) /* +1 ensures callout is always used */ nd6_dad_start(ifa, rand_delay + 1); } + + s = pserialize_read_enter(); + ifa_release(ifa, &psref); } + pserialize_read_exit(s); + curlwp_bindx(bound); /* Restore any detached prefixes */ pfxlist_onlink_check(); @@ -2111,13 +2225,21 @@ in6_if_link_down(struct ifnet *ifp) { struct ifaddr *ifa; struct in6_ifaddr *ia; + int s, bound; /* Any prefixes on this interface should be detached as well */ pfxlist_onlink_check(); + bound = curlwp_bind(); + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref; + if (ifa->ifa_addr->sa_family != AF_INET6) continue; + + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); ia = (struct in6_ifaddr *)ifa; /* Stop DAD processing */ @@ -2139,7 +2261,12 @@ in6_if_link_down(struct ifnet *ifp) ~(IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED); rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL); } + + s = pserialize_read_enter(); + ifa_release(ifa, &psref); } + pserialize_read_exit(s); + curlwp_bindx(bound); } void @@ -2320,18 +2447,22 @@ in6_lltable_rtcheck(struct ifnet *ifp, rt = rtalloc1(l3addr, 0); if (rt == NULL || (rt->rt_flags & RTF_GATEWAY) || rt->rt_ifp != ifp) { + int s; struct ifaddr *ifa; /* * Create an ND6 cache for an IPv6 neighbor * that is not covered by our own prefix. */ /* XXX ifaof_ifpforaddr should take a const param */ + s = pserialize_read_enter(); ifa = ifaof_ifpforaddr(l3addr, ifp); if (ifa != NULL) { + pserialize_read_exit(s); if (rt != NULL) rtfree(rt); return 0; } + pserialize_read_exit(s); log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", ip6_sprintf(&((const struct sockaddr_in6 *)l3addr)->sin6_addr)); if (rt != NULL) diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 932dc64..206e312 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -320,31 +320,35 @@ int in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6) { struct ifaddr *ifa; - const struct sockaddr_dl *sdl = NULL, *tsdl; + const struct sockaddr_dl *sdl = NULL; const char *addr; size_t addrlen; static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static u_int8_t allone[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int s; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { + const struct sockaddr_dl *tsdl; if (ifa->ifa_addr->sa_family != AF_LINK) continue; tsdl = satocsdl(ifa->ifa_addr); if (tsdl == NULL || tsdl->sdl_alen == 0) continue; - if (sdl == NULL || ifa == ifp->if_dl || ifa == ifp->if_hwdl) + if (sdl == NULL || ifa == ifp->if_dl || ifa == ifp->if_hwdl) { sdl = tsdl; + addr = CLLADDR(sdl); + addrlen = sdl->sdl_alen; + } if (ifa == ifp->if_hwdl) break; } + pserialize_read_exit(s); if (sdl == NULL) return -1; - addr = CLLADDR(sdl); - addrlen = sdl->sdl_alen; - switch (ifp->if_type) { case IFT_IEEE1394: case IFT_IEEE80211: @@ -532,7 +536,7 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp) struct in6_ifaddr *ia __diagused; struct in6_aliasreq ifra; struct nd_prefixctl prc0; - int i, error; + int i, error, s; /* * configure link-local address. @@ -589,8 +593,10 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp) return -1; } + s = pserialize_read_enter(); ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ KASSERTMSG(ia, "ia == NULL in in6_ifattach_linklocal"); + pserialize_read_exit(s); /* * Make the link-local prefix (fe80::/64%link) as on-link. @@ -811,24 +817,29 @@ in6_ifattach(struct ifnet *ifp, struct ifnet *altifp) * XXX multiple loopback interface case. */ if ((ifp->if_flags & IFF_LOOPBACK) != 0) { + int s = pserialize_read_enter(); in6 = in6addr_loopback; if (in6ifa_ifpwithaddr(ifp, &in6) == NULL) { - if (in6_ifattach_loopback(ifp) != 0) + if (in6_ifattach_loopback(ifp) != 0) { + pserialize_read_exit(s); return; + } } + pserialize_read_exit(s); } /* * assign a link-local address, if there's none. */ if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) && - ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) - { + ND_IFINFO(ifp)->flags & ND6_IFF_AUTO_LINKLOCAL) { + int s = pserialize_read_enter(); ia = in6ifa_ifpforlinklocal(ifp, 0); if (ia == NULL && in6_ifattach_linklocal(ifp, altifp) != 0) { printf("%s: cannot assign link-local address\n", ifp->if_xname); } + pserialize_read_exit(s); } } diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 0aa3d4d..8fc19a3 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -203,6 +203,7 @@ static int in6_pcbbind_addr(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) { int error; + int s; /* * We should check the family, but old programs @@ -219,9 +220,12 @@ in6_pcbbind_addr(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return (error); + s = pserialize_read_enter(); if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { - if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) - return (EINVAL); + if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) { + error = EINVAL; + goto out; + } if (sin6->sin6_addr.s6_addr32[3]) { struct sockaddr_in sin; @@ -230,18 +234,27 @@ in6_pcbbind_addr(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) sin.sin_family = AF_INET; bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr, sizeof(sin.sin_addr)); - if (!IN_MULTICAST(sin.sin_addr.s_addr) && - ifa_ifwithaddr((struct sockaddr *)&sin) == NULL) - return EADDRNOTAVAIL; + if (!IN_MULTICAST(sin.sin_addr.s_addr)) { + struct ifaddr *ifa; + ifa = ifa_ifwithaddr((struct sockaddr *)&sin); + if (ifa == NULL) { + error = EADDRNOTAVAIL; + goto out; + } + } } } else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { // succeed } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { - struct ifaddr *ia = NULL; + struct ifaddr *ifa = NULL; - if ((in6p->in6p_flags & IN6P_FAITH) == 0 && - (ia = ifa_ifwithaddr(sin6tosa(sin6))) == NULL) - return (EADDRNOTAVAIL); + if ((in6p->in6p_flags & IN6P_FAITH) == 0) { + ifa = ifa_ifwithaddr(sin6tosa(sin6)); + if (ifa == NULL) { + error = EADDRNOTAVAIL; + goto out; + } + } /* * bind to an anycast address might accidentally @@ -256,17 +269,18 @@ in6_pcbbind_addr(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) * flag to control the bind(2) behavior against * deprecated addresses (default: forbid bind(2)). */ - if (ia && - ifatoia6(ia)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) - return (EADDRNOTAVAIL); + if (ifa && + ifatoia6(ifa)->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) { + error = EADDRNOTAVAIL; + goto out; + } } - - in6p->in6p_laddr = sin6->sin6_addr; - - - return (0); + error = 0; +out: + pserialize_read_exit(s); + return error; } /* diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index 8a7055f..71ed583 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -192,6 +192,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, struct in6_addr *ret_ia = NULL; int bound = curlwp_bind(); #define PSREF (psref == NULL) ? &local_psref : psref + int s; KASSERT((ifpp != NULL && psref != NULL) || (ifpp == NULL && psref == NULL)); @@ -222,6 +223,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) { struct sockaddr_in6 srcsock; struct in6_ifaddr *ia6; + int _s; + struct ifaddr *ifa; /* * Determine the appropriate zone id of the source based on @@ -240,9 +243,16 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, goto exit; } - ia6 = ifatoia6(ifa_ifwithaddr(sin6tosa(&srcsock))); - if (ia6 == NULL || - (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) { + _s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sin6tosa(&srcsock)); + if (ifa == NULL) { + pserialize_read_exit(_s); + *errorp = EADDRNOTAVAIL; + goto exit; + } + ia6 = ifatoia6(ifa); + if (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)) { + pserialize_read_exit(_s); *errorp = EADDRNOTAVAIL; goto exit; } @@ -250,6 +260,8 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, if (ifpp) *ifpp = ifp; ret_ia = &ia6->ia_addr.sin6_addr; + pserialize_read_exit(_s); + /* XXX don't return pointer */ goto exit; } @@ -293,6 +305,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, if (*errorp != 0) goto exit; + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { int new_scope = -1, new_matchlen = -1; struct in6_addrpolicy *new_policy = NULL; @@ -551,6 +564,7 @@ in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, out: break; } + pserialize_read_exit(s); if ((ia = ia_best) == NULL) { *errorp = EADDRNOTAVAIL; diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index 9aff491..100f178 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -134,6 +134,25 @@ struct in6_ifaddr { #endif }; +#ifdef _KERNEL +static inline void +ia6_acquire(struct in6_ifaddr *ia, struct psref *psref) +{ + + KASSERT(ia != NULL); + ifa_acquire(&ia->ia_ifa, psref); +} + +static inline void +ia6_release(struct in6_ifaddr *ia, struct psref *psref) +{ + + if (ia == NULL) + return; + ifa_release(&ia->ia_ifa, psref); +} +#endif + /* control structure to manage address selection policy */ struct in6_addrpolicy { struct sockaddr_in6 addr; /* prefix address */ @@ -489,13 +508,17 @@ struct in6_rrenumreq { #ifdef _KERNEL +#include +#include + #include extern pktqueue_t *ip6_pktq; MALLOC_DECLARE(M_IP6OPT); -extern struct pslist_head in6_ifaddr_list; +extern struct pslist_head in6_ifaddr_list; +extern kmutex_t in6_ifaddr_lock; #define IN6_ADDRLIST_ENTRY_INIT(__ia) \ PSLIST_ENTRY_INIT((__ia), ia6_pslist_entry) @@ -573,6 +596,20 @@ in6_get_ia_from_ifp(struct ifnet *ifp) return (struct in6_ifaddr *)ifa; } +static inline struct in6_ifaddr * +in6_get_ia_from_ifp_psref(struct ifnet *ifp, struct psref *psref) +{ + struct in6_ifaddr *ia; + int s; + + s = pserialize_read_enter(); + ia = in6_get_ia_from_ifp(ifp); + if (ia != NULL) + ia6_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} #endif /* _KERNEL */ /* @@ -623,13 +660,18 @@ in6_lookup_multi(struct in6_addr *addr, struct ifnet *ifp) { struct in6_multi *in6m; struct in6_ifaddr *ia; + int s; - if ((ia = in6_get_ia_from_ifp(ifp)) == NULL) + s = pserialize_read_enter(); + if ((ia = in6_get_ia_from_ifp(ifp)) == NULL) { + pserialize_read_exit(s); return NULL; + } LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) { if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, addr)) break; } + pserialize_read_exit(s); return in6m; } @@ -659,6 +701,7 @@ in6_next_multi(struct in6_multistep *step) } while (step->i_ia != NULL) { in6m = LIST_FIRST(&step->i_ia->ia6_multiaddrs); + /* FIXME NOMPSAFE */ step->i_ia = IN6_ADDRLIST_READER_NEXT(step->i_ia); if (in6m != NULL) { step->i_in6m = LIST_NEXT(in6m, in6m_entry); @@ -672,6 +715,7 @@ static inline struct in6_multi * in6_first_multi(struct in6_multistep *step) { + /* FIXME NOMPSAFE */ step->i_ia = IN6_ADDRLIST_READER_FIRST(); step->i_in6m = NULL; return in6_next_multi(step); @@ -748,9 +792,15 @@ void in6_ifremlocal(struct ifaddr *); void in6_ifaddlocal(struct ifaddr *); void in6_createmkludge(struct ifnet *); void in6_purgemkludge(struct ifnet *); -struct in6_ifaddr *in6ifa_ifpforlinklocal(const struct ifnet *, int); -struct in6_ifaddr *in6ifa_ifpwithaddr(const struct ifnet *, - const struct in6_addr *); +struct in6_ifaddr * + in6ifa_ifpforlinklocal(const struct ifnet *, int); +struct in6_ifaddr * + in6ifa_ifpforlinklocal_psref(const struct ifnet *, int, struct psref *); +struct in6_ifaddr * + in6ifa_ifpwithaddr(const struct ifnet *, const struct in6_addr *); +struct in6_ifaddr * + in6ifa_ifpwithaddr_psref(const struct ifnet *, const struct in6_addr *, + struct psref *); struct in6_ifaddr *in6ifa_ifwithaddr(const struct in6_addr *, uint32_t); char *ip6_sprintf(const struct in6_addr *); int in6_matchlen(struct in6_addr *, struct in6_addr *); diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index d14589e..0655481 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -565,8 +565,10 @@ ip6_input(struct mbuf *m, struct ifnet *rcvif) */ if (deliverifp && ip6_getdstifaddr(m) == NULL) { struct in6_ifaddr *ia6; + int s = pserialize_read_enter(); ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); + /* Depends on ip6_setdstifaddr never sleep */ if (ia6 != NULL && ip6_setdstifaddr(m, ia6) == NULL) { /* * XXX maybe we should drop the packet here, @@ -574,6 +576,7 @@ ip6_input(struct mbuf *m, struct ifnet *rcvif) * to the upper layers. */ } + pserialize_read_exit(s); } /* @@ -701,9 +704,11 @@ ip6_input(struct mbuf *m, struct ifnet *rcvif) #ifdef IFA_STATS if (deliverifp != NULL) { struct in6_ifaddr *ia6; + int s = pserialize_read_enter(); ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); if (ia6) ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len; + pserialize_read_exit(s); } #endif IP6_STATINC(IP6_STAT_DELIVERED); diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 764be7a..4f074ef 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -764,13 +764,16 @@ ip6_output( /* case 1-a and 2-a */ struct in6_ifaddr *ia6; int sw_csum; + int s; ip6 = mtod(m, struct ip6_hdr *); + s = pserialize_read_enter(); ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); if (ia6) { /* Record statistics for this interface address. */ ia6->ia_ifa.ifa_data.ifad_outbytes += m->m_pkthdr.len; } + pserialize_read_exit(s); sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_csum_flags_tx; if ((sw_csum & (M_CSUM_UDPv6|M_CSUM_TCPv6)) != 0) { @@ -956,7 +959,9 @@ sendorfree: m->m_nextpkt = 0; if (error == 0) { struct in6_ifaddr *ia6; + int s; ip6 = mtod(m, struct ip6_hdr *); + s = pserialize_read_enter(); ia6 = in6_ifawithifp(ifp, &ip6->ip6_src); if (ia6) { /* @@ -966,6 +971,7 @@ sendorfree: ia6->ia_ifa.ifa_data.ifad_outbytes += m->m_pkthdr.len; } + pserialize_read_exit(s); KASSERT(dst != NULL); error = nd6_output(ifp, origifp, m, dst, rt); } else diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 356008b..4476ff7 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -402,7 +402,9 @@ mld_input(struct mbuf *m, int off) * if we sent the last report. */ switch (mldh->mld_type) { - case MLD_LISTENER_QUERY: + case MLD_LISTENER_QUERY: { + struct psref psref; + if (ifp->if_flags & IFF_LOOPBACK) break; @@ -428,10 +430,14 @@ mld_input(struct mbuf *m, int off) */ timer = ntohs(mldh->mld_maxdelay); - ia = in6_get_ia_from_ifp(ifp); + ia = in6_get_ia_from_ifp_psref(ifp, &psref); if (ia == NULL) break; + /* The following operations may sleep */ + m_put_rcvif(ifp, &s); + ifp = NULL; + LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) { if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) || IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < @@ -457,7 +463,9 @@ mld_input(struct mbuf *m, int off) mld_starttimer(in6m); } } + ia6_release(ia, &psref); break; + } case MLD_LISTENER_REPORT: /* @@ -514,6 +522,8 @@ mld_sendpkt(struct in6_multi *in6m, int type, struct in6_ifaddr *ia = NULL; struct ifnet *ifp = in6m->in6m_ifp; int ignflags; + struct psref psref; + int bound; /* * At first, find a link local address on the outgoing interface @@ -522,20 +532,31 @@ mld_sendpkt(struct in6_multi *in6m, int type, * the case where we first join a link-local address. */ ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE; - if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) + bound = curlwp_bind(); + ia = in6ifa_ifpforlinklocal_psref(ifp, ignflags, &psref); + if (ia == NULL) { + curlwp_bindx(bound); return; - if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) + } + if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) { + ia6_release(ia, &psref); ia = NULL; + } /* Allocate two mbufs to store IPv6 header and MLD header */ mldh = mld_allocbuf(&mh, sizeof(struct mld_hdr), in6m, type); - if (mldh == NULL) + if (mldh == NULL) { + ia6_release(ia, &psref); + curlwp_bindx(bound); return; + } /* fill src/dst here */ ip6 = mtod(mh, struct ip6_hdr *); ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any; ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; + ia6_release(ia, &psref); + curlwp_bindx(bound); mldh->mld_addr = in6m->in6m_addr; in6_clearscope(&mldh->mld_addr); /* XXX */ @@ -645,6 +666,7 @@ in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, */ in6m->in6m_refcount++; } else { + int _s; /* * New address; allocate a new multicast record * and link it into the interface's multicast list. @@ -664,8 +686,10 @@ in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, callout_init(&in6m->in6m_timer_ch, CALLOUT_MPSAFE); callout_setfunc(&in6m->in6m_timer_ch, mld_timeo, in6m); + _s = pserialize_read_enter(); ia = in6_get_ia_from_ifp(ifp); if (ia == NULL) { + pserialize_read_exit(_s); callout_destroy(&in6m->in6m_timer_ch); free(in6m, M_IPMADDR); splx(s); @@ -674,7 +698,9 @@ in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp, } in6m->in6m_ia = ia; ifaref(&ia->ia_ifa); /* gain a reference */ + /* FIXME NOMPSAFE: need to lock */ LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); + pserialize_read_exit(_s); /* * Ask the network driver to update its multicast reception @@ -816,7 +842,9 @@ in6_savemkludge(struct in6_ifaddr *oia) { struct in6_ifaddr *ia; struct in6_multi *in6m; + int s; + s = pserialize_read_enter(); ia = in6_get_ia_from_ifp(oia->ia_ifp); if (ia) { /* there is another address */ KASSERT(ia != oia); @@ -825,6 +853,7 @@ in6_savemkludge(struct in6_ifaddr *oia) ifaref(&ia->ia_ifa); ifafree(&in6m->in6m_ia->ia_ifa); in6m->in6m_ia = ia; + /* FIXME NOMPSAFE: need to lock */ LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry); } } else { /* last address on this if deleted, save */ @@ -844,6 +873,7 @@ in6_savemkludge(struct in6_ifaddr *oia) LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry); } } + pserialize_read_exit(s); } /* @@ -986,8 +1016,8 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) uint32_t tmp; int error; size_t written; - struct psref psref; - int bound; + struct psref psref, psref_ia; + int bound, s; if (namelen != 1) return EINVAL; @@ -1001,6 +1031,7 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) if (oldp == NULL) { *oldlenp = 0; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1010,6 +1041,7 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) sizeof(uint32_t); } } + pserialize_read_exit(s); if_put(ifp, &psref); curlwp_bindx(bound); return 0; @@ -1017,9 +1049,14 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) error = 0; written = 0; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; + + ifa_acquire(ifa, &psref_ia); + pserialize_read_exit(s); + ifa6 = (struct in6_ifaddr *)ifa; LIST_FOREACH(in6m, &ifa6->ia6_multiaddrs, in6m_entry) { if (written + 2 * sizeof(struct in6_addr) + @@ -1044,8 +1081,13 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) oldp = (char *)oldp + sizeof(tmp); written += sizeof(tmp); } + + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ia); } + pserialize_read_exit(s); done: + ifa_release(ifa, &psref_ia); if_put(ifp, &psref); curlwp_bindx(bound); *oldlenp = written; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index a40ce48..bdc3559 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -571,6 +571,8 @@ nd6_timer_work(struct work *wk, void *arg) struct nd_defrouter *next_dr, *dr; struct nd_prefix *next_pr, *pr; struct in6_ifaddr *ia6, *nia6; + int s, bound; + struct psref psref; callout_reset(&nd6_timer_ch, nd6_prune * hz, nd6_timer, NULL); @@ -592,9 +594,15 @@ nd6_timer_work(struct work *wk, void *arg) * However, from a stricter speci-confrmance standpoint, we should * rather separate address lifetimes and prefix lifetimes. */ + bound = curlwp_bind(); addrloop: - for (ia6 = IN6_ADDRLIST_WRITER_FIRST(); ia6; ia6 = nia6) { - nia6 = IN6_ADDRLIST_WRITER_NEXT(ia6); + s = pserialize_read_enter(); + for (ia6 = IN6_ADDRLIST_READER_FIRST(); ia6; ia6 = nia6) { + nia6 = IN6_ADDRLIST_READER_NEXT(ia6); + + ia6_acquire(ia6, &psref); + pserialize_read_exit(s); + /* check address lifetime */ if (IFA6_IS_INVALID(ia6)) { int regen = 0; @@ -615,7 +623,9 @@ nd6_timer_work(struct work *wk, void *arg) regen = 1; } + ia6_release(ia6, &psref); in6_purgeaddr(&ia6->ia_ifa); + ia6 = NULL; if (regen) goto addrloop; /* XXX: see below */ @@ -649,6 +659,7 @@ nd6_timer_work(struct work *wk, void *arg) * loop just for safety. Or does this * significantly reduce performance?? */ + ia6_release(ia6, &psref); goto addrloop; } } @@ -663,7 +674,11 @@ nd6_timer_work(struct work *wk, void *arg) (struct ifaddr *)ia6, 0, NULL); } } + s = pserialize_read_enter(); + ia6_release(ia6, &psref); } + pserialize_read_exit(s); + curlwp_bindx(bound); /* expire prefix list */ LIST_FOREACH_SAFE(pr, &nd_prefix, ndpr_entry, next_pr) { @@ -702,8 +717,10 @@ regen_tmpaddr(const struct in6_ifaddr *ia6) struct ifaddr *ifa; struct ifnet *ifp; struct in6_ifaddr *public_ifa6 = NULL; + int s; ifp = ia6->ia_ifa.ifa_ifp; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { struct in6_ifaddr *it6; @@ -745,18 +762,24 @@ regen_tmpaddr(const struct in6_ifaddr *ia6) if (public_ifa6 != NULL) { int e; + struct psref psref; + ia6_acquire(public_ifa6, &psref); + pserialize_read_exit(s); /* * Random factor is introduced in the preferred lifetime, so * we do not need additional delay (3rd arg to in6_tmpifadd). */ if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) { + ia6_release(public_ifa6, &psref); log(LOG_NOTICE, "regen_tmpaddr: failed to create a new" " tmp addr, errno=%d\n", e); return -1; } + ia6_release(public_ifa6, &psref); return 0; } + pserialize_read_exit(s); return -1; } @@ -914,6 +937,7 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) { struct nd_prefix *pr; struct ifaddr *dstaddr; + int s; /* * A link-local address is always a neighbor. @@ -979,20 +1003,15 @@ nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) * If the address is assigned on the node of the other side of * a p2p interface, the address should be a neighbor. */ + s = pserialize_read_enter(); dstaddr = ifa_ifwithdstaddr(sin6tocsa(addr)); if (dstaddr != NULL) { if (dstaddr->ifa_ifp == ifp) { -#ifdef __FreeBSD__ - /* XXX we need to ifaref in ifa_ifwithdstaddr as well */ - ifafree(dstaddr); -#endif + pserialize_read_exit(s); return 1; } -#ifdef __FreeBSD__ - /* XXX we need to ifaref in ifa_ifwithdstaddr as well */ - ifafree(dstaddr); -#endif } + pserialize_read_exit(s); /* * If the default router list is empty, all addresses are regarded @@ -1374,7 +1393,9 @@ nd6_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) } switch (req) { - case RTM_ADD: + case RTM_ADD: { + int s; + RT_DPRINTF("rt_getkey(rt) = %p\n", rt_getkey(rt)); /* * There is no backward compatibility :) @@ -1481,6 +1502,7 @@ nd6_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) * check if rt_getkey(rt) is an address assigned * to the interface. */ + s = pserialize_read_enter(); ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &satocsin6(rt_getkey(rt))->sin6_addr); if (ifa != NULL) { @@ -1517,8 +1539,8 @@ nd6_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) } } } - out: + pserialize_read_exit(s); /* * If we have too many cache entries, initiate immediate * purging for some entries. @@ -1526,6 +1548,7 @@ nd6_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) if (rt->rt_ifp != NULL) nd6_gc_neighbors(LLTABLE6(rt->rt_ifp)); break; + } case RTM_DELETE: /* leave from solicited node multicast for proxy ND */ @@ -1709,6 +1732,7 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) */ int duplicated_linklocal = 0; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; @@ -1720,6 +1744,7 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) break; } } + pserialize_read_exit(s); if (duplicated_linklocal) { ND.flags |= ND6_IFF_IFDISABLED; @@ -1732,18 +1757,29 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) in6_if_up(ifp); } } else if (!(ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) && - (ND.flags & ND6_IFF_IFDISABLED)) - { + (ND.flags & ND6_IFF_IFDISABLED)) { + int bound = curlwp_bind(); /* Mark all IPv6 addresses as tentative. */ ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED; + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref; if (ifa->ifa_addr->sa_family != AF_INET6) continue; + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); + nd6_dad_stop(ifa); + ia = (struct in6_ifaddr *)ifa; ia->ia6_flags |= IN6_IFF_TENTATIVE; + + s = pserialize_read_enter(); + ifa_release(ifa, &psref); } + pserialize_read_exit(s); + curlwp_bindx(bound); } if (ND.flags & ND6_IFF_AUTO_LINKLOCAL) { @@ -1761,9 +1797,10 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) * address is assigned, and IFF_UP, try to * assign one. */ - int haslinklocal = 0; + int haslinklocal = 0; - IFADDR_READER_FOREACH(ifa, ifp) { + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family !=AF_INET6) continue; ia = (struct in6_ifaddr *)ifa; @@ -1771,8 +1808,9 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) haslinklocal = 1; break; } - } - if (!haslinklocal) + } + pserialize_read_exit(s); + if (!haslinklocal) in6_ifattach(ifp, NULL); } } @@ -1793,22 +1831,30 @@ nd6_ioctl(u_long cmd, void *data, struct ifnet *ifp) s = splsoftnet(); LIST_FOREACH_SAFE(pfx, &nd_prefix, ndpr_entry, next) { struct in6_ifaddr *ia, *ia_next; + int _s; if (IN6_IS_ADDR_LINKLOCAL(&pfx->ndpr_prefix.sin6_addr)) continue; /* XXX */ /* do we really have to remove addresses as well? */ - for (ia = IN6_ADDRLIST_WRITER_FIRST(); ia; + restart: + _s = pserialize_read_enter(); + for (ia = IN6_ADDRLIST_READER_FIRST(); ia; ia = ia_next) { /* ia might be removed. keep the next ptr. */ - ia_next = IN6_ADDRLIST_WRITER_NEXT(ia); + ia_next = IN6_ADDRLIST_READER_NEXT(ia); if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; - if (ia->ia6_ndpr == pfx) + if (ia->ia6_ndpr == pfx) { + pserialize_read_exit(_s); + /* XXX NOMPSAFE? */ in6_purgeaddr(&ia->ia_ifa); + goto restart; + } } + pserialize_read_exit(_s); prelist_remove(pfx); } splx(s); @@ -2172,6 +2218,7 @@ nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, if (rt != NULL && (rt->rt_flags & RTF_GATEWAY) != 0) { struct sockaddr_in6 *gw6 = satosin6(rt->rt_gateway); + int s; /* XXX remain the check to keep the original behavior. */ /* @@ -2182,6 +2229,7 @@ nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, * if the gateway is our own address, which is * sometimes used to install a route to a p2p link. */ + s = pserialize_read_enter(); if (!nd6_is_addr_neighbor(gw6, ifp) || in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) { /* @@ -2192,8 +2240,10 @@ nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, if ((ifp->if_flags & IFF_POINTOPOINT) == 0) senderr(EHOSTUNREACH); + pserialize_read_exit(s); goto sendpkt; } + pserialize_read_exit(s); } /* diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index 62099a2..479ac31 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -104,7 +104,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) struct in6_addr taddr6; struct in6_addr myaddr6; char *lladdr = NULL; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; int lladdrlen = 0; int anycast = 0, proxy = 0, tentative = 0; int router = ip6_forwarding; @@ -112,6 +112,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) union nd_opts ndopts; const struct sockaddr_dl *proxydl = NULL; struct psref psref; + struct psref psref_ia; ifp = m_get_rcvif_psref(m, &psref); if (ifp == NULL) @@ -216,14 +217,20 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) */ /* (1) and (3) check. */ #if NCARP > 0 - if (ifp->if_carp && ifp->if_type != IFT_CARP) + if (ifp->if_carp && ifp->if_type != IFT_CARP) { + int s = pserialize_read_enter(); ifa = carp_iamatch6(ifp->if_carp, &taddr6); - else + if (ifa != NULL) + ifa_acquire(ifa, &psref_ia); + pserialize_read_exit(s); + } else ifa = NULL; if (!ifa) - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, + &psref_ia); #else - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, + &psref_ia); #endif /* (2) check. */ @@ -239,8 +246,8 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) /* * proxy NDP for single entry */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, - IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal_psref(ifp, + IN6_IFF_NOTREADY|IN6_IFF_ANYCAST, &psref_ia); if (ifa) { proxy = 1; proxydl = satocsdl(rt->rt_gateway); @@ -299,9 +306,13 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) */ if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) nd6_dad_ns_input(ifa); + ifa_release(ifa, &psref_ia); + ifa = NULL; goto freeit; } + ifa_release(ifa, &psref_ia); + ifa = NULL; /* * If the source address is unspecified address, entries must not @@ -331,6 +342,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, tlladdr, (const struct sockaddr *)proxydl); freeit: + ifa_release(ifa, &psref_ia); m_put_rcvif_psref(ifp, &psref); m_freem(m); return; @@ -340,6 +352,7 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) nd6log(LOG_ERR, "dst=%s\n", ip6_sprintf(&daddr6)); nd6log(LOG_ERR, "tgt=%s\n", ip6_sprintf(&taddr6)); ICMP6_STATINC(ICMP6_STAT_BADNS); + ifa_release(ifa, &psref_ia); m_put_rcvif_psref(ifp, &psref); m_freem(m); } @@ -430,6 +443,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, goto bad; } if (!dad) { + int s; /* * RFC2461 7.2.2: * "If the source address of the packet prompting the @@ -445,6 +459,7 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, * - hsrc belongs to the outgoing interface. * Otherwise, we perform the source address selection as usual. */ + s = pserialize_read_enter(); if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc)) src = hsrc; else { @@ -459,9 +474,11 @@ nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, nd6log(LOG_DEBUG, "source can't be " "determined: dst=%s, error=%d\n", ip6_sprintf(&dst_sa.sin6_addr), error); + pserialize_read_exit(s); goto bad; } } + pserialize_read_exit(s); } else { /* * Source address for DAD packet must always be IPv6 @@ -559,6 +576,7 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) int rt_announce; bool checklink = false; struct psref psref; + struct psref psref_ia; ifp = m_get_rcvif_psref(m, &psref); if (ifp == NULL) @@ -613,7 +631,7 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } - ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); + ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, &psref_ia); /* * Target address matches one of my interface address. @@ -627,6 +645,8 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { nd6_dad_na_input(ifa); + ifa_release(ifa, &psref_ia); + ifa = NULL; goto freeit; } @@ -635,6 +655,8 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) log(LOG_ERR, "nd6_na_input: duplicate IP6 address %s\n", ip6_sprintf(&taddr6)); + ifa_release(ifa, &psref_ia); + ifa = NULL; goto freeit; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 63ee03f..4ff457b 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -66,7 +66,7 @@ static int rtpref(struct nd_defrouter *); static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *); static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *, struct mbuf *, int); -static struct in6_ifaddr *in6_ifadd(struct nd_prefixctl *, int); +static struct in6_ifaddr *in6_ifadd(struct nd_prefixctl *, int, struct psref *); static struct nd_pfxrouter *pfxrtr_lookup(struct nd_prefix *, struct nd_defrouter *); static void pfxrtr_add(struct nd_prefix *, struct nd_defrouter *); @@ -898,6 +898,7 @@ purge_detached(struct ifnet *ifp) struct ifaddr *ifa, *ifa_next; for (pr = nd_prefix.lh_first; pr; pr = pr_next) { + int s; pr_next = pr->ndpr_next; /* @@ -913,6 +914,8 @@ purge_detached(struct ifnet *ifp) !LIST_EMPTY(&pr->ndpr_advrtrs))) continue; + restart: + s = pserialize_read_enter(); for (ifa = IFADDR_READER_FIRST(ifp); ifa; ifa = ifa_next) { ifa_next = IFADDR_READER_NEXT(ifa); if (ifa->ifa_addr->sa_family != AF_INET6) @@ -920,9 +923,13 @@ purge_detached(struct ifnet *ifp) ia = (struct in6_ifaddr *)ifa; if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == IN6_IFF_AUTOCONF && ia->ia6_ndpr == pr) { + pserialize_read_exit(s); in6_purgeaddr(ifa); + goto restart; } } + pserialize_read_exit(s); + if (pr->ndpr_refcnt == 0) prelist_remove(pr); } @@ -1065,6 +1072,7 @@ prelist_update(struct nd_prefixctl *newprc, int error = 0; int auth; struct in6_addrlifetime lt6_tmp; + int ss; auth = 0; if (m) { @@ -1187,6 +1195,7 @@ prelist_update(struct nd_prefixctl *newprc, * consider autoconfigured addresses while RFC2462 simply said * "address". */ + ss = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { struct in6_ifaddr *ia6; u_int32_t remaininglifetime; @@ -1308,9 +1317,12 @@ prelist_update(struct nd_prefixctl *newprc, ia6->ia6_lifetime = lt6_tmp; ia6->ia6_updatetime = time_uptime; } + pserialize_read_exit(ss); + if (ia6_match == NULL && newprc->ndprc_vltime) { int ifidlen; struct in6_ifaddr *ia6; + struct psref psref; /* * 5.5.3 (d) (continued) @@ -1340,7 +1352,7 @@ prelist_update(struct nd_prefixctl *newprc, goto end; } - if ((ia6 = in6_ifadd(newprc, mcast)) != NULL) { + if ((ia6 = in6_ifadd(newprc, mcast, &psref)) != NULL) { /* * note that we should use pr (not newprc) for reference. */ @@ -1367,6 +1379,7 @@ prelist_update(struct nd_prefixctl *newprc, "address, errno=%d\n", e); } } + ia6_release(ia6, &psref); /* * A newly added address might affect the status @@ -1426,6 +1439,7 @@ pfxlist_onlink_check(void) struct in6_ifaddr *ia; struct nd_defrouter *dr; struct nd_pfxrouter *pfxrtr = NULL; + int s; /* * Check if there is a prefix that has a reachable advertising @@ -1540,6 +1554,7 @@ pfxlist_onlink_check(void) * always be attached. * The precise detection logic is same as the one for prefixes. */ + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { if (!(ia->ia6_flags & IN6_IFF_AUTOCONF)) continue; @@ -1556,20 +1571,30 @@ pfxlist_onlink_check(void) if (find_pfxlist_reachable_router(ia->ia6_ndpr)) break; } + pserialize_read_exit(s); if (ia) { + int bound = curlwp_bind(); + + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { + struct ifaddr *ifa = (struct ifaddr *)ia; + struct psref psref; + if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; if (ia->ia6_ndpr == NULL) /* XXX: see above. */ continue; + ia6_acquire(ia, &psref); + pserialize_read_exit(s); + if (find_pfxlist_reachable_router(ia->ia6_ndpr)) { if (ia->ia6_flags & IN6_IFF_DETACHED) { ia->ia6_flags &= ~IN6_IFF_DETACHED; ia->ia6_flags |= IN6_IFF_TENTATIVE; - nd6_dad_start((struct ifaddr *)ia, + nd6_dad_start(ifa, 0); /* We will notify the routing socket * of the DAD result, so no need to @@ -1579,23 +1604,43 @@ pfxlist_onlink_check(void) if ((ia->ia6_flags & IN6_IFF_DETACHED) == 0) { ia->ia6_flags |= IN6_IFF_DETACHED; rt_newaddrmsg(RTM_NEWADDR, - (struct ifaddr *)ia, 0, NULL); + ifa, 0, NULL); } } + + s = pserialize_read_enter(); + ia6_release(ia, &psref); } + pserialize_read_exit(s); + curlwp_bindx(bound); } else { + int bound = curlwp_bind(); + + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { if ((ia->ia6_flags & IN6_IFF_AUTOCONF) == 0) continue; if (ia->ia6_flags & IN6_IFF_DETACHED) { + struct ifaddr *ifa = (struct ifaddr *)ia; + struct psref psref; + ia->ia6_flags &= ~IN6_IFF_DETACHED; ia->ia6_flags |= IN6_IFF_TENTATIVE; + + ia6_acquire(ia, &psref); + pserialize_read_exit(s); + /* Do we need a delay in this case? */ - nd6_dad_start((struct ifaddr *)ia, 0); + nd6_dad_start(ifa, 0); + + s = pserialize_read_enter(); + ia6_release(ia, &psref); } } + pserialize_read_exit(s); + curlwp_bindx(bound); } } @@ -1608,6 +1653,8 @@ nd6_prefix_onlink(struct nd_prefix *pr) struct nd_prefix *opr; u_long rtflags; int error = 0; + struct psref psref; + int bound; /* sanity check */ if ((pr->ndpr_stateflags & NDPRF_ONLINK) != 0) { @@ -1640,14 +1687,18 @@ nd6_prefix_onlink(struct nd_prefix *pr) * We prefer link-local addresses as the associated interface address. */ /* search for a link-local addr */ - ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, - IN6_IFF_NOTREADY | IN6_IFF_ANYCAST); + bound = curlwp_bind(); + ifa = (struct ifaddr *)in6ifa_ifpforlinklocal_psref(ifp, + IN6_IFF_NOTREADY | IN6_IFF_ANYCAST, &psref); if (ifa == NULL) { - /* XXX: freebsd does not have ifa_ifwithaf */ + int s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family == AF_INET6) break; } + if (ifa != NULL) + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); /* should we care about ia6_flags? */ } if (ifa == NULL) { @@ -1661,6 +1712,7 @@ nd6_prefix_onlink(struct nd_prefix *pr) " to add route for a prefix(%s/%d) on %s\n", ip6_sprintf(&pr->ndpr_prefix.sin6_addr), pr->ndpr_plen, if_name(ifp)); + curlwp_bindx(bound); return (0); } @@ -1697,6 +1749,8 @@ nd6_prefix_onlink(struct nd_prefix *pr) ip6_sprintf(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr), ip6_sprintf(&mask6.sin6_addr), rtflags, error); } + ifa_release(ifa, &psref); + curlwp_bindx(bound); return (error); } @@ -1772,7 +1826,7 @@ nd6_prefix_offlink(struct nd_prefix *pr) } static struct in6_ifaddr * -in6_ifadd(struct nd_prefixctl *prc, int mcast) +in6_ifadd(struct nd_prefixctl *prc, int mcast, struct psref *psref) { struct ifnet *ifp = prc->ndprc_ifp; struct ifaddr *ifa; @@ -1782,6 +1836,7 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) struct in6_addr mask; int prefixlen = prc->ndprc_plen; int updateflags; + int s; in6_prefixlen2mask(&mask, prefixlen); @@ -1805,11 +1860,14 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) * with the same interface identifier, than to have multiple addresses * with different interface identifiers. */ + s = pserialize_read_enter(); ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */ if (ifa) ib = (struct in6_ifaddr *)ifa; - else + else { + pserialize_read_exit(s); return NULL; + } #if 0 /* don't care link local addr state, and always do DAD */ /* if link-local address is not eligible, do not autoconfigure. */ @@ -1825,6 +1883,7 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) nd6log(LOG_INFO, "wrong prefixlen for %s " "(prefix=%d ifid=%d)\n", if_name(ifp), prefixlen, 128 - plen0); + pserialize_read_exit(s); return NULL; } @@ -1852,6 +1911,7 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]); ifra.ifra_addr.sin6_addr.s6_addr32[3] |= (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]); + pserialize_read_exit(s); /* new prefix mask. */ sockaddr_in6_init(&ifra.ifra_prefixmask, &mask, 0, 0, 0); @@ -1869,12 +1929,15 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) * usually not happen, but we can still see this case, e.g., if we * have manually configured the exact address to be configured. */ + s = pserialize_read_enter(); if (in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr) != NULL) { /* this should be rare enough to make an explicit log */ log(LOG_INFO, "in6_ifadd: %s is already configured\n", ip6_sprintf(&ifra.ifra_addr.sin6_addr)); + pserialize_read_exit(s); return (NULL); } + pserialize_read_exit(s); /* * Allocate ifaddr structure, link into chain, etc. @@ -1892,7 +1955,7 @@ in6_ifadd(struct nd_prefixctl *prc, int mcast) return (NULL); /* ifaddr must not have been allocated. */ } - ia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); + ia = in6ifa_ifpwithaddr_psref(ifp, &ifra.ifra_addr.sin6_addr, psref); return (ia); /* this is always non-NULL */ } @@ -2007,14 +2070,17 @@ in6_tmpifadd( if ((error = in6_update_ifa(ifp, &ifra, NULL, updateflags)) != 0) return (error); + s = pserialize_read_enter(); newia = in6ifa_ifpwithaddr(ifp, &ifra.ifra_addr.sin6_addr); if (newia == NULL) { /* XXX: can it happen? */ + pserialize_read_exit(s); nd6log(LOG_ERR, "ifa update succeeded, but we got no ifaddr\n"); return (EINVAL); /* XXX */ } newia->ia6_ndpr = ia0->ia6_ndpr; newia->ia6_ndpr->ndpr_refcnt++; + pserialize_read_exit(s); /* * A newly added address might affect the status of other addresses. diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index 8470352..ec55db3 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -671,8 +671,9 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) { struct in6pcb *in6p = sotoin6pcb(so); struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; - struct ifaddr *ia = NULL; + struct ifaddr *ifa = NULL; int error = 0; + int s; KASSERT(solocked(so)); KASSERT(in6p != NULL); @@ -692,15 +693,24 @@ rip6_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) */ if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) return EADDRNOTAVAIL; + s = pserialize_read_enter(); if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ia = ifa_ifwithaddr(sin6tosa(addr))) == 0) - return EADDRNOTAVAIL; - if (ia && ifatoia6(ia)->ia6_flags & + (ifa = ifa_ifwithaddr(sin6tosa(addr))) == NULL) { + error = EADDRNOTAVAIL; + goto out; + } + if (ifa && (ifatoia6(ifa))->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) - return EADDRNOTAVAIL; + IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + error = EADDRNOTAVAIL; + goto out; + } + in6p->in6p_laddr = addr->sin6_addr; - return 0; + error = 0; +out: + pserialize_read_exit(s); + return error; } static int diff --git a/sys/netinet6/sctp6_usrreq.c b/sys/netinet6/sctp6_usrreq.c index 1c35875..075be64 100644 --- a/sys/netinet6/sctp6_usrreq.c +++ b/sys/netinet6/sctp6_usrreq.c @@ -1294,6 +1294,7 @@ static int sctp6_purgeif(struct socket *so, struct ifnet *ifp) { struct ifaddr *ifa; + /* FIXME NOMPSAFE */ IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family == PF_INET6) { sctp_delete_ip_address(ifa); diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index e9a47dc..86d1574 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -227,6 +227,7 @@ udp6_output(struct in6pcb * const in6p, struct mbuf *m, if (!IN6_IS_ADDR_V4MAPPED(faddr)) { struct psref psref; + int bound = curlwp_bind(); laddr = in6_selectsrc(sin6, optp, in6p->in6p_moptions, @@ -236,9 +237,11 @@ udp6_output(struct in6pcb * const in6p, struct mbuf *m, (error = in6_setscope(&sin6->sin6_addr, oifp, NULL))) { if_put(oifp, &psref); + curlwp_bindx(bound); goto release; } if_put(oifp, &psref); + curlwp_bindx(bound); } else { /* * XXX: freebsd[34] does not have in_selectsrc, but