diff --git a/sys/altq/altq_subr.c b/sys/altq/altq_subr.c index 2d6409b..d89e1e2 100644 --- a/sys/altq/altq_subr.c +++ b/sys/altq/altq_subr.c @@ -356,6 +356,7 @@ tbr_timeout(void *arg) active = 0; s = splnet(); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (!TBR_IS_ENABLED(&ifp->if_snd)) continue; @@ -363,6 +364,7 @@ tbr_timeout(void *arg) if (!IFQ_IS_EMPTY(&ifp->if_snd) && ifp->if_start != NULL) (*ifp->if_start)(ifp); } + IFNET_UNLOCK(); splx(s); if (active > 0) CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0); diff --git a/sys/arch/x86/x86/vmt.c b/sys/arch/x86/x86/vmt.c index 40b09cf..6c5d34a 100644 --- a/sys/arch/x86/x86/vmt.c +++ b/sys/arch/x86/x86/vmt.c @@ -806,6 +806,7 @@ vmt_tclo_tick(void *xarg) /* find first available ipv4 address */ guest_ip = NULL; + IFNET_RLOCK(); IFNET_FOREACH(iface) { struct ifaddr *iface_addr; @@ -824,6 +825,7 @@ vmt_tclo_tick(void *xarg) break; } } + IFNET_UNLOCK(); if (guest_ip != NULL) { if (vm_rpc_send_rpci_tx(sc, "info-set guestinfo.ip %s", diff --git a/sys/compat/common/uipc_syscalls_40.c b/sys/compat/common/uipc_syscalls_40.c index 3eafc5d..f89e96a 100644 --- a/sys/compat/common/uipc_syscalls_40.c +++ b/sys/compat/common/uipc_syscalls_40.c @@ -43,17 +43,21 @@ compat_ifconf(u_long cmd, void *data) space = 0; else space = ifc->ifc_len; + + IFNET_RLOCK(); IFNET_FOREACH(ifp) { (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); - if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') - return ENAMETOOLONG; + if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') { + error = ENAMETOOLONG; + goto out; + } if (IFADDR_EMPTY(ifp)) { memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); if (space >= sz) { error = copyout(&ifr, ifrp, sz); if (error != 0) - return (error); + goto out; ifrp++; } space -= sizeof(ifr); @@ -101,14 +105,19 @@ compat_ifconf(u_long cmd, void *data) } } if (error != 0) - return (error); + goto out; space -= sz; } } + if (ifrp != NULL) ifc->ifc_len -= space; else ifc->ifc_len = -space; - return (0); + error = 0; + +out: + IFNET_UNLOCK(); + return error; } #endif diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index d358c58..af3eddc 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1131,11 +1131,14 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) else space = ifc.ifc_len; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); - if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') + if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') { + IFNET_UNLOCK(); return ENAMETOOLONG; + } if (IFADDR_EMPTY(ifp)) continue; IFADDR_FOREACH(ifa, ifp) { @@ -1148,13 +1151,16 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) osa->sa_family = sa->sa_family; if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) + if (error != 0) { + IFNET_UNLOCK(); return error; + } ifrp++; } space -= sz; } } + IFNET_UNLOCK(); if (ifrp != NULL) ifc.ifc_len -= space; @@ -1207,6 +1213,7 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, * Try real interface name first, then fake "ethX" */ found = 0; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (found) break; @@ -1216,6 +1223,7 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, found=1; if (IFADDR_EMPTY(ifp)) { error = ENODEV; + IFNET_UNLOCK(); goto out; } IFADDR_FOREACH(ifa, ifp) { @@ -1231,9 +1239,11 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, lreq.ifr_hwaddr.sa_family = sadl->sdl_family; error = copyout(&lreq, data, sizeof(lreq)); + IFNET_UNLOCK(); goto out; } } + IFNET_UNLOCK(); if (strncmp(lreq.ifr_name, "eth", 3) == 0) { for (ifnum = 0, index = 3; @@ -1245,6 +1255,7 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, error = EINVAL; /* in case we don't find one */ found = 0; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (found) break; @@ -1271,6 +1282,7 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, break; } } + IFNET_UNLOCK(); } else { /* unknown interface, not even an "eth*" name */ error = ENODEV; diff --git a/sys/compat/linux32/common/linux32_socket.c b/sys/compat/linux32/common/linux32_socket.c index 05f74ec..d459620 100644 --- a/sys/compat/linux32/common/linux32_socket.c +++ b/sys/compat/linux32/common/linux32_socket.c @@ -429,11 +429,14 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) else space = ifc.ifc_len; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); - if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') + if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') { + IFNET_UNLOCK(); return ENAMETOOLONG; + } if (IFADDR_EMPTY(ifp)) continue; IFADDR_FOREACH(ifa, ifp) { @@ -446,13 +449,16 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) osa->sa_family = sa->sa_family; if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) + if (error != 0) { + IFNET_UNLOCK(); return error; + } ifrp++; } space -= sz; } } + IFNET_UNLOCK(); if (ifrp != NULL) ifc.ifc_len -= space; @@ -504,6 +510,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, * Try real interface name first, then fake "ethX" */ found = 0; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (found) break; @@ -513,6 +520,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, found=1; if (IFADDR_EMPTY(ifp)) { error = ENODEV; + IFNET_UNLOCK(); goto out; } IFADDR_FOREACH(ifa, ifp) { @@ -528,9 +536,11 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, lreq.ifr_hwaddr.sa_family = sadl->sdl_family; error = copyout(&lreq, data, sizeof(lreq)); + IFNET_UNLOCK(); goto out; } } + IFNET_UNLOCK(); if (strncmp(lreq.ifr_name, "eth", 3) == 0) { for (ifnum = 0, index = 3; @@ -542,6 +552,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, error = EINVAL; /* in case we don't find one */ found = 0; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (found) break; @@ -568,6 +579,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, break; } } + IFNET_UNLOCK(); } else { /* unknown interface, not even an "eth*" name */ error = ENODEV; diff --git a/sys/compat/svr4/svr4_sockio.c b/sys/compat/svr4/svr4_sockio.c index dd13e34..ae92e02 100644 --- a/sys/compat/svr4/svr4_sockio.c +++ b/sys/compat/svr4/svr4_sockio.c @@ -116,8 +116,10 @@ svr4_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, lifnum.lifn_count = 0; /* XXX: We don't pay attention to family or flags */ + IFNET_RLOCK(); IFNET_FOREACH(ifp) lifnum.lifn_count += svr4_count_ifnum(ifp); + IFNET_UNLOCK(); DPRINTF(("SIOCGLIFNUM [family=%d,flags=%d,count=%d]\n", lifnum.lifn_family, lifnum.lifn_flags, @@ -142,8 +144,10 @@ svr4_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, * entry per physical interface? */ + IFNET_RLOCK(); IFNET_FOREACH(ifp) ifnum += svr4_count_ifnum(ifp); + IFNET_UNLOCK(); DPRINTF(("SIOCGIFNUM %d\n", ifnum)); return copyout(&ifnum, data, sizeof(ifnum)); diff --git a/sys/compat/svr4_32/svr4_32_sockio.c b/sys/compat/svr4_32/svr4_32_sockio.c index 4aa57ff..310fb82 100644 --- a/sys/compat/svr4_32/svr4_32_sockio.c +++ b/sys/compat/svr4_32/svr4_32_sockio.c @@ -109,8 +109,10 @@ svr4_32_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, int fd, u_long * entry per physical interface? */ + IFNET_RLOCK(); IFNET_FOREACH(ifp) ifnum += svr4_count_ifnum(ifp) + IFNET_UNLOCK(); DPRINTF(("SIOCGIFNUM %d\n", ifnum)); return copyout(&ifnum, data, sizeof(ifnum)); diff --git a/sys/dev/pci/if_bge.c b/sys/dev/pci/if_bge.c index c79de7d..d392817 100644 --- a/sys/dev/pci/if_bge.c +++ b/sys/dev/pci/if_bge.c @@ -1532,6 +1532,7 @@ bge_update_all_threshes(int lvl) /* * Now search all the interfaces for this name/number */ + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (strncmp(ifp->if_xname, namebuf, namelen) != 0) continue; @@ -1539,6 +1540,7 @@ bge_update_all_threshes(int lvl) if (bge_auto_thresh) bge_set_thresh(ifp, lvl); } + IFNET_UNLOCK(); } /* diff --git a/sys/dist/pf/net/pf_if.c b/sys/dist/pf/net/pf_if.c index 842e824..aaeb6e6 100644 --- a/sys/dist/pf/net/pf_if.c +++ b/sys/dist/pf/net/pf_if.c @@ -119,10 +119,12 @@ pfi_initialize(void) #ifdef __NetBSD__ ifnet_t *ifp; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { pfi_init_groups(ifp); pfi_attach_ifnet(ifp); } + IFNET_UNLOCK(); pfil_add_hook(pfil_ifnet_wrapper, NULL, PFIL_IFNET, if_pfil); pfil_add_hook(pfil_ifaddr_wrapper, NULL, PFIL_IFADDR, if_pfil); @@ -139,10 +141,12 @@ pfi_destroy(void) pfil_remove_hook(pfil_ifaddr_wrapper, NULL, PFIL_IFADDR, if_pfil); pfil_remove_hook(pfil_ifnet_wrapper, NULL, PFIL_IFNET, if_pfil); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { pfi_detach_ifnet(ifp); pfi_destroy_groups(ifp); } + IFNET_UNLOCK(); while ((p = RB_MIN(pfi_ifhead, &pfi_ifs))) { RB_REMOVE(pfi_ifhead, &pfi_ifs, p); diff --git a/sys/kern/kern_subr.c b/sys/kern/kern_subr.c index c57f8ce..55737d2 100644 --- a/sys/kern/kern_subr.c +++ b/sys/kern/kern_subr.c @@ -217,11 +217,14 @@ setroot(device_t bootdv, int bootpartition) if (vops != NULL && strcmp(rootfstype, MOUNT_NFS) == 0 && rootspec == NULL && (bootdv == NULL || device_class(bootdv) != DV_IFNET)) { + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) break; } + IFNET_UNLOCK(); + if (ifp == NULL) { /* * Can't find a suitable interface; ask the diff --git a/sys/kern/kern_uuid.c b/sys/kern/kern_uuid.c index 8c6f57e..7c0298f 100644 --- a/sys/kern/kern_uuid.c +++ b/sys/kern/kern_uuid.c @@ -109,6 +109,7 @@ uuid_node(uint16_t *node) s = splnet(); KERNEL_LOCK(1, NULL); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { /* Walk the address list */ IFADDR_FOREACH(ifa, ifp) { @@ -123,6 +124,7 @@ uuid_node(uint16_t *node) } } } + IFNET_UNLOCK(); KERNEL_UNLOCK_ONE(NULL); splx(s); diff --git a/sys/kern/subr_tftproot.c b/sys/kern/subr_tftproot.c index f0b9a46..47031e7 100644 --- a/sys/kern/subr_tftproot.c +++ b/sys/kern/subr_tftproot.c @@ -130,16 +130,20 @@ tftproot_dhcpboot(device_t bootdv) int error = -1; if (rootspec != NULL) { - IFNET_FOREACH(ifp) + /* No need to lock because it's called only during bootstrap */ + IFNET_FOREACH(ifp) { if (strcmp(rootspec, ifp->if_xname) == 0) break; + } } if ((ifp == NULL) && (bootdv != NULL && device_class(bootdv) == DV_IFNET)) { - IFNET_FOREACH(ifp) + /* No need to lock because it's called only during bootstrap */ + IFNET_FOREACH(ifp) { if (strcmp(device_xname(bootdv), ifp->if_xname) == 0) break; + } } if (ifp == NULL) { diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c index 10223f2..c896c37 100644 --- a/sys/kern/uipc_mbuf.c +++ b/sys/kern/uipc_mbuf.c @@ -543,7 +543,6 @@ m_reclaim(void *arg, int flags) { struct domain *dp; const struct protosw *pr; - struct ifnet *ifp; int s; KERNEL_LOCK(1, NULL); @@ -554,10 +553,7 @@ m_reclaim(void *arg, int flags) if (pr->pr_drain) (*pr->pr_drain)(); } - IFNET_FOREACH(ifp) { - if (ifp->if_drain) - (*ifp->if_drain)(ifp); - } + if_drain_all(); splx(s); mbstat.m_drain++; KERNEL_UNLOCK_ONE(NULL); diff --git a/sys/net/if.c b/sys/net/if.c index 82d8256..987dad4 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -113,6 +113,7 @@ __KERNEL_RCSID(0, "$NetBSD: if.c,v 1.285 2014/07/01 10:16:02 ozaki-r Exp $"); #include #include #include +#include #include #include @@ -158,10 +159,15 @@ static ifnet_t ** ifindex2ifnet = NULL; static u_int if_index = 1; static size_t if_indexlim = 0; static uint64_t index_gen; -static kmutex_t index_gen_mtx; static struct ifaddr ** ifnet_addrs = NULL; +/* + * Mutex to protect the above objects. + */ +krwlock_t ifnet_lock __cacheline_aligned; +static kmutex_t ifnet_intr_lock __cacheline_aligned; + static callout_t if_slowtimo_ch; struct ifnet *lo0ifp; @@ -246,7 +252,8 @@ ifinit(void) void ifinit1(void) { - mutex_init(&index_gen_mtx, MUTEX_DEFAULT, IPL_NONE); + rw_init(&ifnet_lock); + mutex_init(&ifnet_intr_lock, MUTEX_DEFAULT, IPL_NET); TAILQ_INIT(&ifnet_list); if_indexlim = 8; @@ -392,7 +399,10 @@ static void if_sadl_setrefs(struct ifnet *ifp, struct ifaddr *ifa) { const struct sockaddr_dl *sdl; + IFNET_WLOCK(); ifnet_addrs[ifp->if_index] = ifa; + IFNET_UNLOCK(); + IFAREF(ifa); ifp->if_dl = ifa; IFAREF(ifa); @@ -437,7 +447,10 @@ if_deactivate_sadl(struct ifnet *ifp) ifp->if_sadl = NULL; + IFNET_WLOCK(); ifnet_addrs[ifp->if_index] = NULL; + IFNET_UNLOCK(); + IFAFREE(ifa); ifp->if_dl = NULL; IFAFREE(ifa); @@ -470,12 +483,17 @@ if_free_sadl(struct ifnet *ifp) struct ifaddr *ifa; int s; + KASSERT(!cpu_intr_p()); + + IFNET_WLOCK(); ifa = ifnet_addrs[ifp->if_index]; if (ifa == NULL) { KASSERT(ifp->if_sadl == NULL); KASSERT(ifp->if_dl == NULL); + IFNET_UNLOCK(); return; } + IFNET_UNLOCK(); KASSERT(ifp->if_sadl != NULL); KASSERT(ifp->if_dl != NULL); @@ -496,9 +514,9 @@ if_getindex(ifnet_t *ifp) { bool hitlimit = false; - mutex_enter(&index_gen_mtx); + KASSERT(rw_lock_held(&ifnet_lock)); + ifp->if_index_gen = index_gen++; - mutex_exit(&index_gen_mtx); ifp->if_index = if_index; if (ifindex2ifnet == NULL) { @@ -577,14 +595,22 @@ void if_attach(ifnet_t *ifp) { KASSERT(if_indexlim > 0); + TAILQ_INIT(&ifp->if_addrlist); + + IFNET_WLOCK(); + + mutex_enter(&ifnet_intr_lock); TAILQ_INSERT_TAIL(&ifnet_list, ifp, if_list); + mutex_exit(&ifnet_intr_lock); if (ifioctl_attach(ifp) != 0) panic("%s: ifioctl_attach() failed", __func__); if_getindex(ifp); + IFNET_UNLOCK(); + /* * Link level name is allocated later by a separate call to * if_alloc_sadl(). @@ -635,6 +661,11 @@ if_attachdomain(void) int s; s = splnet(); + /* + * Don't IFNET_RLOCK, because it may be called inside if_attachdomain1. + * Anyway we don't need IFNET_RLOCK because this function is called + * only during bootstrap. + */ IFNET_FOREACH(ifp) if_attachdomain1(ifp); splx(s); @@ -857,9 +888,15 @@ again: /* Announce that the interface is gone. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); + IFNET_WLOCK(); + ifindex2ifnet[ifp->if_index] = NULL; + mutex_enter(&ifnet_intr_lock); TAILQ_REMOVE(&ifnet_list, ifp, if_list); + mutex_exit(&ifnet_intr_lock); + + IFNET_UNLOCK(); ifioctl_detach(ifp); @@ -1157,6 +1194,7 @@ ifa_ifwithdstaddr(const struct sockaddr *addr) struct ifnet *ifp; struct ifaddr *ifa; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (ifp->if_output == if_nulloutput) continue; @@ -1166,10 +1204,13 @@ ifa_ifwithdstaddr(const struct sockaddr *addr) if (ifa->ifa_addr->sa_family != addr->sa_family || ifa->ifa_dstaddr == NULL) continue; - if (equal(addr, ifa->ifa_dstaddr)) + if (equal(addr, ifa->ifa_dstaddr)) { + IFNET_UNLOCK(); return ifa; + } } } + IFNET_UNLOCK(); return NULL; } @@ -1187,12 +1228,15 @@ ifa_ifwithnet(const struct sockaddr *addr) u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; + IFNET_RLOCK(); if (af == AF_LINK) { sdl = satocsdl(addr); if (sdl->sdl_index && sdl->sdl_index < if_indexlim && ifindex2ifnet[sdl->sdl_index] && - ifindex2ifnet[sdl->sdl_index]->if_output != if_nulloutput) - return ifnet_addrs[sdl->sdl_index]; + ifindex2ifnet[sdl->sdl_index]->if_output != if_nulloutput) { + ifa_maybe = ifnet_addrs[sdl->sdl_index]; + goto out; + } } #ifdef NETATALK if (af == AF_APPLETALK) { @@ -1205,14 +1249,16 @@ ifa_ifwithnet(const struct sockaddr *addr) if (ifa == NULL) continue; sat2 = (struct sockaddr_at *)ifa->ifa_addr; - if (sat2->sat_addr.s_net == sat->sat_addr.s_net) - return ifa; /* exact match */ + if (sat2->sat_addr.s_net == sat->sat_addr.s_net) { + ifa_maybe = ifa; /* exact match */ + goto out; + } if (ifa_maybe == NULL) { /* else keep the if with the right range */ ifa_maybe = ifa; } } - return ifa_maybe; + goto out; } #endif IFNET_FOREACH(ifp) { @@ -1241,6 +1287,8 @@ ifa_ifwithnet(const struct sockaddr *addr) ifa_maybe = ifa; } } +out: + IFNET_UNLOCK(); return ifa_maybe; } @@ -1267,14 +1315,18 @@ ifa_ifwithaf(int af) struct ifnet *ifp; struct ifaddr *ifa; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (ifp->if_output == if_nulloutput) continue; IFADDR_FOREACH(ifa, ifp) { - if (ifa->ifa_addr->sa_family == af) + if (ifa->ifa_addr->sa_family == af) { + IFNET_UNLOCK(); return ifa; + } } } + IFNET_UNLOCK(); return NULL; } @@ -1479,12 +1531,14 @@ if_slowtimo(void *arg) struct ifnet *ifp; int s = splnet(); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (ifp->if_timer == 0 || --ifp->if_timer) continue; if (ifp->if_watchdog != NULL) (*ifp->if_watchdog)(ifp); } + IFNET_UNLOCK(); splx(s); callout_reset(&if_slowtimo_ch, hz / IFNET_SLOWHZ, if_slowtimo, NULL); } @@ -1531,7 +1585,7 @@ ifpromisc(struct ifnet *ifp, int pswitch) struct ifnet * ifunit(const char *name) { - struct ifnet *ifp; + struct ifnet *ifp = NULL; const char *cp = name; u_int unit = 0; u_int i; @@ -1543,30 +1597,35 @@ ifunit(const char *name) unit = unit * 10 + (*cp - '0'); } + IFNET_RLOCK(); /* * If the number took all of the name, then it's a valid ifindex. */ if (i == IFNAMSIZ || (cp != name && *cp == '\0')) { if (unit >= if_indexlim) - return NULL; + goto out; ifp = ifindex2ifnet[unit]; if (ifp == NULL || ifp->if_output == if_nulloutput) - return NULL; - return ifp; + ifp = NULL; + goto out; } IFNET_FOREACH(ifp) { if (ifp->if_output == if_nulloutput) continue; if (strcmp(ifp->if_xname, name) == 0) - return ifp; + goto out; } - return NULL; + ifp = NULL; +out: + IFNET_UNLOCK(); + return ifp; } ifnet_t * if_byindex(u_int idx) { + /* We don't need mutex because ifindex2ifnet never shrink. */ return (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; } @@ -2086,11 +2145,15 @@ ifconf(u_long cmd, void *data) space = 0; else space = ifc->ifc_len; + + IFNET_RLOCK(); IFNET_FOREACH(ifp) { (void)strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); - if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') + if (ifr.ifr_name[sizeof(ifr.ifr_name) - 1] != '\0') { + IFNET_UNLOCK(); return ENAMETOOLONG; + } if (IFADDR_EMPTY(ifp)) { /* Interface with no addresses - send zero sockaddr. */ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); @@ -2100,8 +2163,10 @@ ifconf(u_long cmd, void *data) } if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) + if (error != 0) { + IFNET_UNLOCK(); return error; + } ifrp++; space -= sz; } @@ -2119,12 +2184,16 @@ ifconf(u_long cmd, void *data) memcpy(&ifr.ifr_space, sa, sa->sa_len); if (space >= sz) { error = copyout(&ifr, ifrp, sz); - if (error != 0) - return (error); + if (error != 0) { + IFNET_UNLOCK(); + return error; + } ifrp++; space -= sz; } } } + IFNET_UNLOCK(); + if (ifrp != NULL) { KASSERT(0 <= space && space <= ifc->ifc_len); ifc->ifc_len -= space; @@ -2288,6 +2357,22 @@ if_mcast_op(ifnet_t *ifp, const unsigned long cmd, const struct sockaddr *sa) return rc; } +/* + * May be called from interrupt context. + */ +void +if_drain_all(void) +{ + struct ifnet *ifp; + + mutex_enter(&ifnet_intr_lock); + IFNET_FOREACH(ifp) { + if (ifp->if_drain) + (*ifp->if_drain)(ifp); + } + mutex_exit(&ifnet_intr_lock); +} + static void sysctl_sndq_setup(struct sysctllog **clog, const char *ifname, struct ifaltq *ifq) diff --git a/sys/net/if.h b/sys/net/if.h index 67a9dcc..c6fed83 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -80,6 +80,7 @@ #include #include #include +#include #include #include @@ -981,8 +982,13 @@ __END_DECLS #ifdef _KERNEL #define IFNET_FIRST() TAILQ_FIRST(&ifnet_list) +#define IFNET_EMPTY() TAILQ_EMPTY(&ifnet_list) #define IFNET_NEXT(__ifp) TAILQ_NEXT((__ifp), if_list) #define IFNET_FOREACH(__ifp) TAILQ_FOREACH(__ifp, &ifnet_list, if_list) +#define IFNET_RLOCK() rw_enter(&ifnet_lock, RW_READER) +#define IFNET_WLOCK() rw_enter(&ifnet_lock, RW_WRITER) +#define IFNET_UNLOCK() rw_exit(&ifnet_lock) + #define IFADDR_FIRST(__ifp) TAILQ_FIRST(&(__ifp)->if_addrlist) #define IFADDR_NEXT(__ifa) TAILQ_NEXT((__ifa), ifa_list) #define IFADDR_FOREACH(__ifa, __ifp) TAILQ_FOREACH(__ifa, \ @@ -990,9 +996,11 @@ __END_DECLS #define IFADDR_EMPTY(__ifp) TAILQ_EMPTY(&(__ifp)->if_addrlist) extern struct ifnet_head ifnet_list; +extern krwlock_t ifnet_lock; extern struct ifnet *lo0ifp; ifnet_t * if_byindex(u_int); +void if_drain_all(void); /* * ifq sysctl support diff --git a/sys/net/npf/npf_if.c b/sys/net/npf/npf_if.c index 7825b25..ec078ca 100644 --- a/sys/net/npf/npf_if.c +++ b/sys/net/npf/npf_if.c @@ -145,9 +145,11 @@ npf_ifmap_flush(void) npf_ifmap_cnt = 0; KERNEL_LOCK(1, NULL); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { ifp->if_pf_kif = (void *)(uintptr_t)INACTIVE_ID; } + IFNET_UNLOCK(); KERNEL_UNLOCK_ONE(NULL); } diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index db51145..44ba2b4 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -1168,6 +1168,8 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) int len, error = 0; memset(&info, 0, sizeof(info)); + + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (w->w_arg && w->w_arg != ifp->if_index) continue; @@ -1192,7 +1194,7 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) panic("sysctl_iflist(1)"); } if (error) - return error; + goto out; info.rti_info[RTAX_IFP] = NULL; if (w->w_where && w->w_tmem && w->w_needed <= 0) { switch (type) { @@ -1206,7 +1208,7 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) ifm->ifm_addrs = info.rti_addrs; error = copyout(ifm, w->w_where, len); if (error) - return error; + goto out; w->w_where = (char *)w->w_where + len; break; } @@ -1215,14 +1217,14 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) case NET_RT_OOIFLIST: error = compat_14_iflist(ifp, w, &info, len); if (error) - return error; + goto out; break; #endif #ifdef COMPAT_50 case NET_RT_OIFLIST: error = compat_50_iflist(ifp, w, &info, len); if (error) - return error; + goto out; break; #endif default: @@ -1236,7 +1238,7 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask; info.rti_info[RTAX_BRD] = ifa->ifa_dstaddr; if ((error = rt_msg2(RTM_NEWADDR, &info, 0, w, &len))) - return error; + goto out; if (w->w_where && w->w_tmem && w->w_needed <= 0) { struct ifa_xmsghdr *ifam; @@ -1247,14 +1249,16 @@ sysctl_iflist(int af, struct rt_walkarg *w, int type) ifam->ifam_addrs = info.rti_addrs; error = copyout(w->w_tmem, w->w_where, len); if (error) - return error; + goto out; w->w_where = (char *)w->w_where + len; } } info.rti_info[RTAX_IFA] = info.rti_info[RTAX_NETMASK] = info.rti_info[RTAX_BRD] = NULL; } - return 0; +out: + IFNET_UNLOCK(); + return error; } static int diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 39b1580..2ac6fe9 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -923,6 +923,7 @@ carp_send_ad_all(void) struct carp_if *cif; struct carp_softc *vh; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP) continue; @@ -934,6 +935,7 @@ carp_send_ad_all(void) carp_send_ad(vh); } } + IFNET_UNLOCK(); } diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 2b98d95..1644543 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -488,7 +488,7 @@ rip_bind(struct inpcb *inp, struct mbuf *nam) if (nam->m_len != sizeof(*addr)) return (EINVAL); - if (!IFNET_FIRST()) + if (IFNET_EMPTY()) return (EADDRNOTAVAIL); if (addr->sin_family != AF_INET) return (EAFNOSUPPORT); @@ -506,7 +506,7 @@ rip_connect(struct inpcb *inp, struct mbuf *nam) if (nam->m_len != sizeof(*addr)) return (EINVAL); - if (!IFNET_FIRST()) + if (IFNET_EMPTY()) return (EADDRNOTAVAIL); if (addr->sin_family != AF_INET) return (EAFNOSUPPORT); diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 38cf354..5e9b57e 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1648,6 +1648,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, } } + IFNET_RLOCK(); IFNET_FOREACH(ifp) { addrsofif = 0; IFADDR_FOREACH(ifa, ifp) { @@ -1700,11 +1701,13 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, } if (iffound) { *ifpp = ifp; + IFNET_UNLOCK(); return (addrsofif); } addrs += addrsofif; } + IFNET_UNLOCK(); return (addrs); } diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 3d6723b..813685c 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2275,6 +2275,7 @@ in6_setmaxmtu(void) unsigned long maxmtu = 0; struct ifnet *ifp; + IFNET_RLOCK(); IFNET_FOREACH(ifp) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) @@ -2283,6 +2284,7 @@ in6_setmaxmtu(void) IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } + IFNET_UNLOCK(); if (maxmtu) /* update only when maxmtu is positive */ in6_maxmtu = maxmtu; } diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 713f6da..5999e880 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -473,6 +473,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, } /* next, try to get it from some other hardware interface */ + IFNET_RLOCK(); IFNET_FOREACH(ifp) { if (ifp == ifp0) continue; @@ -490,6 +491,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, goto success; } } + IFNET_UNLOCK(); #if 0 /* get from hostid - only for certain architectures */ @@ -967,6 +969,7 @@ in6_tmpaddrtimer(void *ignored_arg) ip6_temp_regen_advance) * hz, in6_tmpaddrtimer, NULL); memset(nullbuf, 0, sizeof(nullbuf)); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { ndi = ND_IFINFO(ifp); if (memcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { @@ -978,6 +981,7 @@ in6_tmpaddrtimer(void *ignored_arg) ndi->randomseed1, ndi->randomid); } } + IFNET_UNLOCK(); KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index f849508..920f1d0 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -2089,6 +2089,7 @@ nd6_slowtimo(void *ignored_arg) KERNEL_LOCK(1, NULL); callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, NULL); + IFNET_RLOCK(); IFNET_FOREACH(ifp) { nd6if = ND_IFINFO(ifp); if (nd6if->basereachable && /* already initialized */ @@ -2103,6 +2104,7 @@ nd6_slowtimo(void *ignored_arg) nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); } } + IFNET_UNLOCK(); KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); } diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index fc2884d..8e2b25a 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -756,7 +756,7 @@ rip6_usrreq(struct socket *so, int req, struct mbuf *m, error = EINVAL; break; } - if (!IFNET_FIRST() || addr->sin6_family != AF_INET6) { + if (IFNET_EMPTY() || addr->sin6_family != AF_INET6) { error = EADDRNOTAVAIL; break; } @@ -797,7 +797,7 @@ rip6_usrreq(struct socket *so, int req, struct mbuf *m, error = EINVAL; break; } - if (!IFNET_FIRST()) { + if (IFNET_EMPTY()) { error = EADDRNOTAVAIL; break; } diff --git a/sys/netipsec/xform_ipip.c b/sys/netipsec/xform_ipip.c index 950ab59..13a0203 100644 --- a/sys/netipsec/xform_ipip.c +++ b/sys/netipsec/xform_ipip.c @@ -338,6 +338,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) if ((m->m_pkthdr.rcvif == NULL || !(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK)) && ipip_allow != 2) { + IFNET_RLOCK(); IFNET_FOREACH(ifp) { IFADDR_FOREACH(ifa, ifp) { #ifdef INET @@ -350,6 +351,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) if (sin->sin_addr.s_addr == ipo->ip_src.s_addr) { + IFNET_UNLOCK(); IPIP_STATINC(IPIP_STAT_SPOOF); m_freem(m); return; @@ -366,6 +368,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) sin6 = (struct sockaddr_in6 *) ifa->ifa_addr; if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_src)) { + IFNET_UNLOCK(); IPIP_STATINC(IPIP_STAT_SPOOF); m_freem(m); return; @@ -375,6 +378,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) #endif /* INET6 */ } } + IFNET_UNLOCK(); } /* Statistics */