commit 10eba573907ad3dbcb5c1992cc3da0dd64993e3c Author: Ryota Ozaki Date: Mon Jul 11 14:13:42 2016 +0900 psz/psref for ifaddr and variants diff --git a/sys/arch/x86/x86/vmt.c b/sys/arch/x86/x86/vmt.c index 82bcf89..c28ac4a 100644 --- a/sys/arch/x86/x86/vmt.c +++ b/sys/arch/x86/x86/vmt.c @@ -801,15 +801,16 @@ vmt_tclo_tick(void *xarg) sc->sc_rpc_error = 1; } } else if (strcmp(sc->sc_rpc_buf, "Set_Option broadcastIP 1") == 0) { + struct ifaddr *iface_addr = NULL; struct ifnet *iface; struct sockaddr_in *guest_ip; int s; + struct psref psref; /* find first available ipv4 address */ guest_ip = NULL; s = pserialize_read_enter(); IFNET_READER_FOREACH(iface) { - struct ifaddr *iface_addr; /* skip loopback */ if (strncmp(iface->if_xname, "lo", 2) == 0 && @@ -823,6 +824,7 @@ vmt_tclo_tick(void *xarg) } guest_ip = satosin(iface_addr->ifa_addr); + ifa_acquire(iface_addr, &psref); break; } } @@ -834,6 +836,7 @@ vmt_tclo_tick(void *xarg) device_printf(sc->sc_dev, "unable to send guest IP address\n"); sc->sc_rpc_error = 1; } + ifa_release(iface_addr, &psref); if (vm_rpc_send_str(&sc->sc_tclo_rpc, VM_RPC_REPLY_OK) != 0) { device_printf(sc->sc_dev, "error sending broadcastIP response\n"); diff --git a/sys/compat/common/uipc_syscalls_40.c b/sys/compat/common/uipc_syscalls_40.c index 4578ee7..723ece8 100644 --- a/sys/compat/common/uipc_syscalls_40.c +++ b/sys/compat/common/uipc_syscalls_40.c @@ -34,7 +34,6 @@ compat_ifconf(u_long cmd, void *data) { struct oifconf *ifc = data; struct ifnet *ifp; - struct ifaddr *ifa; struct oifreq ifr, *ifrp = NULL; int space = 0, error = 0; const int sz = (int)sizeof(ifr); @@ -51,8 +50,9 @@ compat_ifconf(u_long cmd, void *data) bound = curlwp_bind(); s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { + struct ifaddr *ifa; + psref_acquire(&psref, &ifp->if_psref, ifnet_psref_class); - pserialize_read_exit(s); (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); @@ -74,6 +74,10 @@ compat_ifconf(u_long cmd, void *data) IFADDR_READER_FOREACH(ifa, ifp) { struct sockaddr *sa = ifa->ifa_addr; + struct psref psref_ifa; + + ifa_acquire(ifa, &psref_ifa); + pserialize_read_exit(s); #ifdef COMPAT_OSOCK if (cmd == OOSIOCGIFCONF) { struct osockaddr *osa = @@ -81,8 +85,11 @@ compat_ifconf(u_long cmd, void *data) /* * If it does not fit, we don't bother with it */ - if (sa->sa_len > sizeof(*osa)) + if (sa->sa_len > sizeof(*osa)) { + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); continue; + } memcpy(&ifr.ifr_addr, sa, sa->sa_len); osa->sa_family = sa->sa_family; if (space >= sz) { @@ -112,12 +119,13 @@ compat_ifconf(u_long cmd, void *data) (char *)&ifrp->ifr_addr); } } + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); if (error != 0) goto release_exit; space -= sz; } - s = pserialize_read_enter(); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); } pserialize_read_exit(s); @@ -130,6 +138,7 @@ compat_ifconf(u_long cmd, void *data) return (0); release_exit: + pserialize_read_exit(s); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); curlwp_bindx(bound); return error; diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index dc41034..ac81ded 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1117,7 +1117,6 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) struct linux_ifreq ifr, *ifrp = NULL; struct linux_ifconf ifc; struct ifnet *ifp; - struct ifaddr *ifa; struct sockaddr *sa; struct osockaddr *osa; int space = 0, error; @@ -1140,8 +1139,8 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) bound = curlwp_bind(); s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { + struct ifaddr *ifa; psref_acquire(&psref, &ifp->if_psref, ifnet_psref_class); - pserialize_read_exit(s); (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); @@ -1151,23 +1150,32 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) } IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref_ifa; + ifa_acquire(ifa, &psref_ifa); + pserialize_read_exit(s); + sa = ifa->ifa_addr; if (sa->sa_family != AF_INET || sa->sa_len > sizeof(*osa)) - continue; + goto next; memcpy(&ifr.ifr_addr, sa, sa->sa_len); osa = (struct osockaddr *)&ifr.ifr_addr; osa->sa_family = sa->sa_family; if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) + if (error != 0) { + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); goto release_exit; + } ifrp++; } space -= sz; + next: + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); } - s = pserialize_read_enter(); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); } pserialize_read_exit(s); @@ -1181,6 +1189,7 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) return copyout(&ifc, data, sizeof(ifc)); release_exit: + pserialize_read_exit(s); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); curlwp_bindx(bound); return error; diff --git a/sys/compat/linux32/common/linux32_socket.c b/sys/compat/linux32/common/linux32_socket.c index 57a963a..e9926de 100644 --- a/sys/compat/linux32/common/linux32_socket.c +++ b/sys/compat/linux32/common/linux32_socket.c @@ -418,7 +418,6 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) struct linux32_ifreq ifr, *ifrp = NULL; struct linux32_ifconf ifc; struct ifnet *ifp; - struct ifaddr *ifa; struct sockaddr *sa; struct osockaddr *osa; int space = 0, error; @@ -441,8 +440,8 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) bound = curlwp_bind(); s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { + struct ifaddr *ifa; psref_acquire(&psref, &ifp->if_psref, ifnet_psref_class); - pserialize_read_exit(s); (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); @@ -452,23 +451,32 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) } IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref_ifa; + ifa_acquire(ifa, &psref_ifa); + pserialize_read_exit(s); + sa = ifa->ifa_addr; if (sa->sa_family != AF_INET || sa->sa_len > sizeof(*osa)) - continue; + goto next; memcpy(&ifr.ifr_addr, sa, sa->sa_len); osa = (struct osockaddr *)&ifr.ifr_addr; osa->sa_family = sa->sa_family; if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) + if (error != 0) { + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); goto release_exit; + } ifrp++; } space -= sz; + next: + s = pserialize_read_enter(); + ifa_release(ifa, &psref_ifa); } - s = pserialize_read_enter(); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); } pserialize_read_exit(s); @@ -482,6 +490,7 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) return copyout(&ifc, data, sizeof(ifc)); release_exit: + pserialize_read_exit(s); psref_release(&psref, &ifp->if_psref, ifnet_psref_class); curlwp_bindx(bound); return error; diff --git a/sys/kern/subr_psref.c b/sys/kern/subr_psref.c index 2fbc2bf..f2859d8 100644 --- a/sys/kern/subr_psref.c +++ b/sys/kern/subr_psref.c @@ -232,10 +232,10 @@ psref_acquire(struct psref *psref, const struct psref_target *target, pcpu = percpu_getref(class->prc_percpu); /* Record our reference. */ - LIST_INSERT_HEAD(&pcpu->pcpu_head, psref, psref_entry); psref->psref_target = target; psref->psref_lwp = curlwp; psref->psref_cpu = curcpu(); + LIST_INSERT_HEAD(&pcpu->pcpu_head, psref, psref_entry); /* Release the CPU list and restore interrupts. */ percpu_putref(class->prc_percpu); @@ -335,10 +335,10 @@ psref_copy(struct psref *pto, const struct psref *pfrom, pcpu = percpu_getref(class->prc_percpu); /* Record the new reference. */ - LIST_INSERT_HEAD(&pcpu->pcpu_head, pto, psref_entry); pto->psref_target = pfrom->psref_target; pto->psref_lwp = curlwp; pto->psref_cpu = curcpu(); + LIST_INSERT_HEAD(&pcpu->pcpu_head, pto, psref_entry); /* Release the CPU list and restore interrupts. */ percpu_putref(class->prc_percpu); diff --git a/sys/net/if.c b/sys/net/if.c index 472c8f7..3cc864d 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -183,6 +183,8 @@ static kmutex_t if_clone_mtx; struct ifnet *lo0ifp; int ifqmaxlen = IFQ_MAXLEN; +struct psref_class *ifa_psref_class __read_mostly; + static int if_rt_walktree(struct rtentry *, void *); static struct if_clone *if_clone_lookup(const char *, int *); @@ -281,11 +283,14 @@ void ifinit1(void) { mutex_init(&if_clone_mtx, MUTEX_DEFAULT, IPL_NONE); + TAILQ_INIT(&ifnet_list); mutex_init(&ifnet_mtx, MUTEX_DEFAULT, IPL_NONE); ifnet_psz = pserialize_create(); ifnet_psref_class = psref_class_create("ifnet", IPL_SOFTNET); + ifa_psref_class = psref_class_create("ifa", IPL_SOFTNET); PSLIST_INIT(&ifnet_pslist); + if_indexlim = 8; if_pfil = pfil_head_create(PFIL_TYPE_IFNET, NULL); @@ -427,6 +432,7 @@ if_dl_create(const struct ifnet *ifp, const struct sockaddr_dl **sdlp) ifa->ifa_rtrequest = link_rtrequest; ifa->ifa_addr = (struct sockaddr *)sdl; ifa->ifa_netmask = (struct sockaddr *)mask; + ifa_psref_init(ifa); *sdlp = sdl; @@ -486,20 +492,34 @@ if_deactivate_sadl(struct ifnet *ifp) } void -if_activate_sadl(struct ifnet *ifp, struct ifaddr *ifa, +if_activate_sadl(struct ifnet *ifp, struct ifaddr *ifa0, const struct sockaddr_dl *sdl) { - int s; + int s, ss; + struct ifaddr *ifa; + int bound = curlwp_bind(); s = splnet(); if_deactivate_sadl(ifp); - if_sadl_setrefs(ifp, ifa); - IFADDR_READER_FOREACH(ifa, ifp) + if_sadl_setrefs(ifp, ifa0); + + ss = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { + struct psref psref; + ifa_acquire(ifa, &psref); + pserialize_read_exit(ss); + rtinit(ifa, RTM_LLINFO_UPD, 0); + ss = pserialize_read_enter(); + ifa_release(ifa, &psref); + } + pserialize_read_exit(ss); + splx(s); + curlwp_bindx(bound); } /* @@ -1023,13 +1043,20 @@ void if_purgeaddrs(struct ifnet *ifp, int family, void (*purgeaddr)(struct ifaddr *)) { struct ifaddr *ifa, *nifa; + int s; + s = pserialize_read_enter(); for (ifa = IFADDR_READER_FIRST(ifp); ifa; ifa = nifa) { nifa = IFADDR_READER_NEXT(ifa); if (ifa->ifa_addr->sa_family != family) continue; + pserialize_read_exit(s); + (*purgeaddr)(ifa); + + s = pserialize_read_enter(); } + pserialize_read_exit(s); } #ifdef IFAREF_DEBUG @@ -1134,6 +1161,10 @@ if_detach(struct ifnet *ifp) pserialize_perform(ifnet_psz); IFNET_UNLOCK(); + /* Wait for all readers to drain before freeing. */ + psref_target_destroy(&ifp->if_psref, ifnet_psref_class); + PSLIST_ENTRY_DESTROY(ifp, if_pslist_entry); + mutex_obj_free(ifp->if_ioctl_lock); ifp->if_ioctl_lock = NULL; @@ -1178,6 +1209,10 @@ if_detach(struct ifnet *ifp) * least one ifaddr. */ again: + /* + * At this point, no other one tries to remove ifa in the list, + * so we don't need to take a lock or psref. + */ IFADDR_READER_FOREACH(ifa, ifp) { family = ifa->ifa_addr->sa_family; #ifdef IFAREF_DEBUG @@ -1303,10 +1338,6 @@ again: xc = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); xc_wait(xc); - /* Wait for all readers to drain before freeing. */ - psref_target_destroy(&ifp->if_psref, ifnet_psref_class); - PSLIST_ENTRY_DESTROY(ifp, if_pslist_entry); - if (ifp->if_percpuq != NULL) { if_percpuq_destroy(ifp->if_percpuq); ifp->if_percpuq = NULL; @@ -1543,6 +1574,13 @@ if_clone_list(int buf_count, char *buffer, int *total) } void +ifa_psref_init(struct ifaddr *ifa) +{ + + psref_target_init(&ifa->ifa_psref, ifa_psref_class); +} + +void ifaref(struct ifaddr *ifa) { ifa->ifa_refcnt++; @@ -1562,24 +1600,63 @@ ifafree(struct ifaddr *ifa) void ifa_insert(struct ifnet *ifp, struct ifaddr *ifa) { + ifa->ifa_ifp = ifp; + + IFNET_LOCK(); TAILQ_INSERT_TAIL(&ifp->if_addrlist, ifa, ifa_list); IFADDR_ENTRY_INIT(ifa); IFADDR_WRITER_INSERT_TAIL(ifp, ifa); + IFNET_UNLOCK(); + ifaref(ifa); } void ifa_remove(struct ifnet *ifp, struct ifaddr *ifa) { + KASSERT(ifa->ifa_ifp == ifp); + + IFNET_LOCK(); TAILQ_REMOVE(&ifp->if_addrlist, ifa, ifa_list); IFADDR_WRITER_REMOVE(ifa); - /* TODO psref_target_destroy */ IFADDR_ENTRY_DESTROY(ifa); +#if notyet + pserialize_perform(ifnet_psz); +#endif + IFNET_UNLOCK(); + +#if notyet + psref_target_destroy(&ifa->ifa_psref, ifa_psref_class); +#endif ifafree(ifa); } +void +ifa_acquire(struct ifaddr *ifa, struct psref *psref) +{ + + psref_acquire(psref, &ifa->ifa_psref, ifa_psref_class); +} + +void +ifa_release(struct ifaddr *ifa, struct psref *psref) +{ + + if (ifa == NULL) + return; + + psref_release(psref, &ifa->ifa_psref, ifa_psref_class); +} + +bool +ifa_held(struct ifaddr *ifa) +{ + + return psref_held(&ifa->ifa_psref, ifa_psref_class); +} + static inline int equal(const struct sockaddr *sa1, const struct sockaddr *sa2) { @@ -1595,9 +1672,7 @@ ifa_ifwithaddr(const struct sockaddr *addr) { struct ifnet *ifp; struct ifaddr *ifa; - int s; - s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { if (if_is_deactivated(ifp)) continue; @@ -1614,10 +1689,23 @@ ifa_ifwithaddr(const struct sockaddr *addr) return ifa; } } - pserialize_read_exit(s); return NULL; } +struct ifaddr * +ifa_ifwithaddr_psref(const struct sockaddr *addr, struct psref *psref) +{ + struct ifaddr *ifa; + int s = pserialize_read_enter(); + + ifa = ifa_ifwithaddr(addr); + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + return ifa; +} + /* * Locate the point to point interface with a given destination address. */ @@ -1627,9 +1715,7 @@ ifa_ifwithdstaddr(const struct sockaddr *addr) { struct ifnet *ifp; struct ifaddr *ifa; - int s; - s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { if (if_is_deactivated(ifp)) continue; @@ -1643,10 +1729,25 @@ ifa_ifwithdstaddr(const struct sockaddr *addr) return ifa; } } - pserialize_read_exit(s); + return NULL; } +struct ifaddr * +ifa_ifwithdstaddr_psref(const struct sockaddr *addr, struct psref *psref) +{ + struct ifaddr *ifa; + int s; + + s = pserialize_read_enter(); + ifa = ifa_ifwithdstaddr(addr); + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + return ifa; +} + /* * Find an interface on a specific network. If many, choice * is most specific found. @@ -1655,12 +1756,10 @@ struct ifaddr * ifa_ifwithnet(const struct sockaddr *addr) { struct ifnet *ifp; - struct ifaddr *ifa; + struct ifaddr *ifa, *ifa_maybe = NULL; const struct sockaddr_dl *sdl; - struct ifaddr *ifa_maybe = 0; u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; - int s; if (af == AF_LINK) { sdl = satocsdl(addr); @@ -1674,7 +1773,6 @@ ifa_ifwithnet(const struct sockaddr *addr) if (af == AF_APPLETALK) { const struct sockaddr_at *sat, *sat2; sat = (const struct sockaddr_at *)addr; - s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { if (if_is_deactivated(ifp)) continue; @@ -1689,11 +1787,9 @@ ifa_ifwithnet(const struct sockaddr *addr) ifa_maybe = ifa; } } - pserialize_read_exit(s); return ifa_maybe; } #endif - s = pserialize_read_enter(); IFNET_READER_FOREACH(ifp) { if (if_is_deactivated(ifp)) continue; @@ -1720,10 +1816,24 @@ ifa_ifwithnet(const struct sockaddr *addr) ifa_maybe = ifa; } } - pserialize_read_exit(s); return ifa_maybe; } +struct ifaddr * +ifa_ifwithnet_psref(const struct sockaddr *addr, struct psref *psref) +{ + struct ifaddr *ifa; + int s; + + s = pserialize_read_enter(); + ifa = ifa_ifwithnet(addr); + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + return ifa; +} + /* * Find the interface of the addresss. */ @@ -1738,6 +1848,21 @@ ifa_ifwithladdr(const struct sockaddr *addr) return NULL; } +struct ifaddr * +ifa_ifwithladdr_psref(const struct sockaddr *addr, struct psref *psref) +{ + struct ifaddr *ifa; + int s; + + s = pserialize_read_enter(); + ifa = ifa_ifwithladdr(addr); + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + return ifa; +} + /* * Find an interface using a specific address family */ @@ -1806,6 +1931,22 @@ ifaof_ifpforaddr(const struct sockaddr *addr, struct ifnet *ifp) return ifa_maybe; } +struct ifaddr * +ifaof_ifpforaddr_psref(const struct sockaddr *addr, struct ifnet *ifp, + struct psref *psref) +{ + struct ifaddr *ifa; + int s; + + s = pserialize_read_enter(); + ifa = ifaof_ifpforaddr(addr, ifp); + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + return ifa; +} + /* * Default action when installing a route with a Link Level gateway. * Lookup an appropriate real ifa to point to. @@ -1817,14 +1958,16 @@ link_rtrequest(int cmd, struct rtentry *rt, const struct rt_addrinfo *info) struct ifaddr *ifa; const struct sockaddr *dst; struct ifnet *ifp; + struct psref psref; if (cmd != RTM_ADD || (ifa = rt->rt_ifa) == NULL || (ifp = ifa->ifa_ifp) == NULL || (dst = rt_getkey(rt)) == NULL) return; - if ((ifa = ifaof_ifpforaddr(dst, ifp)) != NULL) { + if ((ifa = ifaof_ifpforaddr_psref(dst, ifp, &psref)) != NULL) { rt_replace_ifa(rt, ifa); if (ifa->ifa_rtrequest && ifa->ifa_rtrequest != link_rtrequest) ifa->ifa_rtrequest(cmd, rt, info); + ifa_release(ifa, &psref); } } @@ -2026,6 +2169,7 @@ p2p_rtrequest(int req, struct rtentry *rt, { struct ifnet *ifp = rt->rt_ifp; struct ifaddr *ifa, *lo0ifa; + int s = pserialize_read_enter(); switch (req) { case RTM_ADD: @@ -2064,6 +2208,7 @@ p2p_rtrequest(int req, struct rtentry *rt, default: break; } + pserialize_read_exit(s); } /* @@ -2076,11 +2221,26 @@ if_down(struct ifnet *ifp) { struct ifaddr *ifa; struct domain *dp; + int s, bound; + struct psref psref; ifp->if_flags &= ~IFF_UP; nanotime(&ifp->if_lastchange); - IFADDR_READER_FOREACH(ifa, ifp) + + bound = curlwp_bind(); + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); + pfctlinput(PRC_IFDOWN, ifa->ifa_addr); + + s = pserialize_read_enter(); + ifa_release(ifa, &psref); + } + pserialize_read_exit(s); + curlwp_bindx(bound); + IFQ_PURGE(&ifp->if_snd); #if NCARP > 0 if (ifp->if_carp) @@ -2296,7 +2456,12 @@ if_put(const struct ifnet *ifp, struct psref *psref) ifnet_t * if_byindex(u_int idx) { - return (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; + ifnet_t *ifp; + + ifp = (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; + if (ifp != NULL && if_is_deactivated(ifp)) + ifp = NULL; + return ifp; } /* @@ -2312,6 +2477,8 @@ if_get_byindex(u_int idx, struct psref *psref) s = pserialize_read_enter(); ifp = (__predict_true(idx < if_indexlim)) ? ifindex2ifnet[idx] : NULL; + if (ifp != NULL && if_is_deactivated(ifp)) + ifp = NULL; if (__predict_true(ifp != NULL)) psref_acquire(psref, &ifp->if_psref, ifnet_psref_class); pserialize_read_exit(s); @@ -2503,6 +2670,7 @@ ifaddrpref_ioctl(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) struct sockaddr sa; struct sockaddr_storage ss; } u, v; + int s, error = 0; switch (cmd) { case SIOCSIFADDRPREF: @@ -2531,6 +2699,7 @@ ifaddrpref_ioctl(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) sockaddr_externalize(&v.sa, sizeof(v.ss), sa); + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != sa->sa_family) continue; @@ -2538,22 +2707,27 @@ ifaddrpref_ioctl(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) if (sockaddr_cmp(&u.sa, &v.sa) == 0) break; } - if (ifa == NULL) - return EADDRNOTAVAIL; + if (ifa == NULL) { + error = EADDRNOTAVAIL; + goto out; + } switch (cmd) { case SIOCSIFADDRPREF: ifa->ifa_preference = ifap->ifap_preference; - return 0; + goto out; case SIOCGIFADDRPREF: /* fill in the if_laddrreq structure */ (void)sockaddr_copy(sstosa(&ifap->ifap_addr), sizeof(ifap->ifap_addr), ifa->ifa_addr); ifap->ifap_preference = ifa->ifa_preference; - return 0; + goto out; default: - return EOPNOTSUPP; + error = EOPNOTSUPP; } +out: + pserialize_read_exit(s); + return error; } /* diff --git a/sys/net/if.h b/sys/net/if.h index d8ee186..d9db4b0 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -598,6 +598,7 @@ struct ifaddr { int16_t ifa_preference; /* preference level for this address */ #ifdef _KERNEL struct pslist_entry ifa_pslist_entry; + struct psref_target ifa_psref; #endif }; #define IFA_ROUTE RTF_UP /* (0x01) route installed */ @@ -984,17 +985,29 @@ void void ifa_insert(struct ifnet *, struct ifaddr *); void ifa_remove(struct ifnet *, struct ifaddr *); +void ifa_psref_init(struct ifaddr *); +void ifa_acquire(struct ifaddr *, struct psref *); +void ifa_release(struct ifaddr *, struct psref *); +bool ifa_held(struct ifaddr *); + void ifaref(struct ifaddr *); void ifafree(struct ifaddr *); struct ifaddr *ifa_ifwithaddr(const struct sockaddr *); +struct ifaddr *ifa_ifwithaddr_psref(const struct sockaddr *, struct psref *); struct ifaddr *ifa_ifwithaf(int); struct ifaddr *ifa_ifwithdstaddr(const struct sockaddr *); +struct ifaddr *ifa_ifwithdstaddr_psref(const struct sockaddr *, + struct psref *); struct ifaddr *ifa_ifwithnet(const struct sockaddr *); +struct ifaddr *ifa_ifwithnet_psref(const struct sockaddr *, struct psref *); struct ifaddr *ifa_ifwithladdr(const struct sockaddr *); -struct ifaddr *ifa_ifwithroute(int, const struct sockaddr *, - const struct sockaddr *); +struct ifaddr *ifa_ifwithladdr_psref(const struct sockaddr *, struct psref *); +struct ifaddr *ifa_ifwithroute_psref(int, const struct sockaddr *, + const struct sockaddr *, struct psref *); struct ifaddr *ifaof_ifpforaddr(const struct sockaddr *, struct ifnet *); +struct ifaddr *ifaof_ifpforaddr_psref(const struct sockaddr *, struct ifnet *, + struct psref *); void link_rtrequest(int, struct rtentry *, const struct rt_addrinfo *); void p2p_rtrequest(int, struct rtentry *, const struct rt_addrinfo *); diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 08ffb7f..83ee9ce 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -218,12 +218,17 @@ ether_output(struct ifnet * const ifp0, struct mbuf * const m0, #if NCARP > 0 if (ifp->if_type == IFT_CARP) { struct ifaddr *ifa; + int s = pserialize_read_enter(); /* loop back if this is going to the carp interface */ if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && - (ifa = ifa_ifwithaddr(dst)) != NULL && - ifa->ifa_ifp == ifp0) - return looutput(ifp0, m, dst, rt); + (ifa = ifa_ifwithaddr(dst)) != NULL) { + if (ifa->ifa_ifp == ifp0) { + pserialize_read_exit(s); + return looutput(ifp0, m, dst, rt); + } + } + pserialize_read_exit(s); ifp = ifp->if_carpdev; /* ac = (struct arpcom *)ifp; */ @@ -298,7 +303,10 @@ ether_output(struct ifnet * const ifp0, struct mbuf * const m0, break; #endif #ifdef NETATALK - case AF_APPLETALK: + case AF_APPLETALK: { + struct ifaddr *ifa; + int s; + KERNEL_LOCK(1, NULL); if (!aarpresolve(ifp, m, (const struct sockaddr_at *)dst, edst)) { #ifdef NETATALKDEBUG @@ -310,12 +318,14 @@ ether_output(struct ifnet * const ifp0, struct mbuf * const m0, /* * ifaddr is the first thing in at_ifaddr */ - aa = (struct at_ifaddr *) at_ifawithnet( - (const struct sockaddr_at *)dst, ifp); - if (aa == NULL) { - KERNEL_UNLOCK_ONE(NULL); - goto bad; + s = pserialize_read_enter(); + ifa = at_ifawithnet((const struct sockaddr_at *)dst, ifp); + if (ifa == NULL) { + pserialize_read_exit(s); + KERNEL_UNLOCK_ONE(NULL); + goto bad; } + aa = (struct at_ifaddr *)ifa; /* * In the phase 2 case, we need to prepend an mbuf for the @@ -336,8 +346,10 @@ ether_output(struct ifnet * const ifp0, struct mbuf * const m0, } else { etype = htons(ETHERTYPE_ATALK); } + pserialize_read_exit(s); KERNEL_UNLOCK_ONE(NULL); break; + } #endif /* NETATALK */ case pseudo_AF_HDRCMPLT: hdrcmplt = 1; diff --git a/sys/net/if_fddisubr.c b/sys/net/if_fddisubr.c index 4ce90f2..ce4ea06 100644 --- a/sys/net/if_fddisubr.c +++ b/sys/net/if_fddisubr.c @@ -203,10 +203,16 @@ fddi_output(struct ifnet *ifp0, struct mbuf *m0, const struct sockaddr *dst, struct ifaddr *ifa; /* loop back if this is going to the carp interface */ - if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && - (ifa = ifa_ifwithaddr(dst)) != NULL && - ifa->ifa_ifp == ifp0) - return (looutput(ifp0, m, dst, rt)); + if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP) { + int s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(dst); + if (ifa != NULL && + ifa->ifa_ifp == ifp0) { + pserialize_read_exit(s); + return (looutput(ifp0, m, dst, rt)); + } + pserialize_read_exit(s); + } ifp = ifp->if_carpdev; /* ac = (struct arpcom *)ifp; */ @@ -285,6 +291,9 @@ fddi_output(struct ifnet *ifp0, struct mbuf *m0, const struct sockaddr *dst, #ifdef NETATALK case AF_APPLETALK: { struct at_ifaddr *aa; + struct ifaddr *ifa; + int s; + if (!aarpresolve(ifp, m, (const struct sockaddr_at *)dst, edst)) { #ifdef NETATALKDEBUG printf("aarpresolv: failed\n"); @@ -294,9 +303,13 @@ fddi_output(struct ifnet *ifp0, struct mbuf *m0, const struct sockaddr *dst, /* * ifaddr is the first thing in at_ifaddr */ - if ((aa = (struct at_ifaddr *)at_ifawithnet( - (const struct sockaddr_at *)dst, ifp)) == NULL) + s = pserialize_read_enter(); + ifa = at_ifawithnet((const struct sockaddr_at *)dst, ifp); + if (ifa == NULL) { + pserialize_read_exit(s); goto bad; + } + aa = (struct at_ifaddr *)ifa; /* * In the phase 2 case, we need to prepend an mbuf for the llc @@ -320,6 +333,7 @@ fddi_output(struct ifnet *ifp0, struct mbuf *m0, const struct sockaddr *dst, } else { etype = htons(ETHERTYPE_ATALK); } + pserialize_read_exit(s); break; } #endif /* NETATALK */ diff --git a/sys/net/if_stf.c b/sys/net/if_stf.c index 2b0c838..066cce5 100644 --- a/sys/net/if_stf.c +++ b/sys/net/if_stf.c @@ -312,9 +312,10 @@ stf_getsrcifa6(struct ifnet *ifp) struct in_ifaddr *ia4; struct sockaddr_in6 *sin6; struct in_addr in; + int s; - IFADDR_READER_FOREACH(ifa, ifp) - { + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family != AF_INET6) continue; sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; @@ -326,8 +327,11 @@ stf_getsrcifa6(struct ifnet *ifp) if (ia4 == NULL) continue; + pserialize_read_exit(s); + /* TODO NOMPSAFE */ return (struct in6_ifaddr *)ifa; } + pserialize_read_exit(s); return NULL; } diff --git a/sys/net/if_tokensubr.c b/sys/net/if_tokensubr.c index 8ddd016..e6f3c05 100644 --- a/sys/net/if_tokensubr.c +++ b/sys/net/if_tokensubr.c @@ -175,10 +175,16 @@ token_output(struct ifnet *ifp0, struct mbuf *m0, const struct sockaddr *dst, struct ifaddr *ifa; /* loop back if this is going to the carp interface */ - if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP && - (ifa = ifa_ifwithaddr(dst)) != NULL && - ifa->ifa_ifp == ifp0) - return (looutput(ifp0, m, dst, rt)); + if (dst != NULL && ifp0->if_link_state == LINK_STATE_UP) { + int s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(dst); + if (ifa != NULL && + ifa->ifa_ifp == ifp0) { + pserialize_read_exit(s); + return (looutput(ifp0, m, dst, rt)); + } + pserialize_read_exit(s); + } ifp = ifp->if_carpdev; ah = (struct arphdr *)ifp; diff --git a/sys/net/route.c b/sys/net/route.c index 4e2a8cb..6e38074 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -495,9 +495,10 @@ rtredirect(const struct sockaddr *dst, const struct sockaddr *gateway, uint64_t *stat = NULL; struct rt_addrinfo info; struct ifaddr *ifa; + struct psref psref; /* verify the gateway is directly reachable */ - if ((ifa = ifa_ifwithnet(gateway)) == NULL) { + if ((ifa = ifa_ifwithnet_psref(gateway, &psref)) == NULL) { error = ENETUNREACH; goto out; } @@ -511,8 +512,15 @@ rtredirect(const struct sockaddr *dst, const struct sockaddr *gateway, if (!(flags & RTF_DONE) && rt && (sockaddr_cmp(src, rt->rt_gateway) != 0 || rt->rt_ifa != ifa)) error = EINVAL; - else if (ifa_ifwithaddr(gateway)) - error = EHOSTUNREACH; + else { + int s = pserialize_read_enter(); + struct ifaddr *_ifa; + + _ifa = ifa_ifwithaddr(gateway); + if (_ifa != NULL) + error = EHOSTUNREACH; + pserialize_read_exit(s); + } if (error) goto done; /* @@ -580,6 +588,7 @@ out: info.rti_info[RTAX_NETMASK] = netmask; info.rti_info[RTAX_AUTHOR] = src; rt_missmsg(RTM_REDIRECT, &info, flags, error); + ifa_release(ifa, &psref); } /* @@ -610,10 +619,11 @@ rtdeletemsg(struct rtentry *rt) } struct ifaddr * -ifa_ifwithroute(int flags, const struct sockaddr *dst, - const struct sockaddr *gateway) +ifa_ifwithroute_psref(int flags, const struct sockaddr *dst, + const struct sockaddr *gateway, struct psref *psref) { - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; + if ((flags & RTF_GATEWAY) == 0) { /* * If we are adding a route to an interface, @@ -622,35 +632,55 @@ ifa_ifwithroute(int flags, const struct sockaddr *dst, * as our clue to the interface. Otherwise * we can use the local address. */ - ifa = NULL; if ((flags & RTF_HOST) && gateway->sa_family != AF_LINK) - ifa = ifa_ifwithdstaddr(dst); + ifa = ifa_ifwithdstaddr_psref(dst, psref); if (ifa == NULL) - ifa = ifa_ifwithaddr(gateway); + ifa = ifa_ifwithaddr_psref(gateway, psref); } else { /* * If we are adding a route to a remote net * or host, the gateway may still be on the * other end of a pt to pt link. */ - ifa = ifa_ifwithdstaddr(gateway); + ifa = ifa_ifwithdstaddr_psref(gateway, psref); } if (ifa == NULL) - ifa = ifa_ifwithnet(gateway); + ifa = ifa_ifwithnet_psref(gateway, psref); if (ifa == NULL) { - struct rtentry *rt = rtalloc1(dst, 0); + int s; + struct rtentry *rt; + + rt = rtalloc1(dst, 0); if (rt == NULL) return NULL; - ifa = rt->rt_ifa; + /* + * Just in case. May not need to do this workaround. + * Revisit when working on rtentry MP-ification. + */ + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, rt->rt_ifp) { + if (ifa == rt->rt_ifa) + break; + } + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); rtfree(rt); if (ifa == NULL) return NULL; } if (ifa->ifa_addr->sa_family != dst->sa_family) { - struct ifaddr *oifa = ifa; - ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); - if (ifa == NULL) - ifa = oifa; + struct ifaddr *nifa; + int s; + + s = pserialize_read_enter(); + nifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); + if (nifa != NULL) { + ifa_release(ifa, psref); + ifa_acquire(nifa, psref); + ifa = nifa; + } + pserialize_read_exit(s); } return ifa; } @@ -699,48 +729,69 @@ rtrequest_newmsg(const int req, const struct sockaddr *dst, return 0; } -int -rt_getifa(struct rt_addrinfo *info) +struct ifnet * +rt_getifp(struct rt_addrinfo *info, struct psref *psref) { - struct ifaddr *ifa; - const struct sockaddr *dst = info->rti_info[RTAX_DST]; - const struct sockaddr *gateway = info->rti_info[RTAX_GATEWAY]; - const struct sockaddr *ifaaddr = info->rti_info[RTAX_IFA]; const struct sockaddr *ifpaddr = info->rti_info[RTAX_IFP]; - int flags = info->rti_flags; + if (info->rti_ifp != NULL) + return NULL; /* * ifp may be specified by sockaddr_dl when protocol address * is ambiguous */ - if (info->rti_ifp == NULL && ifpaddr != NULL - && ifpaddr->sa_family == AF_LINK && - (ifa = ifa_ifwithnet(ifpaddr)) != NULL) - info->rti_ifp = ifa->ifa_ifp; - if (info->rti_ifa == NULL && ifaaddr != NULL) - info->rti_ifa = ifa_ifwithaddr(ifaaddr); - if (info->rti_ifa == NULL) { - const struct sockaddr *sa; + if (ifpaddr != NULL && ifpaddr->sa_family == AF_LINK) { + struct ifaddr *ifa; + int s = pserialize_read_enter(); - sa = ifaaddr != NULL ? ifaaddr : - (gateway != NULL ? gateway : dst); - if (sa != NULL && info->rti_ifp != NULL) - info->rti_ifa = ifaof_ifpforaddr(sa, info->rti_ifp); - else if (dst != NULL && gateway != NULL) - info->rti_ifa = ifa_ifwithroute(flags, dst, gateway); - else if (sa != NULL) - info->rti_ifa = ifa_ifwithroute(flags, sa, sa); + ifa = ifa_ifwithnet(ifpaddr); + if (ifa != NULL) + info->rti_ifp = if_get_byindex(ifa->ifa_ifp->if_index, + psref); + pserialize_read_exit(s); } - if ((ifa = info->rti_ifa) == NULL) - return ENETUNREACH; + + return info->rti_ifp; +} + +struct ifaddr * +rt_getifa(struct rt_addrinfo *info, struct psref *psref) +{ + struct ifaddr *ifa; + const struct sockaddr *dst = info->rti_info[RTAX_DST]; + const struct sockaddr *gateway = info->rti_info[RTAX_GATEWAY]; + const struct sockaddr *ifaaddr = info->rti_info[RTAX_IFA]; + int flags = info->rti_flags; + const struct sockaddr *sa; + + if (info->rti_ifa == NULL && ifaaddr != NULL) { + ifa = ifa_ifwithaddr_psref(ifaaddr, psref); + if (ifa != NULL) + goto got; + } + + sa = ifaaddr != NULL ? ifaaddr : + (gateway != NULL ? gateway : dst); + if (sa != NULL && info->rti_ifp != NULL) + ifa = ifaof_ifpforaddr_psref(sa, info->rti_ifp, psref); + else if (dst != NULL && gateway != NULL) + ifa = ifa_ifwithroute_psref(flags, dst, gateway, psref); + else if (sa != NULL) + ifa = ifa_ifwithroute_psref(flags, sa, sa, psref); + if (ifa == NULL) + return NULL; +got: if (ifa->ifa_getifa != NULL) { - info->rti_ifa = ifa = (*ifa->ifa_getifa)(ifa, dst); + /* FIXME NOMPSAFE */ + ifa = (*ifa->ifa_getifa)(ifa, dst); if (ifa == NULL) - return ENETUNREACH; + return NULL; + ifa_acquire(ifa, psref); } + info->rti_ifa = ifa; if (info->rti_ifp == NULL) info->rti_ifp = ifa->ifa_ifp; - return 0; + return ifa; } /* @@ -750,18 +801,23 @@ rt_getifa(struct rt_addrinfo *info) int rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) { - int s = splsoftnet(); + int s = splsoftnet(), ss; int error = 0, rc; struct rtentry *rt; rtbl_t *rtbl; - struct ifaddr *ifa, *ifa2; + struct ifaddr *ifa = NULL, *ifa2 = NULL; struct sockaddr_storage maskeddst; const struct sockaddr *dst = info->rti_info[RTAX_DST]; const struct sockaddr *gateway = info->rti_info[RTAX_GATEWAY]; const struct sockaddr *netmask = info->rti_info[RTAX_NETMASK]; int flags = info->rti_flags; + struct psref psref_ifp, psref_ifa; + int bound = 0; + struct ifnet *ifp = NULL; + bool need_to_release_ifa = true; #define senderr(x) { error = x ; goto bad; } + bound = curlwp_bind(); if ((rtbl = rt_gettable(dst->sa_family)) == NULL) senderr(ESRCH); if (flags & RTF_HOST) @@ -788,6 +844,7 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) } if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, info); + ifa = NULL; } rttrash++; if (ret_nrt) { @@ -802,9 +859,16 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) break; case RTM_ADD: - if (info->rti_ifa == NULL && (error = rt_getifa(info))) - senderr(error); - ifa = info->rti_ifa; + if (info->rti_ifa == NULL) { + ifp = rt_getifp(info, &psref_ifp); + ifa = rt_getifa(info, &psref_ifa); + if (ifa == NULL) + senderr(ENETUNREACH); + } else { + /* Caller should have a reference of ifa */ + ifa = info->rti_ifa; + need_to_release_ifa = false; + } rt = pool_get(&rtentry_pool, PR_NOWAIT); if (rt == NULL) senderr(ENOBUFS); @@ -835,17 +899,23 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) senderr(ENOBUFS); } RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key); - if (info->rti_info[RTAX_IFP] != NULL && - (ifa2 = ifa_ifwithnet(info->rti_info[RTAX_IFP])) != NULL && - ifa2->ifa_ifp != NULL) - rt->rt_ifp = ifa2->ifa_ifp; - else + + ss = pserialize_read_enter(); + if (info->rti_info[RTAX_IFP] != NULL) { + ifa2 = ifa_ifwithnet(info->rti_info[RTAX_IFP]); + if (ifa2 != NULL) + rt->rt_ifp = ifa2->ifa_ifp; + else + rt->rt_ifp = ifa->ifa_ifp; + } else rt->rt_ifp = ifa->ifa_ifp; + pserialize_read_exit(ss); + RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key); rc = rt_addaddr(rtbl, rt, netmask); RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key); if (rc != 0) { - ifafree(ifa); + ifafree(ifa); /* for rt_set_ifa above */ rt_destroy(rt); pool_put(&rtentry_pool, rt); senderr(rc); @@ -853,6 +923,11 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key); if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, info); + if (need_to_release_ifa) + ifa_release(ifa, &psref_ifa); + ifa = NULL; + if_put(ifp, &psref_ifp); + ifp = NULL; RT_DPRINTF("rt->_rt_key = %p\n", (void *)rt->_rt_key); if (ret_nrt) { *ret_nrt = rt; @@ -875,6 +950,10 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) break; } bad: + if (need_to_release_ifa) + ifa_release(ifa, &psref_ifa); + if_put(ifp, &psref_ifp); + curlwp_bindx(bound); splx(s); return error; } diff --git a/sys/net/route.h b/sys/net/route.h index 3821eeb..5f8c37a 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -396,7 +396,10 @@ int rt_ifa_addlocal(struct ifaddr *); int rt_ifa_remlocal(struct ifaddr *, struct ifaddr *); struct ifaddr * rt_get_ifa(struct rtentry *); -int rt_getifa(struct rt_addrinfo *); +struct ifaddr * + rt_getifa(struct rt_addrinfo *, struct psref *); +struct ifnet * + rt_getifp(struct rt_addrinfo *, struct psref *); void rt_replace_ifa(struct rtentry *, struct ifaddr *); int rt_setgate(struct rtentry *, const struct sockaddr *); diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index e6e58d1..c288095 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -541,13 +541,13 @@ route_output_report(struct rtentry *rt, struct rt_addrinfo *info, static struct ifaddr * route_output_get_ifa(const struct rt_addrinfo info, const struct rtentry *rt, - struct ifnet **ifp) + struct ifnet **ifp, struct psref *psref) { struct ifaddr *ifa = NULL; *ifp = NULL; if (info.rti_info[RTAX_IFP] != NULL) { - ifa = ifa_ifwithnet(info.rti_info[RTAX_IFP]); + ifa = ifa_ifwithnet_psref(info.rti_info[RTAX_IFP], psref); if (ifa == NULL) goto next; *ifp = ifa->ifa_ifp; @@ -556,29 +556,29 @@ route_output_get_ifa(const struct rt_addrinfo info, const struct rtentry *rt, goto next; if (info.rti_info[RTAX_IFA] == NULL) { /* route change -ifp */ - ifa = ifaof_ifpforaddr(info.rti_info[RTAX_GATEWAY], - *ifp); + ifa = ifaof_ifpforaddr_psref(info.rti_info[RTAX_GATEWAY], + *ifp, psref); } else { /* route change -ifp -ifa */ - ifa = ifa_ifwithaddr(info.rti_info[RTAX_IFA]); + ifa = ifa_ifwithaddr_psref(info.rti_info[RTAX_IFA], psref); if (ifa != NULL) goto out; - ifa = ifaof_ifpforaddr(info.rti_info[RTAX_IFA], - *ifp); + ifa = ifaof_ifpforaddr_psref(info.rti_info[RTAX_IFA], + *ifp, psref); } goto out; } next: if (info.rti_info[RTAX_IFA] != NULL) { /* route change -ifa */ - ifa = ifa_ifwithaddr(info.rti_info[RTAX_IFA]); + ifa = ifa_ifwithaddr_psref(info.rti_info[RTAX_IFA], psref); if (ifa != NULL) goto out; } if (info.rti_info[RTAX_GATEWAY] != NULL) { /* route change */ - ifa = ifa_ifwithroute(rt->rt_flags, rt_getkey(rt), - info.rti_info[RTAX_GATEWAY]); + ifa = ifa_ifwithroute_psref(rt->rt_flags, rt_getkey(rt), + info.rti_info[RTAX_GATEWAY], psref); } out: if (ifa != NULL && *ifp == NULL) @@ -601,11 +601,15 @@ COMPATNAME(route_output)(struct mbuf *m, struct socket *so) struct ifaddr *ifa = NULL; sa_family_t family; struct sockaddr_dl sdl; + struct psref psref; + int bound = curlwp_bind(); #define senderr(e) do { error = e; goto flush;} while (/*CONSTCOND*/ 0) if (m == NULL || ((m->m_len < sizeof(int32_t)) && - (m = m_pullup(m, sizeof(int32_t))) == NULL)) - return ENOBUFS; + (m = m_pullup(m, sizeof(int32_t))) == NULL)) { + error = ENOBUFS; + goto out; + } if ((m->m_flags & M_PKTHDR) == 0) panic("%s", __func__); len = m->m_pkthdr.len; @@ -807,30 +811,45 @@ COMPATNAME(route_output)(struct mbuf *m, struct socket *so) } break; - case RTM_CHANGE: + case RTM_CHANGE: { + struct ifnet *_ifp; + struct ifaddr *_ifa; + struct psref _psref, psref_ifp; /* * new gateway could require new ifaddr, ifp; * flags may also be different; ifp may be specified * by ll sockaddr when protocol address is ambiguous */ - if ((error = rt_getifa(&info)) != 0) - senderr(error); + _ifp = rt_getifp(&info, &psref_ifp); + ifa = rt_getifa(&info, &psref); + if (ifa == NULL) { + if_put(_ifp, &psref_ifp); + senderr(ENETUNREACH); + } if (info.rti_info[RTAX_GATEWAY]) { error = rt_setgate(rt, info.rti_info[RTAX_GATEWAY]); - if (error != 0) + if (error != 0) { + if_put(_ifp, &psref_ifp); senderr(error); + } } if (info.rti_info[RTAX_TAG]) { const struct sockaddr *tag; tag = rt_settag(rt, info.rti_info[RTAX_TAG]); - if (tag == NULL) + if (tag == NULL) { + if_put(_ifp, &psref_ifp); senderr(ENOBUFS); + } } /* new gateway could require new ifaddr, ifp; flags may also be different; ifp may be specified by ll sockaddr when protocol address is ambiguous */ - ifa = route_output_get_ifa(info, rt, &ifp); + _ifa = route_output_get_ifa(info, rt, &ifp, &_psref); + if (_ifa != NULL) { + ifa_release(ifa, &psref); + ifa = _ifa; + } if (ifa) { struct ifaddr *oifa = rt->rt_ifa; if (oifa != ifa) { @@ -841,7 +860,10 @@ COMPATNAME(route_output)(struct mbuf *m, struct socket *so) rt_replace_ifa(rt, ifa); rt->rt_ifp = ifp; } + if (_ifa == NULL) + ifa_release(ifa, &psref); } + ifa_release(_ifa, &_psref); if (ifp && rt->rt_ifp != ifp) rt->rt_ifp = ifp; rt_setmetrics(rtm->rtm_inits, rtm, rt); @@ -850,7 +872,9 @@ COMPATNAME(route_output)(struct mbuf *m, struct socket *so) | (rt->rt_flags & PRESERVED_RTF); if (rt->rt_ifa && rt->rt_ifa->ifa_rtrequest) rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, &info); + if_put(_ifp, &psref_ifp); /*FALLTHROUGH*/ + } case RTM_LOCK: rt->rt_rmx.rmx_locks &= ~(rtm->rtm_inits); rt->rt_rmx.rmx_locks |= @@ -890,7 +914,7 @@ flush: if (rtm) Free(rtm); m_freem(m); - return error; + goto out; } /* There is another listener, so construct message */ rp = sotorawcb(so); @@ -914,6 +938,8 @@ flush: if (rp) rp->rcb_proto.sp_family = PF_XROUTE; } +out: + curlwp_bindx(bound); return error; } diff --git a/sys/netatalk/aarp.c b/sys/netatalk/aarp.c index 65655d1..1321779 100644 --- a/sys/netatalk/aarp.c +++ b/sys/netatalk/aarp.c @@ -131,7 +131,7 @@ aarptimer(void *ignored) struct ifaddr * at_ifawithnet(const struct sockaddr_at *sat, struct ifnet *ifp) { - struct ifaddr *ifa; + struct ifaddr *ifa; struct sockaddr_at *sat2; struct netrange *nr; @@ -149,6 +149,7 @@ at_ifawithnet(const struct sockaddr_at *sat, struct ifnet *ifp) && (ntohs(nr->nr_lastnet) >= ntohs(sat->sat_addr.s_net))) break; } + return ifa; } @@ -251,17 +252,24 @@ aarpresolve(struct ifnet *ifp, struct mbuf *m, int s; if (at_broadcast(destsat)) { - aa = (struct at_ifaddr *) at_ifawithnet(destsat, ifp); - if (aa == NULL) { + struct ifaddr *ifa; + + s = pserialize_read_enter(); + ifa = at_ifawithnet(destsat, ifp); + if (ifa == NULL) { + pserialize_read_exit(s); m_freem(m); return (0); } + aa = (struct at_ifaddr *)ifa; + if (aa->aa_flags & AFA_PHASE2) memcpy(desten, atmulticastaddr, sizeof(atmulticastaddr)); else memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr)); + pserialize_read_exit(s); return 1; } s = splnet(); @@ -331,7 +339,6 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) { struct ether_aarp *ea; struct at_ifaddr *aa; - struct ifaddr *ia; struct aarptab *aat; struct ether_header *eh; struct llc *llc; @@ -340,6 +347,9 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) struct at_addr spa, tpa, ma; int op; u_int16_t net; + int s; + struct psref psref; + struct ifaddr *ifa; ea = mtod(m, struct ether_aarp *); @@ -355,11 +365,18 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) sat.sat_len = sizeof(struct sockaddr_at); sat.sat_family = AF_APPLETALK; sat.sat_addr.s_net = net; - aa = (struct at_ifaddr *) at_ifawithnet(&sat, ifp); - if (aa == NULL) { + + s = pserialize_read_enter(); + ifa = at_ifawithnet(&sat, ifp); + if (ifa == NULL) { + pserialize_read_exit(s); m_freem(m); return; } + ifa_acquire(ifa, &psref); + pserialize_read_exit(s); + aa = (struct at_ifaddr *)ifa; + memcpy(&spa.s_net, ea->aarp_spnet, sizeof(spa.s_net)); memcpy(&tpa.s_net, ea->aarp_tpnet, sizeof(tpa.s_net)); } else { @@ -367,13 +384,18 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) * Since we don't know the net, we just look for the first * phase 1 address on the interface. */ - IFADDR_READER_FOREACH(ia, ifp) { - aa = (struct at_ifaddr *)ia; + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { + aa = (struct at_ifaddr *)ifa; if (AA_SAT(aa)->sat_family == AF_APPLETALK && - (aa->aa_flags & AFA_PHASE2) == 0) + (aa->aa_flags & AFA_PHASE2) == 0) { + ifa_acquire(ifa, &psref); break; + } } - if (ia == NULL) { + pserialize_read_exit(s); + + if (ifa == NULL) { m_freem(m); return; } @@ -398,7 +420,7 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) callout_stop(&aa->aa_probe_ch); wakeup(aa); m_freem(m); - return; + goto out; } else if (op != AARPOP_PROBE) { /* * This is not a probe, and we're not probing. @@ -408,7 +430,7 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) log(LOG_ERR, "aarp: duplicate AT address!! %s\n", ether_sprintf(ea->aarp_sha)); m_freem(m); - return; + goto out; } } AARPTAB_LOOK(aat, spa); @@ -421,7 +443,7 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) */ aarptfree(aat); m_freem(m); - return; + goto out; } memcpy(aat->aat_enaddr, ea->aarp_sha, sizeof(ea->aarp_sha)); aat->aat_flags |= ATF_COM; @@ -449,7 +471,7 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) if (tpa.s_net != ma.s_net || tpa.s_node != ma.s_node || op == AARPOP_RESPONSE || (aa->aa_flags & AFA_PROBING)) { m_freem(m); - return; + goto out; } /* @@ -467,7 +489,7 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) if (aa->aa_flags & AFA_PHASE2) { M_PREPEND(m, sizeof(struct llc), M_DONTWAIT); if (m == NULL) - return; + goto out; llc = mtod(m, struct llc *); llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; @@ -489,6 +511,8 @@ at_aarpinput(struct ifnet *ifp, struct mbuf *m) sa.sa_len = sizeof(struct sockaddr); sa.sa_family = AF_UNSPEC; (*ifp->if_output) (ifp, m, &sa, NULL); /* XXX */ +out: + ifa_release(ifa, &psref); return; } diff --git a/sys/netatalk/at_control.c b/sys/netatalk/at_control.c index ddcfd2a..e625022 100644 --- a/sys/netatalk/at_control.c +++ b/sys/netatalk/at_control.c @@ -194,6 +194,7 @@ at_control(u_long cmd, void *data, struct ifnet *ifp) TAILQ_INSERT_TAIL(&at_ifaddr, aa, aa_list); } ifaref(&aa->aa_ifa); + ifa_psref_init(&aa->aa_ifa); /* * Find the end of the interface's addresses diff --git a/sys/netinet/if_arp.c b/sys/netinet/if_arp.c index 42b1c19..fd8f648 100644 --- a/sys/netinet/if_arp.c +++ b/sys/netinet/if_arp.c @@ -446,6 +446,8 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) struct in_ifaddr *ia; struct ifaddr *ifa; struct ifnet *ifp = rt->rt_ifp; + int bound; + int s; if (req == RTM_LLINFO_UPD) { struct in_addr *in; @@ -553,10 +555,13 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) if (rt->rt_flags & RTF_CONNECTED) break; } + + bound = curlwp_bind(); /* Announce a new entry if requested. */ if (rt->rt_flags & RTF_ANNOUNCE) { - ia = in_get_ia_on_iface( - satocsin(rt_getkey(rt))->sin_addr, ifp); + struct psref psref; + ia = in_get_ia_on_iface_psref( + satocsin(rt_getkey(rt))->sin_addr, ifp, &psref); if (ia == NULL || ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED)) ; @@ -565,12 +570,14 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) &satocsin(rt_getkey(rt))->sin_addr, &satocsin(rt_getkey(rt))->sin_addr, CLLADDR(satocsdl(gate))); + if (ia != NULL) + ia4_release(ia, &psref); } if (gate->sa_family != AF_LINK || gate->sa_len < sockaddr_dl_measure(0, ifp->if_addrlen)) { log(LOG_DEBUG, "%s: bad gateway value\n", __func__); - break; + goto out; } satosdl(gate)->sdl_type = ifp->if_type; @@ -586,7 +593,7 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) rt->rt_flags |= RTF_BROADCAST; /* There is little point in resolving the broadcast address */ if (rt->rt_flags & RTF_BROADCAST) - break; + goto out; /* * When called from rt_ifa_addlocal, we cannot depend on that @@ -599,12 +606,15 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) rt->rt_ifp = lo0ifp; rt->rt_rmx.rmx_mtu = 0; } - break; + goto out; } + s = pserialize_read_enter(); ia = in_get_ia_on_iface(satocsin(rt_getkey(rt))->sin_addr, ifp); - if (ia == NULL) - break; + if (ia == NULL) { + pserialize_read_exit(s); + goto out; + } rt->rt_expire = 0; if (useloopback) { @@ -619,7 +629,11 @@ arp_rtrequest(int req, struct rtentry *rt, const struct rt_addrinfo *info) */ ifa = &ia->ia_ifa; if (ifa != rt->rt_ifa) + /* Assume it doesn't sleep */ rt_replace_ifa(rt, ifa); + pserialize_read_exit(s); + out: + curlwp_bindx(bound); break; } } @@ -971,7 +985,7 @@ in_arpinput(struct mbuf *m) struct arphdr *ah; struct ifnet *ifp, *rcvif = NULL; struct llentry *la = NULL; - struct in_ifaddr *ia; + struct in_ifaddr *ia = NULL; #if NBRIDGE > 0 struct in_ifaddr *bridge_ia = NULL; #endif @@ -983,7 +997,8 @@ in_arpinput(struct mbuf *m) int op; void *tha; uint64_t *arps; - struct psref psref; + struct psref psref, psref_ia; + int s; if (__predict_false(m_makewritable(&m, 0, m->m_pkthdr.len, M_DONTWAIT))) goto out; @@ -1024,6 +1039,7 @@ in_arpinput(struct mbuf *m) * or any address on the interface to use * as a dummy address in the rest of this function */ + s = pserialize_read_enter(); IN_ADDRHASH_READER_FOREACH(ia, itaddr.s_addr) { if (!in_hosteq(ia->ia_addr.sin_addr, itaddr)) continue; @@ -1064,11 +1080,14 @@ in_arpinput(struct mbuf *m) ifp = bridge_ia->ia_ifp; } #endif + if (ia != NULL) + ia4_acquire(ia, &psref_ia); + pserialize_read_exit(s); if (ia == NULL) { - ia = in_get_ia_on_iface(isaddr, rcvif); + ia = in_get_ia_on_iface_psref(isaddr, rcvif, &psref_ia); if (ia == NULL) { - ia = in_get_ia_from_ifp(ifp); + ia = in_get_ia_from_ifp_psref(ifp, &psref_ia); if (ia == NULL) { ARP_STATINC(ARP_STAT_RCVNOINT); goto out; @@ -1289,7 +1308,6 @@ reply: struct llentry *lle = NULL; struct sockaddr_in sin; #if NCARP > 0 - int s; struct ifnet *_rcvif = m_get_rcvif(m, &s); if (ifp->if_type == IFT_CARP && _rcvif->if_type != IFT_CARP) goto out; @@ -1314,6 +1332,7 @@ reply: goto drop; } } + ia4_release(ia, &psref_ia); memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); memcpy(ar_spa(ah), &itaddr, ah->ar_pln); @@ -1350,6 +1369,8 @@ out: if (la != NULL) LLE_WUNLOCK(la); drop: + if (ia != NULL) + ia4_release(ia, &psref_ia); if (rcvif != NULL) m_put_rcvif_psref(rcvif, &psref); m_freem(m); diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c index 6f1cd5a..c9dd3aa 100644 --- a/sys/netinet/igmp.c +++ b/sys/netinet/igmp.c @@ -361,9 +361,11 @@ igmp_input(struct mbuf *m, ...) * determine the arrival interface of an incoming packet. */ if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { - ia = in_get_ia_from_ifp(ifp); /* XXX */ + int s = pserialize_read_enter(); + ia = in_get_ia_from_ifp(ifp); /* XXX */ if (ia) ip->ip_src.s_addr = ia->ia_subnet; + pserialize_read_exit(s); } /* @@ -394,26 +396,32 @@ igmp_input(struct mbuf *m, ...) in_multi_unlock(); break; - case IGMP_v2_HOST_MEMBERSHIP_REPORT: + case IGMP_v2_HOST_MEMBERSHIP_REPORT: { + int s = pserialize_read_enter(); #ifdef MROUTING /* * Make sure we don't hear our own membership report. Fast * leave requires knowing that we are the only member of a * group. */ - ia = in_get_ia_from_ifp(ifp); /* XXX */ - if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) + ia = in_get_ia_from_ifp(ifp); /* XXX */ + if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) { + pserialize_read_exit(s); break; + } #endif IGMP_STATINC(IGMP_STAT_RCV_REPORTS); - if (ifp->if_flags & IFF_LOOPBACK) + if (ifp->if_flags & IFF_LOOPBACK) { + pserialize_read_exit(s); break; + } if (!IN_MULTICAST(igmp->igmp_group.s_addr) || !in_hosteq(igmp->igmp_group, ip->ip_dst)) { IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS); + pserialize_read_exit(s); goto drop; } @@ -428,11 +436,12 @@ igmp_input(struct mbuf *m, ...) */ if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) { #ifndef MROUTING - ia = in_get_ia_from_ifp(ifp); /* XXX */ + ia = in_get_ia_from_ifp(ifp); /* XXX */ #endif if (ia) ip->ip_src.s_addr = ia->ia_subnet; } + pserialize_read_exit(s); /* * If we belong to the group being reported, stop @@ -457,7 +466,7 @@ igmp_input(struct mbuf *m, ...) } in_multi_unlock(); break; - + } } m_put_rcvif_psref(ifp, &psref); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index 90354f8..0a5e112 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -189,6 +189,7 @@ static krwlock_t in_multilock; struct in_ifaddrhashhead * in_ifaddrhashtbl; u_long in_ifaddrhash; struct in_ifaddrhead in_ifaddrhead; +static kmutex_t in_ifaddr_lock; struct pslist_head * in_ifaddrhashtbl_pslist; u_long in_ifaddrhash_pslist; @@ -204,8 +205,11 @@ in_init(void) in_ifaddrhashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, true, &in_ifaddrhash); + in_ifaddrhashtbl_pslist = hashinit(IN_IFADDR_HASH_SIZE, HASH_PSLIST, true, &in_ifaddrhash_pslist); + mutex_init(&in_ifaddr_lock, MUTEX_DEFAULT, IPL_NONE); + in_multihashtbl = hashinit(IN_IFADDR_HASH_SIZE, HASH_LIST, true, &in_multihash); rw_init(&in_multilock); @@ -223,19 +227,27 @@ int in_localaddr(struct in_addr in) { struct in_ifaddr *ia; + int localaddr = 0; + int s = pserialize_read_enter(); if (subnetsarelocal) { IN_ADDRLIST_READER_FOREACH(ia) { - if ((in.s_addr & ia->ia_netmask) == ia->ia_net) - return (1); + if ((in.s_addr & ia->ia_netmask) == ia->ia_net) { + localaddr = 1; + break; + } } } else { IN_ADDRLIST_READER_FOREACH(ia) { - if ((in.s_addr & ia->ia_subnetmask) == ia->ia_subnet) - return (1); + if ((in.s_addr & ia->ia_subnetmask) == ia->ia_subnet) { + localaddr = 1; + break; + } } } - return (0); + pserialize_read_exit(s); + + return localaddr; } /* @@ -307,6 +319,7 @@ in_setmaxmtu(void) struct in_ifaddr *ia; struct ifnet *ifp; unsigned long maxmtu = 0; + int s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if ((ifp = ia->ia_ifp) == 0) @@ -318,6 +331,7 @@ in_setmaxmtu(void) } if (maxmtu) in_maxmtu = maxmtu; + pserialize_read_exit(s); } static u_int @@ -371,6 +385,8 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) int newifaddr = 0; bool run_hook = false; bool need_reinsert = false; + struct psref psref; + int bound; switch (cmd) { case SIOCALIFADDR: @@ -386,11 +402,12 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) return ifaddrpref_ioctl(so, cmd, data, ifp); } + bound = curlwp_bind(); /* * Find address for this interface, if it exists. */ if (ifp != NULL) - ia = in_get_ia_from_ifp(ifp); + ia = in_get_ia_from_ifp_psref(ifp, &psref); hostIsNew = 1; /* moved here to appease gcc */ switch (cmd) { @@ -399,6 +416,11 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) case SIOCGIFALIAS: case SIOCGIFAFLAG_IN: if (ifra->ifra_addr.sin_family == AF_INET) { + int s; + + if (ia != NULL) + ia4_release(ia, &psref); + s = pserialize_read_enter(); IN_ADDRHASH_READER_FOREACH(ia, ifra->ifra_addr.sin_addr.s_addr) { if (ia->ia_ifp == ifp && @@ -406,12 +428,17 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) ifra->ifra_addr.sin_addr)) break; } + if (ia != NULL) + ia4_acquire(ia, &psref); + pserialize_read_exit(s); } if ((cmd == SIOCDIFADDR || cmd == SIOCGIFALIAS || cmd == SIOCGIFAFLAG_IN) && - ia == NULL) - return (EADDRNOTAVAIL); + ia == NULL) { + error = EADDRNOTAVAIL; + goto out; + } if (cmd == SIOCDIFADDR && ifra->ifra_addr.sin_family == AF_UNSPEC) { @@ -429,8 +456,10 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) hostIsNew = 0; /* FALLTHROUGH */ case SIOCSIFDSTADDR: - if (ifra->ifra_addr.sin_family != AF_INET) - return (EAFNOSUPPORT); + if (ifra->ifra_addr.sin_family != AF_INET) { + error = EAFNOSUPPORT; + goto out; + } /* FALLTHROUGH */ case SIOCSIFNETMASK: if (ifp == NULL) @@ -440,18 +469,24 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; if (ia == NULL && - (cmd == SIOCSIFNETMASK || cmd == SIOCSIFDSTADDR)) - return (EADDRNOTAVAIL); + (cmd == SIOCSIFNETMASK || cmd == SIOCSIFDSTADDR)) { + error = EADDRNOTAVAIL; + goto out; + } if (kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_INTERFACE, KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, - NULL) != 0) - return (EPERM); + NULL) != 0) { + error = EPERM; + goto out; + } if (ia == NULL) { ia = malloc(sizeof(*ia), M_IFADDR, M_WAITOK|M_ZERO); - if (ia == NULL) - return (ENOBUFS); + if (ia == NULL) { + error = ENOBUFS; + goto out; + } ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr); ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr); ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask); @@ -471,6 +506,7 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) LIST_INIT(&ia->ia_multiaddrs); IN_ADDRHASH_ENTRY_INIT(ia); IN_ADDRLIST_ENTRY_INIT(ia); + ifa_psref_init(&ia->ia_ifa); newifaddr = 1; } @@ -479,16 +515,20 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) case SIOCSIFBRDADDR: if (kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_INTERFACE, KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, - NULL) != 0) - return (EPERM); + NULL) != 0) { + error = EPERM; + goto out; + } /* FALLTHROUGH */ case SIOCGIFADDR: case SIOCGIFNETMASK: case SIOCGIFDSTADDR: case SIOCGIFBRDADDR: - if (ia == NULL) - return (EADDRNOTAVAIL); + if (ia == NULL) { + error = EADDRNOTAVAIL; + goto out; + } break; } error = 0; @@ -499,14 +539,18 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCGIFBRDADDR: - if ((ifp->if_flags & IFF_BROADCAST) == 0) - return (EINVAL); + if ((ifp->if_flags & IFF_BROADCAST) == 0) { + error = EINVAL; + goto out; + } ifreq_setdstaddr(cmd, ifr, sintocsa(&ia->ia_broadaddr)); break; case SIOCGIFDSTADDR: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return (EINVAL); + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + error = EINVAL; + goto out; + } ifreq_setdstaddr(cmd, ifr, sintocsa(&ia->ia_dstaddr)); break; @@ -522,13 +566,15 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCSIFDSTADDR: - if ((ifp->if_flags & IFF_POINTOPOINT) == 0) - return (EINVAL); + if ((ifp->if_flags & IFF_POINTOPOINT) == 0) { + error = EINVAL; + goto out; + } oldaddr = ia->ia_dstaddr; ia->ia_dstaddr = *satocsin(ifreq_getdstaddr(cmd, ifr)); if ((error = if_addr_init(ifp, &ia->ia_ifa, false)) != 0) { ia->ia_dstaddr = oldaddr; - return error; + goto out; } if (ia->ia_flags & IFA_ROUTE) { ia->ia_ifa.ifa_dstaddr = sintosa(&oldaddr); @@ -539,15 +585,19 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCSIFBRDADDR: - if ((ifp->if_flags & IFF_BROADCAST) == 0) - return EINVAL; + if ((ifp->if_flags & IFF_BROADCAST) == 0) { + error = EINVAL; + goto out; + } ia->ia_broadaddr = *satocsin(ifreq_getbroadaddr(cmd, ifr)); break; case SIOCSIFADDR: if (!newifaddr) { + mutex_enter(&in_ifaddr_lock); LIST_REMOVE(ia, ia_hash); IN_ADDRHASH_WRITER_REMOVE(ia); + mutex_exit(&in_ifaddr_lock); need_reinsert = true; } error = in_ifinit(ifp, ia, satocsin(ifreq_getaddr(cmd, ifr)), @@ -561,8 +611,10 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) ia->ia_sockmask = *satocsin(ifreq_getaddr(cmd, ifr)); ia->ia_subnetmask = ia->ia_sockmask.sin_addr.s_addr; if (!newifaddr) { + mutex_enter(&in_ifaddr_lock); LIST_REMOVE(ia, ia_hash); IN_ADDRHASH_WRITER_REMOVE(ia); + mutex_exit(&in_ifaddr_lock); need_reinsert = true; } error = in_ifinit(ifp, ia, NULL, 0, 0); @@ -591,8 +643,10 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) if (ifra->ifra_addr.sin_family == AF_INET && (hostIsNew || maskIsNew)) { if (!newifaddr) { + mutex_enter(&in_ifaddr_lock); LIST_REMOVE(ia, ia_hash); IN_ADDRHASH_WRITER_REMOVE(ia); + mutex_exit(&in_ifaddr_lock); need_reinsert = true; } error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0, @@ -622,7 +676,9 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) break; case SIOCDIFADDR: + ia4_release(ia, &psref); in_purgeaddr(&ia->ia_ifa); + ia = NULL; run_hook = true; break; @@ -634,7 +690,8 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) #endif /* MROUTING */ default: - return ENOTTY; + error = ENOTTY; + goto out; } /* @@ -642,17 +699,22 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) * Need to improve. */ if (newifaddr) { - TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_list); ifaref(&ia->ia_ifa); ifa_insert(ifp, &ia->ia_ifa); + + mutex_enter(&in_ifaddr_lock); + TAILQ_INSERT_TAIL(&in_ifaddrhead, ia, ia_list); IN_ADDRLIST_WRITER_INSERT_TAIL(ia); LIST_INSERT_HEAD(&IN_IFADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); IN_ADDRHASH_WRITER_INSERT_HEAD(ia); + mutex_exit(&in_ifaddr_lock); } else if (need_reinsert) { + mutex_enter(&in_ifaddr_lock); LIST_INSERT_HEAD(&IN_IFADDR_HASH(ia->ia_addr.sin_addr.s_addr), ia, ia_hash); IN_ADDRHASH_WRITER_INSERT_HEAD(ia); + mutex_exit(&in_ifaddr_lock); } if (error == 0) { @@ -662,8 +724,13 @@ in_control0(struct socket *so, u_long cmd, void *data, struct ifnet *ifp) } else if (newifaddr) { KASSERT(ia != NULL); in_purgeaddr(&ia->ia_ifa); + ia = NULL; } +out: + if (!newifaddr && ia != NULL) + ia4_release(ia, &psref); + curlwp_bindx(bound); return error; } @@ -704,10 +771,14 @@ in_ifremlocal(struct ifaddr *ifa) struct in_ifaddr *ia, *p; struct ifaddr *alt_ifa = NULL; int ia_count = 0; + int s; + struct psref psref; + int bound = curlwp_bind(); ia = (struct in_ifaddr *)ifa; /* Delete the entry if exactly one ifaddr matches the * address, ifa->ifa_addr. */ + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(p) { if (!in_hosteq(p->ia_addr.sin_addr, ia->ia_addr.sin_addr)) continue; @@ -716,35 +787,54 @@ in_ifremlocal(struct ifaddr *ifa) if (++ia_count > 1 && alt_ifa != NULL) break; } + if (alt_ifa != NULL && ia_count > 1) + ifa_acquire(alt_ifa, &psref); + pserialize_read_exit(s); if (ia_count == 0) - return; + goto out; rt_ifa_remlocal(ifa, ia_count == 1 ? NULL : alt_ifa); + if (alt_ifa != NULL && ia_count > 1) + ifa_release(alt_ifa, &psref); +out: + curlwp_bindx(bound); } +/* + * Depends on it isn't called in concurrent. It should be guaranteed + * by ifa->ifa_ifp's ioctl lock. The possible callers are in_control + * and if_purgeaddrs; the former is called iva ifa->ifa_ifp's ioctl + * and the latter is called via ifa->ifa_ifp's if_detach. The functions + * never be executed in concurrent. + */ void in_purgeaddr(struct ifaddr *ifa) { struct ifnet *ifp = ifa->ifa_ifp; struct in_ifaddr *ia = (void *) ifa; + KASSERT(!ifa_held(ifa)); + /* stop DAD processing */ if (ia->ia_dad_stop != NULL) ia->ia_dad_stop(ifa); in_ifscrub(ifp, ia); in_ifremlocal(ifa); + if (ia->ia_allhosts != NULL) + in_delmulti(ia->ia_allhosts); + + mutex_enter(&in_ifaddr_lock); LIST_REMOVE(ia, ia_hash); IN_ADDRHASH_WRITER_REMOVE(ia); - IN_ADDRHASH_ENTRY_DESTROY(ia); - ifa_remove(ifp, &ia->ia_ifa); TAILQ_REMOVE(&in_ifaddrhead, ia, ia_list); IN_ADDRLIST_WRITER_REMOVE(ia); + ifa_remove(ifp, &ia->ia_ifa); + mutex_exit(&in_ifaddr_lock); + + IN_ADDRHASH_ENTRY_DESTROY(ia); IN_ADDRLIST_ENTRY_DESTROY(ia); - - if (ia->ia_allhosts != NULL) - in_delmulti(ia->ia_allhosts); ifafree(&ia->ia_ifa); in_setmaxmtu(); } @@ -1078,6 +1168,7 @@ in_addprefix(struct in_ifaddr *target, int flags) struct in_ifaddr *ia; struct in_addr prefix, mask, p; int error; + int s; if ((flags & RTF_HOST) != 0) prefix = target->ia_dstaddr.sin_addr; @@ -1087,6 +1178,7 @@ in_addprefix(struct in_ifaddr *target, int flags) prefix.s_addr &= mask.s_addr; } + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if (rtinitflags(ia)) p = ia->ia_dstaddr.sin_addr; @@ -1104,9 +1196,12 @@ in_addprefix(struct in_ifaddr *target, int flags) * * XXX RADIX_MPATH implications here? -dyoung */ - if (ia->ia_flags & IFA_ROUTE) + if (ia->ia_flags & IFA_ROUTE) { + pserialize_read_exit(s); return 0; + } } + pserialize_read_exit(s); /* * noone seem to have prefix route. insert it. @@ -1134,6 +1229,7 @@ in_scrubprefix(struct in_ifaddr *target) struct in_ifaddr *ia; struct in_addr prefix, mask, p; int error; + int s; /* If we don't have IFA_ROUTE we should still inform userland */ if ((target->ia_flags & IFA_ROUTE) == 0) @@ -1147,6 +1243,7 @@ in_scrubprefix(struct in_ifaddr *target) prefix.s_addr &= mask.s_addr; } + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if (rtinitflags(ia)) p = ia->ia_dstaddr.sin_addr; @@ -1162,6 +1259,12 @@ in_scrubprefix(struct in_ifaddr *target) * if we got a matching prefix route, move IFA_ROUTE to him */ if ((ia->ia_flags & IFA_ROUTE) == 0) { + struct psref psref; + int bound = curlwp_bind(); + + ia4_acquire(ia, &psref); + pserialize_read_exit(s); + rtinit(&target->ia_ifa, RTM_DELETE, rtinitflags(target)); target->ia_flags &= ~IFA_ROUTE; @@ -1170,9 +1273,14 @@ in_scrubprefix(struct in_ifaddr *target) rtinitflags(ia) | RTF_UP); if (error == 0) ia->ia_flags |= IFA_ROUTE; + + ia4_release(ia, &psref); + curlwp_bindx(bound); + return error; } } + pserialize_read_exit(s); /* * noone seem to have prefix route. remove it. @@ -1191,6 +1299,9 @@ int in_broadcast(struct in_addr in, struct ifnet *ifp) { struct ifaddr *ifa; + int s; + + KASSERT(ifp != NULL); if (in.s_addr == INADDR_BROADCAST || in_nullhost(in)) @@ -1202,7 +1313,8 @@ in_broadcast(struct in_addr in, struct ifnet *ifp) * with a broadcast address. */ #define ia (ifatoia(ifa)) - IFADDR_READER_FOREACH(ifa, ifp) + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, ifp) { if (ifa->ifa_addr->sa_family == AF_INET && !in_hosteq(in, ia->ia_addr.sin_addr) && (in_hosteq(in, ia->ia_broadaddr.sin_addr) || @@ -1212,8 +1324,12 @@ in_broadcast(struct in_addr in, struct ifnet *ifp) * Check for old-style (host 0) broadcast. */ (in.s_addr == ia->ia_subnet || - in.s_addr == ia->ia_net)))) + in.s_addr == ia->ia_net)))) { + pserialize_read_exit(s); return 1; + } + } + pserialize_read_exit(s); return (0); #undef ia } @@ -1505,13 +1621,14 @@ in_multi_lock_held(void) return rw_lock_held(&in_multilock); } -struct sockaddr_in * +struct in_ifaddr * in_selectsrc(struct sockaddr_in *sin, struct route *ro, - int soopts, struct ip_moptions *mopts, int *errorp) + int soopts, struct ip_moptions *mopts, int *errorp, struct psref *psref) { struct rtentry *rt = NULL; struct in_ifaddr *ia = NULL; + KASSERT(ISSET(curlwp->l_pflag, LP_BOUND)); /* * If route is known or can be allocated now, take the * source address from the interface. Otherwise, punt. @@ -1535,20 +1652,45 @@ in_selectsrc(struct sockaddr_in *sin, struct route *ro, * * XXX Is this still true? Do we care? */ - if (rt != NULL && (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) - ia = ifatoia(rt->rt_ifa); + if (rt != NULL && (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) { + int s; + struct ifaddr *ifa; + /* + * Just in case. May not need to do this workaround. + * Revisit when working on rtentry MP-ification. + */ + s = pserialize_read_enter(); + IFADDR_READER_FOREACH(ifa, rt->rt_ifp) { + if (ifa == rt->rt_ifa) + break; + } + if (ifa != NULL) + ifa_acquire(ifa, psref); + pserialize_read_exit(s); + + ia = ifatoia(ifa); + } if (ia == NULL) { u_int16_t fport = sin->sin_port; + struct ifaddr *ifa; + int s; sin->sin_port = 0; - ia = ifatoia(ifa_ifwithladdr(sintosa(sin))); + ifa = ifa_ifwithladdr_psref(sintosa(sin), psref); sin->sin_port = fport; - if (ia == NULL) { + if (ifa == NULL) { /* Find 1st non-loopback AF_INET address */ + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if (!(ia->ia_ifp->if_flags & IFF_LOOPBACK)) break; } + if (ia != NULL) + ia4_acquire(ia, psref); + pserialize_read_exit(s); + } else { + /* ia is already referenced by psref */ + ia = ifatoia(ifa); } if (ia == NULL) { *errorp = EADDRNOTAVAIL; @@ -1566,15 +1708,21 @@ in_selectsrc(struct sockaddr_in *sin, struct route *ro, imo = mopts; if (imo->imo_multicast_if_index != 0) { struct ifnet *ifp; - int s = pserialize_read_enter(); + int s; + if (ia != NULL) + ia4_release(ia, psref); + s = pserialize_read_enter(); ifp = if_byindex(imo->imo_multicast_if_index); if (ifp != NULL) { - ia = in_get_ia_from_ifp(ifp); /* XXX */ + /* XXX */ + ia = in_get_ia_from_ifp_psref(ifp, psref); } else ia = NULL; if (ia == NULL || ia->ia4_flags & IN_IFF_NOTREADY) { pserialize_read_exit(s); + if (ia != NULL) + ia4_release(ia, psref); *errorp = EADDRNOTAVAIL; return NULL; } @@ -1588,12 +1736,14 @@ in_selectsrc(struct sockaddr_in *sin, struct route *ro, *errorp = EADDRNOTAVAIL; return NULL; } + /* FIXME NOMPSAFE */ + ia4_acquire(ia, psref); } #ifdef GETIFA_DEBUG else printf("%s: missing ifa_getifa\n", __func__); #endif - return &ia->ia_addr; + return ia; } #if NARP > 0 diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 582dac8..a64e20b 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -507,6 +507,8 @@ struct ip_mreq { #undef __KAME_NETINET_IN_H_INCLUDED_ #ifdef _KERNEL +#include + /* * in_cksum_phdr: * @@ -576,8 +578,8 @@ void in_if_link_state_change(struct ifnet *, int); struct route; struct ip_moptions; -struct sockaddr_in *in_selectsrc(struct sockaddr_in *, - struct route *, int, struct ip_moptions *, int *); +struct in_ifaddr *in_selectsrc(struct sockaddr_in *, + struct route *, int, struct ip_moptions *, int *, struct psref *); #define in_hosteq(s,t) ((s).s_addr == (t).s_addr) #define in_nullhost(x) ((x).s_addr == INADDR_ANY) diff --git a/sys/netinet/in_gif.c b/sys/netinet/in_gif.c index 248dd1b..4b512b5 100644 --- a/sys/netinet/in_gif.c +++ b/sys/netinet/in_gif.c @@ -289,6 +289,7 @@ gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) { struct sockaddr_in *src, *dst; struct in_ifaddr *ia4; + int s; src = satosin(sc->gif_psrc); dst = satosin(sc->gif_pdst); @@ -306,12 +307,16 @@ gif_validate4(const struct ip *ip, struct gif_softc *sc, struct ifnet *ifp) return 0; } /* reject packets with broadcast on source */ + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia4) { if ((ia4->ia_ifa.ifa_ifp->if_flags & IFF_BROADCAST) == 0) continue; - if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) + if (ip->ip_src.s_addr == ia4->ia_broadaddr.sin_addr.s_addr) { + pserialize_read_exit(s); return 0; + } } + pserialize_read_exit(s); /* ingress filters on outer source */ if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) { diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 786697c..d0eb6c3 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -275,27 +275,39 @@ in_pcbsetport(struct sockaddr_in *sin, struct inpcb *inp, kauth_cred_t cred) static int in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred) { + int error = EADDRNOTAVAIL; + struct ifaddr *ifa = NULL; + int s; + if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); + s = pserialize_read_enter(); if (IN_MULTICAST(sin->sin_addr.s_addr)) { /* Always succeed; port reuse handled in in_pcbbind_port(). */ } else if (!in_nullhost(sin->sin_addr)) { - struct in_ifaddr *ia = NULL; + struct in_ifaddr *ia; ia = in_get_ia(sin->sin_addr); /* check for broadcast addresses */ + if (ia == NULL) { + ifa = ifa_ifwithaddr(sintosa(sin)); + if (ifa != NULL) + ia = ifatoia(ifa); + } if (ia == NULL) - ia = ifatoia(ifa_ifwithaddr(sintosa(sin))); - if (ia == NULL) - return (EADDRNOTAVAIL); + goto error; if (ia->ia4_flags & (IN_IFF_NOTREADY | IN_IFF_DETACHED)) - return (EADDRNOTAVAIL); + goto error; } + pserialize_read_exit(s); inp->inp_laddr = sin->sin_addr; return (0); +error: + pserialize_read_exit(s); + return error; } static int @@ -484,6 +496,7 @@ in_pcbconnect(void *v, struct sockaddr_in *sin, struct lwp *l) IN_ADDRLIST_READER_FIRST()->ia_addr.sin_addr; } else if (sin->sin_addr.s_addr == INADDR_BROADCAST) { struct in_ifaddr *ia; + int s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if (ia->ia_ifp->if_flags & IFF_BROADCAST) { sin->sin_addr = @@ -491,6 +504,7 @@ in_pcbconnect(void *v, struct sockaddr_in *sin, struct lwp *l) break; } } + pserialize_read_exit(s); } } /* @@ -507,26 +521,40 @@ in_pcbconnect(void *v, struct sockaddr_in *sin, struct lwp *l) */ if (in_nullhost(inp->inp_laddr)) { int xerror; - struct sockaddr_in *ifaddr; - struct in_ifaddr *ia; + struct in_ifaddr *ia, *_ia; + int s; + struct psref psref; + int bound; - ifaddr = in_selectsrc(sin, &inp->inp_route, - inp->inp_socket->so_options, inp->inp_moptions, &xerror); - if (ifaddr == NULL) { + bound = curlwp_bind(); + ia = in_selectsrc(sin, &inp->inp_route, + inp->inp_socket->so_options, inp->inp_moptions, &xerror, + &psref); + if (ia == NULL) { + curlwp_bindx(bound); if (xerror == 0) xerror = EADDRNOTAVAIL; return xerror; } - ia = in_get_ia(ifaddr->sin_addr); - if (ia == NULL) + s = pserialize_read_enter(); + _ia = in_get_ia(IA_SIN(ia)->sin_addr); + if (_ia == NULL) { + pserialize_read_exit(s); + ia4_release(ia, &psref); + curlwp_bindx(bound); return (EADDRNOTAVAIL); - laddr = ifaddr->sin_addr; + } + pserialize_read_exit(s); + laddr = IA_SIN(ia)->sin_addr; + ia4_release(ia, &psref); + curlwp_bindx(bound); } else laddr = inp->inp_laddr; if (in_pcblookup_connect(inp->inp_table, sin->sin_addr, sin->sin_port, - laddr, inp->inp_lport, &vestige) != 0 - || vestige.valid) + laddr, inp->inp_lport, &vestige) != NULL || + vestige.valid) { return (EADDRINUSE); + } if (in_nullhost(inp->inp_laddr)) { if (inp->inp_lport == 0) { error = in_pcbbind(inp, NULL, l); diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 7204340..1cd020c 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -111,6 +111,25 @@ struct in_ifaddr { #endif }; +#ifdef _KERNEL +static inline void +ia4_acquire(struct in_ifaddr *ia, struct psref *psref) +{ + + KASSERT(ia != NULL); + ifa_acquire(&ia->ia_ifa, psref); +} + +static inline void +ia4_release(struct in_ifaddr *ia, struct psref *psref) +{ + + if (ia == NULL) + return; + ifa_release(&ia->ia_ifa, psref); +} +#endif + struct in_aliasreq { char ifra_name[IFNAMSIZ]; /* if name, e.g. "en0" */ struct sockaddr_in ifra_addr; @@ -125,6 +144,8 @@ struct in_aliasreq { */ #define IA_SIN(ia) (&(((struct in_ifaddr *)(ia))->ia_addr)) +#define iatoifa(ia) (struct ifaddr *)(ia) + #ifdef _KERNEL /* Note: 61, 127, 251, 509, 1021, 2039 are good. */ @@ -238,6 +259,21 @@ in_get_ia(struct in_addr addr) return ia; } +static inline struct in_ifaddr * +in_get_ia_psref(struct in_addr addr, struct psref *psref) +{ + struct in_ifaddr *ia; + int s; + + s = pserialize_read_enter(); + ia = in_get_ia(addr); + if (ia != NULL) + ia4_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} + /* * Find whether an internet address (in_addr) belongs to a specified * interface. NULL if the address isn't ours. @@ -256,6 +292,21 @@ in_get_ia_on_iface(struct in_addr addr, struct ifnet *ifp) return ia; } +static inline struct in_ifaddr * +in_get_ia_on_iface_psref(struct in_addr addr, struct ifnet *ifp, struct psref *psref) +{ + struct in_ifaddr *ia; + int s; + + s = pserialize_read_enter(); + ia = in_get_ia_on_iface(addr, ifp); + if (ia != NULL) + ia4_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} + /* * Find an internet address structure (in_ifaddr) corresponding * to a given interface (ifnet structure). @@ -273,6 +324,21 @@ in_get_ia_from_ifp(struct ifnet *ifp) return ifatoia(ifa); } +static inline struct in_ifaddr * +in_get_ia_from_ifp_psref(struct ifnet *ifp, struct psref *psref) +{ + struct in_ifaddr *ia; + int s; + + s = pserialize_read_enter(); + ia = in_get_ia_from_ifp(ifp); + if (ia != NULL) + ia4_acquire(ia, psref); + pserialize_read_exit(s); + + return ia; +} + #include /* * IPv4 per-interface state. diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 71c2251..925450b 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -635,15 +635,18 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) if ((sc->sc_carpdev->if_flags & IFF_SIMPLEX) == 0) { struct sockaddr sa; struct ifaddr *ifa; + int s; memset(&sa, 0, sizeof(sa)); sa.sa_family = af; + s = pserialize_read_enter(); ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev); if (ifa && af == AF_INET) { struct ip *ip = mtod(m, struct ip *); if (ip->ip_src.s_addr == ifatoia(ifa)->ia_addr.sin_addr.s_addr) { + pserialize_read_exit(s); m_freem(m); return; } @@ -660,11 +663,13 @@ carp_proto_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af) if (IN6_IS_ADDR_LINKLOCAL(&in6_found)) in6_found.s6_addr16[1] = 0; if (IN6_ARE_ADDR_EQUAL(&in6_src, &in6_found)) { + pserialize_read_exit(s); m_freem(m); return; } } #endif /* INET6 */ + pserialize_read_exit(s); } nanotime(&sc->sc_if.if_lastchange); @@ -989,7 +994,6 @@ carp_send_ad(void *v) struct carp_header *ch_ptr; struct mbuf *m; int error, len, advbase, advskew, s; - struct ifaddr *ifa; struct sockaddr sa; KERNEL_LOCK(1, NULL); @@ -1029,6 +1033,8 @@ carp_send_ad(void *v) #ifdef INET if (sc->sc_naddrs) { struct ip *ip; + struct ifaddr *ifa; + int _s; MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { @@ -1057,12 +1063,14 @@ carp_send_ad(void *v) memset(&sa, 0, sizeof(sa)); sa.sa_family = AF_INET; + _s = pserialize_read_enter(); ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev); if (ifa == NULL) ip->ip_src.s_addr = 0; else ip->ip_src.s_addr = ifatoia(ifa)->ia_addr.sin_addr.s_addr; + pserialize_read_exit(_s); ip->ip_dst.s_addr = INADDR_CARP_GROUP; ch_ptr = (struct carp_header *)(&ip[1]); @@ -1110,6 +1118,8 @@ carp_send_ad(void *v) #ifdef INET6_notyet if (sc->sc_naddrs6) { struct ip6_hdr *ip6; + struct ifaddr *ifa; + int _s; MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { @@ -1134,12 +1144,14 @@ carp_send_ad(void *v) /* set the source address */ memset(&sa, 0, sizeof(sa)); sa.sa_family = AF_INET6; + _s = pserialize_read_enter(); ifa = ifaof_ifpforaddr(&sa, sc->sc_carpdev); if (ifa == NULL) /* This should never happen with IPv6 */ memset(&ip6->ip6_src, 0, sizeof(struct in6_addr)); else bcopy(ifatoia6(ifa)->ia_addr.sin6_addr.s6_addr, &ip6->ip6_src, sizeof(struct in6_addr)); + pserialize_read_exit(_s); /* set the multicast destination */ ip6->ip6_dst.s6_addr16[0] = htons(0xff02); @@ -1746,6 +1758,7 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) struct ifnet *ifp = sc->sc_carpdev; struct in_ifaddr *ia, *ia_if; int error = 0; + int s; if (sin->sin_addr.s_addr == 0) { if (!(sc->sc_if.if_flags & IFF_UP)) @@ -1758,6 +1771,7 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) /* we have to do this by hand to ensure we don't match on ourselves */ ia_if = NULL; + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { /* and, yeah, we need a multicast-capable iface too */ if (ia->ia_ifp != &sc->sc_if && @@ -1776,9 +1790,11 @@ carp_set_addr(struct carp_softc *sc, struct sockaddr_in *sin) if (ifp != ia->ia_ifp) return (EADDRNOTAVAIL); } else { + /* FIXME NOMPSAFE */ ifp = ia->ia_ifp; } } + pserialize_read_exit(s); if ((error = carp_set_ifp(sc, ifp))) return (error); @@ -1836,6 +1852,7 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) struct ifnet *ifp = sc->sc_carpdev; struct in6_ifaddr *ia, *ia_if; int error = 0; + int s; if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { if (!(sc->sc_if.if_flags & IFF_UP)) @@ -1848,6 +1865,7 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) /* we have to do this by hand to ensure we don't match on ourselves */ ia_if = NULL; + s = pserialize_read_enter(); IN6_ADDRLIST_READER_FOREACH(ia) { int i; @@ -1867,6 +1885,7 @@ carp_set_addr6(struct carp_softc *sc, struct sockaddr_in6 *sin6) ia_if = ia; } } + pserialize_read_exit(s); if (ia_if) { ia = ia_if; diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 473aad0..62112ed 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -562,7 +562,8 @@ icmp_input(struct mbuf *m, ...) case ICMP_MASKREQ: { struct ifnet *rcvif; - int s; + int s, ss; + struct ifaddr *ifa; if (icmpmaskrepl == 0) break; @@ -579,12 +580,15 @@ icmp_input(struct mbuf *m, ...) icmpdst.sin_addr = ip->ip_src; else icmpdst.sin_addr = ip->ip_dst; + ss = pserialize_read_enter(); rcvif = m_get_rcvif(m, &s); - ia = ifatoia(ifaof_ifpforaddr(sintosa(&icmpdst), - rcvif)); + ifa = ifaof_ifpforaddr(sintosa(&icmpdst), rcvif); m_put_rcvif(rcvif, &s); - if (ia == 0) + if (ifa == NULL) { + pserialize_read_exit(ss); break; + } + ia = ifatoia(ifa); icp->icmp_type = ICMP_MASKREPLY; icp->icmp_mask = ia->ia_sockmask.sin_addr.s_addr; if (in_nullhost(ip->ip_src)) { @@ -593,6 +597,7 @@ icmp_input(struct mbuf *m, ...) else if (ia->ia_ifp->if_flags & IFF_POINTOPOINT) ip->ip_src = ia->ia_dstaddr.sin_addr; } + pserialize_read_exit(ss); reflect: { uint64_t *icps = percpu_getref(icmpstat_percpu); @@ -693,7 +698,11 @@ icmp_reflect(struct mbuf *m) struct mbuf *opts = NULL; int optlen = (ip->ip_hl << 2) - sizeof(struct ip); struct ifnet *rcvif; - struct psref psref; + struct psref psref, psref_ia; + int s; + int bound; + + bound = curlwp_bind(); if (!in_canforward(ip->ip_src) && ((ip->ip_src.s_addr & IN_CLASSA_NET) != @@ -712,15 +721,18 @@ icmp_reflect(struct mbuf *m) */ /* Look for packet addressed to us */ - ia = in_get_ia(t); - if (ia && (ia->ia4_flags & IN_IFF_NOTREADY)) + ia = in_get_ia_psref(t, &psref_ia); + if (ia && (ia->ia4_flags & IN_IFF_NOTREADY)) { + ia4_release(ia, &psref_ia); ia = NULL; + } rcvif = m_get_rcvif_psref(m, &psref); /* look for packet sent to broadcast address */ if (ia == NULL && rcvif && (rcvif->if_flags & IFF_BROADCAST)) { + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, rcvif) { if (ifa->ifa_addr->sa_family != AF_INET) continue; @@ -731,6 +743,9 @@ icmp_reflect(struct mbuf *m) ia = NULL; } } + if (ia != NULL) + ia4_acquire(ia, &psref_ia); + pserialize_read_exit(s); } sin = ia ? &ia->ia_addr : NULL; @@ -751,14 +766,17 @@ icmp_reflect(struct mbuf *m) sockaddr_in_init(&sin_dst, &ip->ip_dst, 0); memset(&icmproute, 0, sizeof(icmproute)); errornum = 0; - sin = in_selectsrc(&sin_dst, &icmproute, 0, NULL, &errornum); + ia = in_selectsrc(&sin_dst, &icmproute, 0, NULL, &errornum, + &psref_ia); /* errornum is never used */ rtcache_free(&icmproute); /* check to make sure sin is a source address on rcvif */ - if (sin) { + if (ia != NULL) { + sin = &ia->ia_addr; t = sin->sin_addr; sin = NULL; - ia = in_get_ia_on_iface(t, rcvif); + ia4_release(ia, &psref_ia); + ia = in_get_ia_on_iface_psref(t, rcvif, &psref_ia); if (ia != NULL) sin = &ia->ia_addr; } @@ -771,12 +789,16 @@ icmp_reflect(struct mbuf *m) * when the incoming packet was encapsulated */ if (sin == NULL && rcvif) { + KASSERT(ia == NULL); + s = pserialize_read_enter(); IFADDR_READER_FOREACH(ifa, rcvif) { if (ifa->ifa_addr->sa_family != AF_INET) continue; sin = &(ifatoia(ifa)->ia_addr); + ia4_acquire(ifatoia(ifa), &psref_ia); break; } + pserialize_read_exit(s); } m_put_rcvif_psref(rcvif, &psref); @@ -787,19 +809,25 @@ icmp_reflect(struct mbuf *m) * We find the first AF_INET address on the first non-loopback * interface. */ - if (sin == NULL) + if (sin == NULL) { + KASSERT(ia == NULL); + s = pserialize_read_enter(); IN_ADDRLIST_READER_FOREACH(ia) { if (ia->ia_ifp->if_flags & IFF_LOOPBACK) continue; sin = &ia->ia_addr; + ia4_acquire(ia, &psref_ia); break; } + pserialize_read_exit(s); + } /* * If we still didn't find an address, punt. We could have an * interface up (and receiving packets) with no address. */ if (sin == NULL) { + KASSERT(ia == NULL); m_freem(m); goto done; } @@ -807,6 +835,9 @@ icmp_reflect(struct mbuf *m) ip->ip_src = sin->sin_addr; ip->ip_ttl = MAXTTL; + if (ia != NULL) + ia4_release(ia, &psref_ia); + if (optlen > 0) { u_char *cp; int opt, cnt; @@ -890,6 +921,7 @@ icmp_reflect(struct mbuf *m) icmp_send(m, opts); done: + curlwp_bindx(bound); if (opts) (void)m_free(opts); } diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 29022d8..e075c8b 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -447,12 +447,13 @@ static void ip_input(struct mbuf *m) { struct ip *ip = NULL; - struct in_ifaddr *ia; + struct in_ifaddr *ia = NULL; int hlen = 0, len; int downmatch; int srcrt = 0; ifnet_t *ifp; struct psref psref; + int s; KASSERTMSG(cpu_softintr_p(), "ip_input: not in the software " "interrupt handler; synchronization assumptions violated"); @@ -665,15 +666,21 @@ ip_input(struct mbuf *m) * or IN_IFF_NOTREADY addresses as not mine. */ downmatch = 0; + s = pserialize_read_enter(); ia = ip_match_our_address(ifp, ip, &downmatch); - if (ia != NULL) + if (ia != NULL) { + pserialize_read_exit(s); goto ours; + } if (ifp->if_flags & IFF_BROADCAST) { ia = ip_match_our_address_broadcast(ifp, ip); - if (ia != NULL) + if (ia != NULL) { + pserialize_read_exit(s); goto ours; + } } + pserialize_read_exit(s); if (IN_MULTICAST(ip->ip_dst.s_addr)) { #ifdef MROUTING @@ -810,8 +817,17 @@ ours: * Switch out to protocol's input routine. */ #if IFA_STATS - if (ia && ip) - ia->ia_ifa.ifa_data.ifad_inbytes += ntohs(ip->ip_len); + if (ia && ip) { + struct in_ifaddr *_ia; + /* + * Keep a reference from ip_match_our_address with psref + * is expensive, so explore ia here again. + */ + s = pserialize_read_enter(); + _ia = in_get_ia(ip->ip_dst.s_addr); + _ia->ia_ifa.ifa_data.ifad_inbytes += ntohs(ip->ip_len); + pserialize_read_exit(s); + } #endif IP_STATINC(IP_STAT_DELIVERED); @@ -873,6 +889,8 @@ ip_dooptions(struct mbuf *m) int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; struct in_addr dst; n_time ntime; + struct ifaddr *ifa; + int s; dst = ip->ip_dst; cp = (u_char *)(ip + 1); @@ -924,8 +942,11 @@ ip_dooptions(struct mbuf *m) goto bad; } ipaddr.sin_addr = ip->ip_dst; - ia = ifatoia(ifa_ifwithaddr(sintosa(&ipaddr))); - if (ia == 0) { + + s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sintosa(&ipaddr)); + if (ifa == NULL) { + pserialize_read_exit(s); if (opt == IPOPT_SSRR) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; @@ -937,6 +958,8 @@ ip_dooptions(struct mbuf *m) */ break; } + pserialize_read_exit(s); + off--; /* 0 origin */ if ((off + sizeof(struct in_addr)) > optlen) { /* @@ -950,18 +973,26 @@ ip_dooptions(struct mbuf *m) */ memcpy((void *)&ipaddr.sin_addr, (void *)(cp + off), sizeof(ipaddr.sin_addr)); - if (opt == IPOPT_SSRR) - ia = ifatoia(ifa_ifwithladdr(sintosa(&ipaddr))); - else + s = pserialize_read_enter(); + if (opt == IPOPT_SSRR) { + ifa = ifa_ifwithladdr(sintosa(&ipaddr)); + if (ifa != NULL) + ia = ifatoia(ifa); + else + ia = NULL; + } else { ia = ip_rtaddr(ipaddr.sin_addr); - if (ia == 0) { + } + if (ia == NULL) { type = ICMP_UNREACH; code = ICMP_UNREACH_SRCFAIL; + pserialize_read_exit(s); goto bad; } ip->ip_dst = ipaddr.sin_addr; bcopy((void *)&ia->ia_addr.sin_addr, (void *)(cp + off), sizeof(struct in_addr)); + pserialize_read_exit(s); cp[IPOPT_OFFSET] += sizeof(struct in_addr); /* * Let ip_intr's mcast routing check handle mcast pkts @@ -990,15 +1021,22 @@ ip_dooptions(struct mbuf *m) * locate outgoing interface; if we're the destination, * use the incoming interface (should be same). */ - if ((ia = ifatoia(ifa_ifwithaddr(sintosa(&ipaddr)))) - == NULL && - (ia = ip_rtaddr(ipaddr.sin_addr)) == NULL) { - type = ICMP_UNREACH; - code = ICMP_UNREACH_HOST; - goto bad; + s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sintosa(&ipaddr)); + if (ifa == NULL) { + ia = ip_rtaddr(ipaddr.sin_addr); + if (ia == NULL) { + pserialize_read_exit(s); + type = ICMP_UNREACH; + code = ICMP_UNREACH_HOST; + goto bad; + } + } else { + ia = ifatoia(ifa); } bcopy((void *)&ia->ia_addr.sin_addr, (void *)(cp + off), sizeof(struct in_addr)); + pserialize_read_exit(s); cp[IPOPT_OFFSET] += sizeof(struct in_addr); break; @@ -1029,7 +1067,7 @@ ip_dooptions(struct mbuf *m) case IPOPT_TS_TSANDADDR: { struct ifnet *rcvif; - int s; + int _s, _ss; if (ipt->ipt_ptr - 1 + sizeof(n_time) + sizeof(struct in_addr) > ipt->ipt_len) { @@ -1038,14 +1076,18 @@ ip_dooptions(struct mbuf *m) goto bad; } ipaddr.sin_addr = dst; - rcvif = m_get_rcvif(m, &s); - ia = ifatoia(ifaof_ifpforaddr(sintosa(&ipaddr), - rcvif)); - m_put_rcvif(rcvif, &s); - if (ia == 0) - continue; + _ss = pserialize_read_enter(); + rcvif = m_get_rcvif(m, &_s); + ifa = ifaof_ifpforaddr(sintosa(&ipaddr), rcvif); + m_put_rcvif(rcvif, &_s); + if (ifa == NULL) { + pserialize_read_exit(_ss); + break; + } + ia = ifatoia(ifa); bcopy(&ia->ia_addr.sin_addr, cp0, sizeof(struct in_addr)); + pserialize_read_exit(_ss); ipt->ipt_ptr += sizeof(struct in_addr); break; } @@ -1059,9 +1101,13 @@ ip_dooptions(struct mbuf *m) } memcpy(&ipaddr.sin_addr, cp0, sizeof(struct in_addr)); - if (ifatoia(ifa_ifwithaddr(sintosa(&ipaddr))) - == NULL) + s = pserialize_read_enter(); + ifa = ifa_ifwithaddr(sintosa(&ipaddr)); + if (ifa == NULL) { + pserialize_read_exit(s); continue; + } + pserialize_read_exit(s); ipt->ipt_ptr += sizeof(struct in_addr); break; @@ -1080,7 +1126,7 @@ ip_dooptions(struct mbuf *m) } if (forward) { struct ifnet *rcvif; - struct psref psref; + struct psref _psref; if (ip_forwsrcrt == 0) { type = ICMP_UNREACH; @@ -1088,14 +1134,14 @@ ip_dooptions(struct mbuf *m) goto bad; } - rcvif = m_get_rcvif_psref(m, &psref); + rcvif = m_get_rcvif_psref(m, &_psref); if (__predict_false(rcvif == NULL)) { type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; goto bad; } ip_forward(m, 1, rcvif); - m_put_rcvif_psref(rcvif, &psref); + m_put_rcvif_psref(rcvif, &_psref); return true; } return false; diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c index ece39d3..6abaca3 100644 --- a/sys/netinet/ip_mroute.c +++ b/sys/netinet/ip_mroute.c @@ -785,7 +785,6 @@ static int add_vif(struct vifctl *vifcp) { struct vif *vifp; - struct ifaddr *ifa; struct ifnet *ifp; int error, s; struct sockaddr_in sin; @@ -811,11 +810,18 @@ add_vif(struct vifctl *vifcp) } else #endif { + struct ifaddr *ifa; + sockaddr_in_init(&sin, &vifcp->vifc_lcl_addr, 0); + s = pserialize_read_enter(); ifa = ifa_ifwithaddr(sintosa(&sin)); - if (ifa == NULL) - return (EADDRNOTAVAIL); + if (ifa == NULL) { + pserialize_read_exit(s); + return EADDRNOTAVAIL; + } ifp = ifa->ifa_ifp; + /* FIXME NOMPSAFE */ + pserialize_read_exit(s); } if (vifcp->vifc_flags & VIFF_TUNNEL) { diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 297cb63..08678c8 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -112,6 +112,7 @@ __KERNEL_RCSID(0, "$NetBSD: ip_output.c,v 1.259 2016/07/08 04:33:30 ozaki-r Exp #include #endif #include +#include #include #include @@ -235,7 +236,7 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, int len, error = 0; struct route iproute; const struct sockaddr_in *dst; - struct in_ifaddr *ia; + struct in_ifaddr *ia = NULL; int isbroadcast; int sw_csum; u_long mtu; @@ -251,8 +252,9 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, struct sockaddr *rdst = &u.dst; /* real IP destination, as opposed * to the nexthop */ - struct psref psref; + struct psref psref, psref_ia; int bound; + bool bind_need_restore = false; len = 0; @@ -311,15 +313,23 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, goto bad; } + bound = curlwp_bind(); + bind_need_restore = true; /* * If routing to interface only, short circuit routing lookup. */ if (flags & IP_ROUTETOIF) { - if ((ia = ifatoia(ifa_ifwithladdr(sintocsa(dst)))) == NULL) { + struct ifaddr *ifa; + + ifa = ifa_ifwithladdr_psref(sintocsa(dst), &psref_ia); + if (ifa == NULL) { IP_STATINC(IP_STAT_NOROUTE); error = ENETUNREACH; goto bad; } + /* ia is already referenced by psref_ia */ + ia = ifatoia(ifa); + ifp = ia->ia_ifp; mtu = ifp->if_mtu; ip->ip_ttl = 1; @@ -327,16 +337,18 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, } else if ((IN_MULTICAST(ip->ip_dst.s_addr) || ip->ip_dst.s_addr == INADDR_BROADCAST) && imo != NULL && imo->imo_multicast_if_index != 0) { - bound = curlwp_bind(); ifp = mifp = if_get_byindex(imo->imo_multicast_if_index, &psref); if (ifp == NULL) { - curlwp_bindx(bound); IP_STATINC(IP_STAT_NOROUTE); error = ENETUNREACH; goto bad; } mtu = ifp->if_mtu; - ia = in_get_ia_from_ifp(ifp); + ia = in_get_ia_from_ifp_psref(ifp, &psref_ia); + if (ia == NULL) { + error = EADDRNOTAVAIL; + goto bad; + } isbroadcast = 0; } else { if (rt == NULL) @@ -346,6 +358,11 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, error = EHOSTUNREACH; goto bad; } + /* + * XXX NOMPSAFE: depends on accessing rt->rt_ifa isn't racy. + * Revisit when working on rtentry MP-ification. + */ + ifa_acquire(rt->rt_ifa, &psref_ia); ia = ifatoia(rt->rt_ifa); ifp = rt->rt_ifp; if ((mtu = rt->rt_rmx.rmx_mtu) == 0) @@ -403,21 +420,26 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, if (in_nullhost(ip->ip_src)) { struct in_ifaddr *xia; struct ifaddr *xifa; + struct psref _psref; - xia = in_get_ia_from_ifp(ifp); + xia = in_get_ia_from_ifp_psref(ifp, &_psref); if (!xia) { error = EADDRNOTAVAIL; goto bad; } xifa = &xia->ia_ifa; if (xifa->ifa_getifa != NULL) { + ia4_release(xia, &_psref); + /* FIXME NOMPSAFE */ xia = ifatoia((*xifa->ifa_getifa)(xifa, rdst)); if (xia == NULL) { error = EADDRNOTAVAIL; goto bad; } + ia4_acquire(xia, &_psref); } ip->ip_src = xia->ia_addr.sin_addr; + ia4_release(xia, &_psref); } inmgroup = in_multi_group(ip->ip_dst, ifp, flags); @@ -477,11 +499,14 @@ ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags, xifa = &ia->ia_ifa; if (xifa->ifa_getifa != NULL) { + ia4_release(ia, &psref_ia); + /* FIXME NOMPSAFE */ ia = ifatoia((*xifa->ifa_getifa)(xifa, rdst)); if (ia == NULL) { error = EADDRNOTAVAIL; goto bad; } + ia4_acquire(ia, &psref_ia); } ip->ip_src = ia->ia_addr.sin_addr; } @@ -543,6 +568,10 @@ sendit: ip->ip_id = ip_newid_range(ia, num); } } + if (ia != NULL) { + ia4_release(ia, &psref_ia); + ia = NULL; + } /* * If we're doing Path MTU Discovery, we need to set DF unless @@ -583,7 +612,8 @@ sendit: * search for the source address structure to * maintain output statistics. */ - ia = in_get_ia(ip->ip_src); + KASSERT(ia == NULL); + ia = in_get_ia_psref(ip->ip_src, &psref_ia); #endif /* Maybe skip checksums on loopback interfaces. */ @@ -713,6 +743,7 @@ sendit: IP_STATINC(IP_STAT_FRAGMENTED); } done: + ia4_release(ia, &psref_ia); if (ro == &iproute) { rtcache_free(&iproute); } @@ -723,8 +754,9 @@ done: #endif if (mifp != NULL) { if_put(mifp, &psref); + } + if (bind_need_restore) curlwp_bindx(bound); - } return error; bad: m_freem(m); diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index bab4d46..d593a39 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -554,8 +554,8 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) struct inpcb *inp = sotoinpcb(so); struct sockaddr_in *addr = (struct sockaddr_in *)nam; int error = 0; - int s; - struct ifaddr *ia; + int s, ss; + struct ifaddr *ifa; KASSERT(solocked(so)); KASSERT(inp != NULL); @@ -573,18 +573,22 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct lwp *l) error = EAFNOSUPPORT; goto release; } - if ((ia = ifa_ifwithaddr(sintosa(addr))) == 0 && + ss = pserialize_read_enter(); + if ((ifa = ifa_ifwithaddr(sintosa(addr))) == NULL && !in_nullhost(addr->sin_addr)) { + pserialize_read_exit(ss); error = EADDRNOTAVAIL; goto release; } - if (ia && ((struct in_ifaddr *)ia)->ia4_flags & + if (ifa && (ifatoia(ifa))->ia4_flags & (IN6_IFF_NOTREADY | IN_IFF_DETACHED)) { + pserialize_read_exit(ss); error = EADDRNOTAVAIL; goto release; } + pserialize_read_exit(ss); inp->inp_laddr = addr->sin_addr; diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 54e5823..eaaad1b 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -930,6 +930,7 @@ in6_update_ifa1(struct ifnet *ifp, struct in6_aliasreq *ifra, ia->ia_ifp = ifp; IN6_ADDRLIST_ENTRY_INIT(ia); + ifa_psref_init(&ia->ia_ifa); } /* update timestamp */ diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 750a420..0aa3d4d 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -485,23 +485,27 @@ in6_pcbconnect(void *v, struct sockaddr_in6 *sin6, struct lwp *l) if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) && in6p->in6p_laddr.s6_addr32[3] == 0) { #ifdef INET - struct sockaddr_in sin, *sinp; + struct sockaddr_in sin; + struct in_ifaddr *ia4; + struct psref _psref; memset(&sin, 0, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr32[3], sizeof(sin.sin_addr)); - sinp = in_selectsrc(&sin, &in6p->in6p_route, - in6p->in6p_socket->so_options, NULL, &error); - if (sinp == NULL) { + ia4 = in_selectsrc(&sin, &in6p->in6p_route, + in6p->in6p_socket->so_options, NULL, &error, &_psref); + if (ia4 == NULL) { if (error == 0) error = EADDRNOTAVAIL; return (error); } memset(&mapped, 0, sizeof(mapped)); mapped.s6_addr16[5] = htons(0xffff); - memcpy(&mapped.s6_addr32[3], &sinp->sin_addr, sizeof(sinp->sin_addr)); + memcpy(&mapped.s6_addr32[3], &IA_SIN(ia4)->sin_addr, + sizeof(IA_SIN(ia4)->sin_addr)); + ia4_release(ia4, &_psref); in6a = &mapped; #else return EADDRNOTAVAIL; diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c index bb39f43..8a7055f 100644 --- a/sys/netinet6/in6_src.c +++ b/sys/netinet6/in6_src.c @@ -659,8 +659,12 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, goto done; } ifp = rt->rt_ifp; - if (ifp != NULL) - if_acquire_NOMPSAFE(ifp, PSREF); + if (ifp != NULL) { + if (!if_is_deactivated(ifp)) + if_acquire_NOMPSAFE(ifp, PSREF); + else + ifp = NULL; + } /* * When cloning is required, try to allocate a route to the @@ -699,8 +703,12 @@ selectroute(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, else { if_put(ifp, PSREF); ifp = rt->rt_ifp; - if (ifp != NULL) - if_acquire_NOMPSAFE(ifp, PSREF); + if (ifp != NULL) { + if (!if_is_deactivated(ifp)) + if_acquire_NOMPSAFE(ifp, PSREF); + else + ifp = NULL; + } } /* @@ -788,7 +796,8 @@ in6_selectif(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts, * addresses.) */ if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp && - rt->rt_ifa->ifa_ifp != *retifp) { + rt->rt_ifa->ifa_ifp != *retifp && + !if_is_deactivated(rt->rt_ifa->ifa_ifp)) { if_put(*retifp, psref); *retifp = rt->rt_ifa->ifa_ifp; if_acquire_NOMPSAFE(*retifp, psref); diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 0b92c65..764be7a 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -527,6 +527,8 @@ ip6_output( */ if (ia != NULL && ia->ia_ifp) { origifp = ia->ia_ifp; + if (if_is_deactivated(origifp)) + goto bad; if_acquire_NOMPSAFE(origifp, &psref_ia); release_psref_ia = true; } else diff --git a/sys/netinet6/udp6_output.c b/sys/netinet6/udp6_output.c index f8bea05..e9a47dc 100644 --- a/sys/netinet6/udp6_output.c +++ b/sys/netinet6/udp6_output.c @@ -247,15 +247,20 @@ udp6_output(struct in6pcb * const in6p, struct mbuf *m, * never see this path. */ if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) { - struct sockaddr_in *sinp, sin_dst; + struct sockaddr_in sin_dst; struct in_addr ina; + struct in_ifaddr *ia4; + struct psref _psref; + int bound; memcpy(&ina, &faddr->s6_addr[12], sizeof(ina)); sockaddr_in_init(&sin_dst, &ina, 0); - sinp = in_selectsrc(&sin_dst, &in6p->in6p_route, + bound = curlwp_bind(); + ia4 = in_selectsrc(&sin_dst, &in6p->in6p_route, in6p->in6p_socket->so_options, NULL, - &error); - if (sinp == NULL) { + &error, &_psref); + if (ia4 == NULL) { + curlwp_bindx(bound); if (error == 0) error = EADDRNOTAVAIL; goto release; @@ -263,8 +268,10 @@ udp6_output(struct in6pcb * const in6p, struct mbuf *m, memset(&laddr_mapped, 0, sizeof(laddr_mapped)); laddr_mapped.s6_addr16[5] = 0xffff; /* ugly */ memcpy(&laddr_mapped.s6_addr[12], - &sinp->sin_addr, - sizeof(sinp->sin_addr)); + &IA_SIN(ia4)->sin_addr, + sizeof(IA_SIN(ia4)->sin_addr)); + ia4_release(ia4, &_psref); + curlwp_bindx(bound); laddr = &laddr_mapped; } else {