diff --git a/sys/altq/altq_subr.c b/sys/altq/altq_subr.c index 2d6409b..427539c 100644 --- a/sys/altq/altq_subr.c +++ b/sys/altq/altq_subr.c @@ -352,18 +352,26 @@ static void tbr_timeout(void *arg) { struct ifnet *ifp; - int active, s; + int active, s, ss; active = 0; - s = splnet(); - IFNET_FOREACH(ifp) { - if (!TBR_IS_ENABLED(&ifp->if_snd)) + IFNET_RENTER(ss); + IFNET_FOREACH_SAFE(ifp) { + if (IFNET_DYING_P(ifp) || !TBR_IS_ENABLED(&ifp->if_snd)) continue; + ifhold(ifp); + IFNET_REXIT(ss); + active++; + s = splnet(); if (!IFQ_IS_EMPTY(&ifp->if_snd) && ifp->if_start != NULL) (*ifp->if_start)(ifp); + splx(s); + + ifput(ifp); + IFNET_RENTER(ss); } - splx(s); + IFNET_REXIT(ss); if (active > 0) CALLOUT_RESET(&tbr_callout, 1, tbr_timeout, (void *)0); else diff --git a/sys/arch/x86/x86/vmt.c b/sys/arch/x86/x86/vmt.c index 27adba6..ac1af2b 100644 --- a/sys/arch/x86/x86/vmt.c +++ b/sys/arch/x86/x86/vmt.c @@ -803,9 +803,11 @@ vmt_tclo_tick(void *xarg) } else if (strcmp(sc->sc_rpc_buf, "Set_Option broadcastIP 1") == 0) { struct ifnet *iface; struct sockaddr_in *guest_ip; + int s; /* find first available ipv4 address */ guest_ip = NULL; + IFNET_RENTER(s); IFNET_FOREACH(iface) { struct ifaddr *iface_addr; @@ -824,6 +826,7 @@ vmt_tclo_tick(void *xarg) break; } } + IFNET_REXIT(s); if (guest_ip != NULL) { if (vm_rpc_send_rpci_tx(sc, "info-set guestinfo.ip %s", diff --git a/sys/compat/common/if_43.c b/sys/compat/common/if_43.c index d692dfe..3ef9a02 100644 --- a/sys/compat/common/if_43.c +++ b/sys/compat/common/if_43.c @@ -218,7 +218,7 @@ compat_ifioctl(struct socket *so, u_long ocmd, u_long cmd, void *data, struct ifreq *ifr = (struct ifreq *)data; struct ifreq ifrb; struct oifreq *oifr = NULL; - struct ifnet *ifp = ifunit(ifr->ifr_name); + struct ifnet *ifp = ifget(ifr->ifr_name); struct sockaddr *sa; if (ifp == NULL) @@ -271,5 +271,6 @@ compat_ifioctl(struct socket *so, u_long ocmd, u_long cmd, void *data, if (cmd != ocmd) ifreqn2o(oifr, ifr); + ifput(ifp); return error; } diff --git a/sys/compat/common/uipc_syscalls_40.c b/sys/compat/common/uipc_syscalls_40.c index ee49603..855706e 100644 --- a/sys/compat/common/uipc_syscalls_40.c +++ b/sys/compat/common/uipc_syscalls_40.c @@ -27,6 +27,9 @@ __KERNEL_RCSID(0, "$NetBSD: uipc_syscalls_40.c,v 1.8 2014/11/26 09:53:53 ozaki-r * of system. List may be used * in later ioctl's (above) to get * other information. + * + * If the user buffer pointer is NULL, this routine copies no data and + * returns the amount of space that would be needed. */ /*ARGSUSED*/ int @@ -36,29 +39,34 @@ compat_ifconf(u_long cmd, void *data) struct ifnet *ifp; struct ifaddr *ifa; struct oifreq ifr, *ifrp = NULL; - int space = 0, error = 0; + int space = 0, error = 0, buflen = 0; const int sz = (int)sizeof(ifr); const bool docopy = ifc->ifc_req != NULL; + char *buf = NULL; + int s; if (docopy) { - space = ifc->ifc_len; - ifrp = ifc->ifc_req; + buflen = space = ifc->ifc_len; + buf = kmem_zalloc(buflen, KM_SLEEP); + KASSERT(buf != NULL); + ifrp = (struct oifreq*)buf; } + IFNET_RENTER(s); 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); + memcpy(ifrp, &ifr, sz); ifrp++; } - space -= sizeof(ifr); + space -= sz; continue; } @@ -76,7 +84,7 @@ compat_ifconf(u_long cmd, void *data) memcpy(&ifr.ifr_addr, sa, sa->sa_len); osa->sa_family = sa->sa_family; if (space >= sz) { - error = copyout(&ifr, ifrp, sz); + memcpy(ifrp, &ifr, sz); ifrp++; } } else @@ -84,33 +92,39 @@ compat_ifconf(u_long cmd, void *data) if (sa->sa_len <= sizeof(*sa)) { memcpy(&ifr.ifr_addr, sa, sa->sa_len); if (space >= sz) { - error = copyout(&ifr, ifrp, sz); + memcpy(ifrp, &ifr, sz); ifrp++; } } else { space -= sa->sa_len - sizeof(*sa); if (space >= sz) { - error = copyout(&ifr, ifrp, + memcpy(ifrp, &ifr, sizeof(ifr.ifr_name)); - if (error == 0) { - error = copyout(sa, - &ifrp->ifr_addr, - sa->sa_len); - } + memcpy(&ifrp->ifr_addr, sa, + sa->sa_len); ifrp = (struct oifreq *) (sa->sa_len + (char *)&ifrp->ifr_addr); } } - if (error != 0) - return (error); space -= sz; } } +out: + IFNET_REXIT(s); + + if (error == 0) { + if (docopy) { + ifc->ifc_len -= space; + KASSERT(ifc->ifc_len <= buflen); + error = copyout(buf, ifc->ifc_req, ifc->ifc_len); + } else + ifc->ifc_len = -space; + } + if (docopy) - ifc->ifc_len -= space; - else - ifc->ifc_len = -space; - return (0); + kmem_free(buf, buflen); + + return error; } #endif diff --git a/sys/compat/common/uipc_syscalls_50.c b/sys/compat/common/uipc_syscalls_50.c index 116b717..36c060b 100644 --- a/sys/compat/common/uipc_syscalls_50.c +++ b/sys/compat/common/uipc_syscalls_50.c @@ -62,16 +62,16 @@ compat_ifdatareq(struct lwp *l, u_long cmd, void *data) { struct oifdatareq *ifdr = data; struct ifnet *ifp; - int error; + int error = 0; - ifp = ifunit(ifdr->ifdr_name); + ifp = ifget(ifdr->ifdr_name); if (ifp == NULL) return ENXIO; switch (cmd) { case SIOCGIFDATA: ifdatan2o(&ifdr->ifdr_data, &ifp->if_data); - return 0; + break; case SIOCZIFDATA: if (l != NULL) { @@ -80,7 +80,7 @@ compat_ifdatareq(struct lwp *l, u_long cmd, void *data) KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, NULL); if (error != 0) - return error; + goto out; } ifdatan2o(&ifdr->ifdr_data, &ifp->if_data); /* @@ -89,10 +89,13 @@ compat_ifdatareq(struct lwp *l, u_long cmd, void *data) */ memset(&ifp->if_data.ifi_ipackets, 0, sizeof(ifp->if_data) - offsetof(struct if_data, ifi_ipackets)); - return 0; + break; default: - return EINVAL; + error = EINVAL; } +out: + ifput(ifp); + return error; } #endif diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index 7b7e07d3..953d46c 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1115,9 +1115,11 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) struct ifaddr *ifa; struct sockaddr *sa; struct osockaddr *osa; - int space = 0, error; + int space = 0, error = 0, buflen = 0; const int sz = (int)sizeof(ifr); bool docopy; + char *buf = NULL; + int s; error = copyin(data, &ifc, sizeof(ifc)); if (error) @@ -1125,15 +1127,20 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) docopy = ifc.ifc_req != NULL; if (docopy) { - space = ifc.ifc_len; - ifrp = ifc.ifc_req; + buflen = space = ifc.ifc_len; + buf = kmem_zalloc(buflen, KM_SLEEP); + KASSERT(buf != NULL); + ifrp = (struct linux_ifreq*)buf; } + IFNET_RENTER(s); 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)) continue; IFADDR_FOREACH(ifa, ifp) { @@ -1145,21 +1152,31 @@ linux_getifconf(struct lwp *l, register_t *retval, void *data) osa = (struct osockaddr *)&ifr.ifr_addr; osa->sa_family = sa->sa_family; if (space >= sz) { - error = copyout(&ifr, ifrp, sz); - if (error != 0) - return error; + memcpy(ifrp, &ifr, sz); ifrp++; } space -= sz; } } +out: + IFNET_REXIT(s); + + if (error == 0) { + if (docopy) { + ifc.ifc_len -= space; + KASSERT(ifc.ifc_len <= buflen); + error = copyout(buf, ifc.ifc_req, ifc.ifc_len); + } else + ifc.ifc_len = -space; + + if (error == 0) + error = copyout(&ifc, data, sizeof(ifc)); + } if (docopy) - ifc.ifc_len -= space; - else - ifc.ifc_len = -space; + kmem_free(buf, buflen); - return copyout(&ifc, data, sizeof(ifc)); + return error; } int @@ -1172,8 +1189,9 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, struct ifaddr *ifa; struct ifnet *ifp; struct sockaddr_dl *sadl; - int error, found; + int error; int index, ifnum; + int s; /* * We can't emulate this ioctl by calling sys_ioctl() to run @@ -1204,16 +1222,14 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, /* * Try real interface name first, then fake "ethX" */ - found = 0; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { - if (found) - break; if (strcmp(lreq.ifr_name, ifp->if_xname)) /* not this interface */ continue; - found=1; if (IFADDR_EMPTY(ifp)) { error = ENODEV; + IFNET_REXIT(s); goto out; } IFADDR_FOREACH(ifa, ifp) { @@ -1228,10 +1244,15 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + + IFNET_REXIT(s); error = copyout(&lreq, data, sizeof(lreq)); goto out; } + /* Interface found, but no ethernet address found */ + break; } + IFNET_REXIT(s); if (strncmp(lreq.ifr_name, "eth", 3) != 0) { /* unknown interface, not even an "eth*" name */ @@ -1247,10 +1268,8 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, } error = EINVAL; /* in case we don't find one */ - found = 0; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { - if (found) - break; memcpy(lreq.ifr_name, ifp->if_xname, MIN(LINUX_IFNAMSIZ, IFNAMSIZ)); IFADDR_FOREACH(ifa, ifp) { @@ -1269,11 +1288,13 @@ linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + + IFNET_REXIT(s); error = copyout(&lreq, data, sizeof(lreq)); - found = 1; - break; + goto out; } } + IFNET_REXIT(s); out: KERNEL_UNLOCK_ONE(NULL); diff --git a/sys/compat/linux32/common/linux32_socket.c b/sys/compat/linux32/common/linux32_socket.c index 24c88ca..8e1ab53 100644 --- a/sys/compat/linux32/common/linux32_socket.c +++ b/sys/compat/linux32/common/linux32_socket.c @@ -416,9 +416,11 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) struct ifaddr *ifa; struct sockaddr *sa; struct osockaddr *osa; - int space = 0, error; + int space = 0, error = 0, buflen = 0; const int sz = (int)sizeof(ifr); bool docopy; + char *buf = NULL; + int s; error = copyin(data, &ifc, sizeof(ifc)); if (error) @@ -426,15 +428,20 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) docopy = NETBSD32PTR64(ifc.ifc_req) != NULL; if (docopy) { - space = ifc.ifc_len; - ifrp = NETBSD32PTR64(ifc.ifc_req); + buflen = space = ifc.ifc_len; + buf = kmem_zalloc(buflen, KM_SLEEP); + KASSERT(buf != NULL); + ifrp = (struct linux32_ifreq*)buf; } + IFNET_RENTER(s); 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)) continue; IFADDR_FOREACH(ifa, ifp) { @@ -446,21 +453,31 @@ linux32_getifconf(struct lwp *l, register_t *retval, void *data) osa = (struct osockaddr *)&ifr.ifr_addr; osa->sa_family = sa->sa_family; if (space >= sz) { - error = copyout(&ifr, ifrp, sz); - if (error != 0) - return error; + memcpy(ifrp, &ifr, sz); ifrp++; } space -= sz; } } +out: + IFNET_REXIT(s); + + if (error == 0) { + if (docopy) { + ifc.ifc_len -= space; + KASSERT(ifc.ifc_len <= buflen); + error = copyout(buf, NETBSD32PTR64(ifc.ifc_req), ifc.ifc_len); + } else + ifc.ifc_len = -space; + + if (error == 0) + error = copyout(&ifc, data, sizeof(ifc)); + } if (docopy) - ifc.ifc_len -= space; - else - ifc.ifc_len = -space; + kmem_free(buf, buflen); - return copyout(&ifc, data, sizeof(ifc)); + return error; } int @@ -474,6 +491,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, struct sockaddr_dl *sadl; int error, found; int index, ifnum; + int s; /* * We can't emulate this ioctl by calling sys_ioctl() to run @@ -505,6 +523,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, * Try real interface name first, then fake "ethX" */ found = 0; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if (found) break; @@ -513,6 +532,7 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, continue; found=1; if (IFADDR_EMPTY(ifp)) { + IFNET_REXIT(s); error = ENODEV; goto out; } @@ -528,10 +548,12 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + IFNET_REXIT(s); error = copyout(&lreq, data, sizeof(lreq)); goto out; } } + IFNET_REXIT(s); if (strncmp(lreq.ifr_name, "eth", 3) == 0) { for (ifnum = 0, index = 3; @@ -542,10 +564,8 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, } error = EINVAL; /* in case we don't find one */ - found = 0; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { - if (found) - break; memcpy(lreq.ifr_name, ifp->if_xname, MIN(LINUX32_IFNAMSIZ, IFNAMSIZ)); IFADDR_FOREACH(ifa, ifp) { @@ -564,11 +584,12 @@ linux32_getifhwaddr(struct lwp *l, register_t *retval, u_int fd, sizeof(lreq.ifr_hwaddr.sa_data))); lreq.ifr_hwaddr.sa_family = sadl->sdl_family; + IFNET_REXIT(s); error = copyout(&lreq, data, sizeof(lreq)); - found = 1; - break; + goto out; } } + IFNET_REXIT(s); } 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 dde39d1..7f2fcea 100644 --- a/sys/compat/svr4/svr4_sockio.c +++ b/sys/compat/svr4/svr4_sockio.c @@ -108,6 +108,7 @@ svr4_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, { struct ifnet *ifp; struct svr4_lifnum lifnum; + int s; error = copyin(data, &lifnum, sizeof(lifnum)); if (error) @@ -115,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_RENTER(s); IFNET_FOREACH(ifp) lifnum.lifn_count += svr4_count_ifnum(ifp); + IFNET_REXIT(s); DPRINTF(("SIOCGLIFNUM [family=%d,flags=%d,count=%d]\n", lifnum.lifn_family, lifnum.lifn_flags, @@ -128,6 +131,7 @@ svr4_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, { struct ifnet *ifp; int ifnum = 0; + int s; /* * This does not return the number of physical @@ -141,8 +145,10 @@ svr4_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, * entry per physical interface? */ + IFNET_RENTER(s); IFNET_FOREACH(ifp) ifnum += svr4_count_ifnum(ifp); + IFNET_REXIT(s); 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..4adffb7 100644 --- a/sys/compat/svr4_32/svr4_32_sockio.c +++ b/sys/compat/svr4_32/svr4_32_sockio.c @@ -96,6 +96,7 @@ svr4_32_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, int fd, u_long { struct ifnet *ifp; int ifnum = 0; + int s; /* * This does not return the number of physical @@ -109,8 +110,10 @@ svr4_32_sock_ioctl(file_t *fp, struct lwp *l, register_t *retval, int fd, u_long * entry per physical interface? */ + IFNET_RENTER(s); IFNET_FOREACH(ifp) ifnum += svr4_count_ifnum(ifp) + IFNET_REXIT(s); 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 81908f1..1131ac0 100644 --- a/sys/dev/pci/if_bge.c +++ b/sys/dev/pci/if_bge.c @@ -1516,6 +1516,7 @@ bge_update_all_threshes(int lvl) struct ifnet *ifp; const char * const namebuf = "bge"; int namelen; + int s; if (lvl < 0) lvl = 0; @@ -1526,6 +1527,7 @@ bge_update_all_threshes(int lvl) /* * Now search all the interfaces for this name/number */ + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if (strncmp(ifp->if_xname, namebuf, namelen) != 0) continue; @@ -1533,6 +1535,7 @@ bge_update_all_threshes(int lvl) if (bge_auto_thresh) bge_set_thresh(ifp, lvl); } + IFNET_REXIT(s); } /* diff --git a/sys/dist/pf/net/pf_if.c b/sys/dist/pf/net/pf_if.c index 842e824..239c86e 100644 --- a/sys/dist/pf/net/pf_if.c +++ b/sys/dist/pf/net/pf_if.c @@ -100,6 +100,8 @@ RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare); void pfi_initialize(void) { + int s; + if (pfi_all != NULL) /* already initialized */ return; @@ -119,10 +121,20 @@ pfi_initialize(void) #ifdef __NetBSD__ ifnet_t *ifp; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { + if (IFNET_DYING_P(ifp)) + continue; + ifhold(ifp); + IFNET_REXIT(s); + pfi_init_groups(ifp); pfi_attach_ifnet(ifp); + + ifput(ifp); + IFNET_RENTER(s); } + IFNET_REXIT(s); pfil_add_hook(pfil_ifnet_wrapper, NULL, PFIL_IFNET, if_pfil); pfil_add_hook(pfil_ifaddr_wrapper, NULL, PFIL_IFADDR, if_pfil); @@ -134,15 +146,26 @@ void pfi_destroy(void) { struct pfi_kif *p; - ifnet_t *ifp; + ifnet_t *ifp, *_next; + int s; pfil_remove_hook(pfil_ifaddr_wrapper, NULL, PFIL_IFADDR, if_pfil); pfil_remove_hook(pfil_ifnet_wrapper, NULL, PFIL_IFNET, if_pfil); - IFNET_FOREACH(ifp) { + IFNET_RENTER(s); + IFNET_FOREACH_SAFE(ifp, _next) { + if (IFNET_DYING_P(ifp)) + continue; + ifhold(ifp); + IFNET_REXIT(s); + pfi_detach_ifnet(ifp); pfi_destroy_groups(ifp); + + ifput(ifp); + IFNET_RENTER(s); } + IFNET_REXIT(s); 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 4e51f69..63fe07c 100644 --- a/sys/kern/kern_subr.c +++ b/sys/kern/kern_subr.c @@ -224,11 +224,15 @@ setroot(device_t bootdv, int bootpartition) if (vops != NULL && strcmp(rootfstype, MOUNT_NFS) == 0 && rootspec == NULL && (bootdv == NULL || device_class(bootdv) != DV_IFNET)) { + int s; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) break; } + IFNET_REXIT(s); + if (ifp == NULL) { /* * Can't find a suitable interface; ask the diff --git a/sys/kern/subr_tftproot.c b/sys/kern/subr_tftproot.c index d849511..7b9fac0 100644 --- a/sys/kern/subr_tftproot.c +++ b/sys/kern/subr_tftproot.c @@ -131,16 +131,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/net/agr/if_agr.c b/sys/net/agr/if_agr.c index 5210867..83c3a7ab 100644 --- a/sys/net/agr/if_agr.c +++ b/sys/net/agr/if_agr.c @@ -459,7 +459,7 @@ agr_setconfig(struct agr_softc *sc, const struct agrreq *ar) if (error) { return error; } - ifp_port = ifunit(ifname); + ifp_port = ifget(ifname); if (ifp_port == NULL) { return ENOENT; } @@ -479,6 +479,7 @@ agr_setconfig(struct agr_softc *sc, const struct agrreq *ar) break; } agr_ports_unlock(sc); + ifput(ifp_port); return error; } diff --git a/sys/net/if.c b/sys/net/if.c index 313b4c1c..a8002b5 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -169,9 +169,67 @@ static uint64_t index_gen; static kmutex_t index_gen_mtx; static kmutex_t if_clone_mtx; +/* + * Mutex to protect the above objects. + */ +kmutex_t ifnet_mtx __cacheline_aligned; +static pserialize_t ifnet_psz; + struct ifnet *lo0ifp; int ifqmaxlen = IFQ_MAXLEN; +/* + * ifnet locking rules: + * - ifnet_mtx + * - is an adaptive lock + * - serializes modifications of ifnet_list + * - is used with IFNET_{LOCK,UNLOCK,LOCKED} macros + * - ifnet_psz + * - is used for pserialize on ifnet_list and its ifnet objects + * - ifnet_list + * - lists all existing ifnet objects + * - should be iterated with IFNET_FOREACH + * - is protected with the pserialize facility + * - must be read between IFNET_RENTER and IFNET_REXIT + * - must not block/sleep during a critical section + * - if want to do so, use the referenc counting facility + * to keep holding an ifnet object (see below) + * - must be modified in the pserialize way: + * call IFNET_LOCK, LIST_REMOVE and pserialize_perform to ensure + * nobody is working on the removing oject + * + * TODO: + * - ifnet_list is access by m_reclaim that can be still run + * in hardware interrupt. We should avoid that somehow. + */ + +/* + * ifget/ifput/ifhold - ifnet object reference + * A replacement of ifunit API. ifget returns an ifnet object as same as ifunit, + * but additionally it takes a reference to the ifnet object. The kernel won't + * free the ifnet object until there is no reference to it. ifput needs to be + * called when finished working on the ifnet object to release it. ifget may + * return NULL when a target interface is absent or being destroyed. ifhold + * can be used instead of ifget when ifp is already available. + * + * Typical usage is like this: + * + * ifp = ifget(name); // or ifhold(ifp); + * if (ifp == NULL) + * return ENXIO; + * // do something on ifp + * ifput(ifp); + * + * It uses an adaptive lock so it cannot be used in hardware interrupt. + * + * Note that ifget needs no locking, however, ifput needs locking + * to call cv_wait safely. It's good to remove the locking somehow. + * + * TODO: + * - We have to deal with ifnet pointers embedded to other objects, e.g., rtentry. + * An embedded pointer of ifnet can be expired without knowing by its host object. + */ + static int if_rt_walktree(struct rtentry *, void *); static struct if_clone *if_clone_lookup(const char *, int *); @@ -256,6 +314,8 @@ ifinit1(void) { mutex_init(&index_gen_mtx, MUTEX_DEFAULT, IPL_NONE); mutex_init(&if_clone_mtx, MUTEX_DEFAULT, IPL_NONE); + ifnet_psz = pserialize_create(); + mutex_init(&ifnet_mtx, MUTEX_DEFAULT, IPL_NONE); TAILQ_INIT(&ifnet_list); if_indexlim = 8; @@ -596,6 +656,8 @@ if_initialize(ifnet_t *ifp) ifp->if_capenable = 0; ifp->if_csum_flags_tx = 0; ifp->if_csum_flags_rx = 0; + ifp->if_dying = false; + refcount_init(&ifp->if_refcount, &ifnet_mtx, "ifp"); #ifdef ALTQ ifp->if_snd.altq_type = 0; @@ -645,7 +707,9 @@ if_register(ifnet_t *ifp) if_slowtimo(ifp); } + IFNET_LOCK(); TAILQ_INSERT_TAIL(&ifnet_list, ifp, if_list); + IFNET_UNLOCK(); } /* @@ -666,6 +730,7 @@ if_attachdomain(void) int s; s = splnet(); + /* Called only during boot */ IFNET_FOREACH(ifp) if_attachdomain1(ifp); splx(s); @@ -756,6 +821,16 @@ if_detach(struct ifnet *ifp) s = splnet(); + sysctl_teardown(&ifp->if_sysctl_log); + + IFNET_LOCK(); + ifindex2ifnet[ifp->if_index] = NULL; + TAILQ_REMOVE(&ifnet_list, ifp, if_list); + + pserialize_perform(ifnet_psz); + refcount_wait(ifp->if_refcount); + IFNET_UNLOCK(); + if (ifp->if_slowtimo != NULL) { ifp->if_slowtimo = NULL; callout_halt(ifp->if_slowtimo_ch, NULL); @@ -763,6 +838,8 @@ if_detach(struct ifnet *ifp) kmem_free(ifp->if_slowtimo_ch, sizeof(*ifp->if_slowtimo_ch)); } + refcount_destroy(ifp->if_refcount); + /* * Do an if_down() to give protocols a chance to do something. */ @@ -778,8 +855,6 @@ if_detach(struct ifnet *ifp) if (ifp->if_snd.ifq_lock) mutex_obj_free(ifp->if_snd.ifq_lock); - sysctl_teardown(&ifp->if_sysctl_log); - #if NCARP > 0 /* Remove the interface from any carp group it is a part of. */ if (ifp->if_carp != NULL && ifp->if_type != IFT_CARP) @@ -890,10 +965,6 @@ again: /* Announce that the interface is gone. */ rt_ifannouncemsg(ifp, IFAN_DEPARTURE); - ifindex2ifnet[ifp->if_index] = NULL; - - TAILQ_REMOVE(&ifnet_list, ifp, if_list); - ifioctl_detach(ifp); /* @@ -977,6 +1048,7 @@ if_rt_walktree(struct rtentry *rt, void *v) KASSERT(retrt == rt); KASSERT((retrt->rt_flags & RTF_UP) == 0); retrt->rt_ifp = NULL; + retrt->rt_if_index = -1; rtfree(retrt); } else { printf("%s: warning: unable to delete rtentry @ %p, " @@ -1017,7 +1089,11 @@ if_clone_destroy(const char *name) if (ifc == NULL) return EINVAL; + IFNET_LOCK(); ifp = ifunit(name); + if (ifp != NULL) + ifp->if_dying = true; + IFNET_UNLOCK(); if (ifp == NULL) return ENXIO; @@ -1180,8 +1256,10 @@ struct ifaddr * ifa_ifwithaddr(const struct sockaddr *addr) { struct ifnet *ifp; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; + int s; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if (ifp->if_output == if_nulloutput) continue; @@ -1189,16 +1267,20 @@ ifa_ifwithaddr(const struct sockaddr *addr) if (ifa->ifa_addr->sa_family != addr->sa_family) continue; if (equal(addr, ifa->ifa_addr)) - return ifa; + goto found; if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr && /* IP6 doesn't have broadcast */ ifa->ifa_broadaddr->sa_len != 0 && equal(ifa->ifa_broadaddr, addr)) - return ifa; + goto found; } } - return NULL; + ifa = NULL; +found: + IFNET_REXIT(s); + + return ifa; } /* @@ -1210,7 +1292,9 @@ ifa_ifwithdstaddr(const struct sockaddr *addr) { struct ifnet *ifp; struct ifaddr *ifa; + int s; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if (ifp->if_output == if_nulloutput) continue; @@ -1220,10 +1304,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_REXIT(s); return ifa; + } } } + IFNET_REXIT(s); return NULL; } @@ -1240,13 +1327,16 @@ ifa_ifwithnet(const struct sockaddr *addr) struct ifaddr *ifa_maybe = 0; u_int af = addr->sa_family; const char *addr_data = addr->sa_data, *cplim; + int s; + IFNET_RENTER(s); 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 ifindex2ifnet[sdl->sdl_index]->if_dl; + ifa_maybe = ifindex2ifnet[sdl->sdl_index]->if_dl; + goto out; } } #ifdef NETATALK @@ -1260,14 +1350,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) { @@ -1296,6 +1388,8 @@ ifa_ifwithnet(const struct sockaddr *addr) ifa_maybe = ifa; } } +out: + IFNET_REXIT(s); return ifa_maybe; } @@ -1321,15 +1415,20 @@ ifa_ifwithaf(int af) { struct ifnet *ifp; struct ifaddr *ifa; + int s; + IFNET_RENTER(s); 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_REXIT(s); return ifa; + } } } + IFNET_REXIT(s); return NULL; } @@ -1642,7 +1741,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; @@ -1659,20 +1758,53 @@ ifunit(const char *name) */ 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; + if (ifp == NULL || IFNET_DYING_P(ifp) || + ifp->if_output == if_nulloutput) + ifp = NULL; + goto out; } IFNET_FOREACH(ifp) { - if (ifp->if_output == if_nulloutput) + if (IFNET_DYING_P(ifp) || ifp->if_output == if_nulloutput) continue; if (strcmp(ifp->if_xname, name) == 0) - return ifp; + goto out; } - return NULL; + ifp = NULL; +out: + return ifp; +} + +struct ifnet * +ifget(const char *name) +{ + struct ifnet *ifp; + int s; + + IFNET_RENTER(s); + ifp = ifunit(name); + if (ifp != NULL) + refcount_hold(ifp->if_refcount); + IFNET_REXIT(s); + + return ifp; +} + +void +ifput(struct ifnet *ifp) +{ + if (ifp == NULL) + return; + + refcount_release(ifp->if_refcount); +} + +void +ifhold(struct ifnet *ifp) +{ + refcount_hold(ifp->if_refcount); } ifnet_t * @@ -1681,6 +1813,25 @@ if_byindex(u_int idx) return (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; } +ifnet_t * +ifget_byindex(u_int idx) +{ + struct ifnet *ifp; + int s; + + IFNET_RENTER(s); + ifp = if_byindex(idx); + if (ifp != NULL) { + if (IFNET_DYING_P(ifp)) + ifp = NULL; + else + refcount_hold(ifp->if_refcount); + } + IFNET_REXIT(s); + + return ifp; +} + /* common */ int ifioctl_common(struct ifnet *ifp, u_long cmd, void *data) @@ -1969,7 +2120,7 @@ doifioctl(struct socket *so, u_long cmd, void *data, struct lwp *l) #endif ifr = data; - ifp = ifunit(ifr->ifr_name); + ifp = ifget(ifr->ifr_name); switch (cmd) { case SIOCIFCREATE: @@ -1979,9 +2130,12 @@ doifioctl(struct socket *so, u_long cmd, void *data, struct lwp *l) KAUTH_NETWORK_INTERFACE, KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, NULL); - if (error != 0) + if (error != 0) { + ifput(ifp); return error; + } } + ifput(ifp); mutex_enter(&if_clone_mtx); r = (cmd == SIOCIFCREATE) ? if_clone_create(ifr->ifr_name) : @@ -1992,8 +2146,10 @@ doifioctl(struct socket *so, u_long cmd, void *data, struct lwp *l) case SIOCIFGCLONERS: { struct if_clonereq *req = (struct if_clonereq *)data; - return if_clone_list(req->ifcr_count, req->ifcr_buffer, + r = if_clone_list(req->ifcr_count, req->ifcr_buffer, &req->ifcr_total); + ifput(ifp); + return r; } } @@ -2032,8 +2188,10 @@ doifioctl(struct socket *so, u_long cmd, void *data, struct lwp *l) KAUTH_NETWORK_INTERFACE, KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, NULL); - if (error != 0) + if (error != 0) { + ifput(ifp); return error; + } } } @@ -2067,6 +2225,7 @@ doifioctl(struct socket *so, u_long cmd, void *data, struct lwp *l) #endif ifnet_lock_exit(ifp->if_ioctl_lock); + ifput(ifp); return error; } @@ -2196,20 +2355,27 @@ ifconf(u_long cmd, void *data) struct ifnet *ifp; struct ifaddr *ifa; struct ifreq ifr, *ifrp = NULL; - int space = 0, error = 0; + int space = 0, error = 0, buflen = 0; const int sz = (int)sizeof(struct ifreq); const bool docopy = ifc->ifc_req != NULL; + char *buf = NULL; + int s; if (docopy) { - space = ifc->ifc_len; - ifrp = ifc->ifc_req; + buflen = space = ifc->ifc_len; + buf = kmem_zalloc(buflen, KM_SLEEP); + KASSERT(buf != NULL); + ifrp = (struct ifreq*)buf; } + IFNET_RENTER(s); 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)) { /* Interface with no addresses - send zero sockaddr. */ memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr)); @@ -2218,9 +2384,7 @@ ifconf(u_long cmd, void *data) continue; } if (space >= sz) { - error = copyout(&ifr, ifrp, sz); - if (error != 0) - return error; + memcpy(ifrp, &ifr, sz); ifrp++; space -= sz; } @@ -2237,21 +2401,31 @@ 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); + memcpy(ifrp, &ifr, sz); ifrp++; space -= sz; } } } - if (docopy) { - KASSERT(0 <= space && space <= ifc->ifc_len); - ifc->ifc_len -= space; - } else { - KASSERT(space >= 0); - ifc->ifc_len = space; +out: + IFNET_REXIT(s); + + if (error == 0) { + if (docopy) { + KASSERT(0 <= space && space <= ifc->ifc_len); + ifc->ifc_len -= space; + + KASSERT(ifc->ifc_len <= buflen); + error = copyout(buf, ifc->ifc_req, ifc->ifc_len); + } else { + KASSERT(space >= 0); + ifc->ifc_len = space; + } } - return (0); + + if (docopy) + kmem_free(buf, buflen); + + return error; } int @@ -2602,28 +2776,33 @@ if_sdl_sysctl(SYSCTLFN_ARGS) { struct ifnet *ifp; const struct sockaddr_dl *sdl; + int r = 0; if (namelen != 1) return EINVAL; - ifp = if_byindex(name[0]); + ifp = ifget_byindex(name[0]); if (ifp == NULL) return ENODEV; sdl = ifp->if_sadl; if (sdl == NULL) { *oldlenp = 0; - return 0; + goto out; } if (oldp == NULL) { *oldlenp = sdl->sdl_alen; - return 0; + goto out; } if (*oldlenp >= sdl->sdl_alen) *oldlenp = sdl->sdl_alen; - return sysctl_copyout(l, &sdl->sdl_data[sdl->sdl_nlen], oldp, *oldlenp); + r = sysctl_copyout(l, &sdl->sdl_data[sdl->sdl_nlen], oldp, *oldlenp); + +out: + ifput(ifp); + return r; } SYSCTL_SETUP(sysctl_net_sdl_setup, "sysctl net.sdl subtree setup") diff --git a/sys/net/if.h b/sys/net/if.h index ba28c5f..12b7d3e 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -209,6 +209,7 @@ struct ifqueue { }; struct ifnet_lock; +struct refcount; #ifdef _KERNEL #include @@ -219,6 +220,9 @@ struct ifnet_lock; #else #include #endif +#include +#include +#include struct ifnet_lock { kmutex_t il_lock; /* Protects the critical section. */ @@ -238,6 +242,79 @@ struct ifnet_lock { * before they leave. */ }; + +struct refcount { + kcondvar_t rc_cv; + unsigned int rc_refs; + bool rc_waiting; + kmutex_t *rc_mtx; +}; + +static inline void +refcount_init(struct refcount **rc, kmutex_t *mtx, const char *wmesg) +{ + struct refcount *rcp; + + rcp = kmem_alloc(sizeof(**rc), KM_SLEEP); + KASSERT(rcp != NULL); + + cv_init(&rcp->rc_cv, wmesg); + rcp->rc_refs = 0; + rcp->rc_waiting = false; + rcp->rc_mtx = mtx; + + *rc = rcp; +} + +static inline void +refcount_destroy(struct refcount *rcp) +{ + cv_destroy(&rcp->rc_cv); + kmem_free(rcp, sizeof(*rcp)); +} + +static inline void +refcount_hold(struct refcount *rc) +{ + atomic_inc_uint(&rc->rc_refs); +} + +static inline void +refcount_release(struct refcount *rc) +{ + unsigned int refs; + + do { + refs = rc->rc_refs; + if (refs == 1) { + mutex_enter(rc->rc_mtx); + refs = atomic_dec_uint_nv(&rc->rc_refs); + if (__predict_false(refs == 0 && rc->rc_waiting)) + cv_broadcast(&rc->rc_cv); + mutex_exit(rc->rc_mtx); + break; + } + } while (atomic_cas_uint(&rc->rc_refs, refs, refs - 1) != refs); +} + +static inline int +refcount_refs(struct refcount *rc) +{ + return rc->rc_refs; +} + +static inline void +refcount_wait(struct refcount *rc) +{ + KASSERT(mutex_owned(rc->rc_mtx)); + + rc->rc_waiting = true; + membar_sync(); + while (__predict_false(rc->rc_refs > 0)) { + cv_wait(&rc->rc_cv, rc->rc_mtx); + } + rc->rc_waiting = false; +} #endif /* _KERNEL */ /* @@ -351,6 +428,8 @@ typedef struct ifnet { struct ifnet_lock *if_ioctl_lock; #ifdef _KERNEL /* XXX kvm(3) */ struct callout *if_slowtimo_ch; + struct refcount *if_refcount; + bool if_dying; #endif #ifdef GATEWAY struct kmutex *if_afdata_lock; @@ -939,6 +1018,10 @@ extern int (*ifioctl)(struct socket *, u_long, void *, struct lwp *); int ifioctl_common(struct ifnet *, u_long, void *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); +struct ifnet *ifget(const char *); +struct ifnet *ifget_byindex(u_int); +void ifhold(struct ifnet *); +void ifput(struct ifnet *ifp); int if_addr_init(ifnet_t *, struct ifaddr *, bool); int if_do_dad(struct ifnet *); int if_mcast_op(ifnet_t *, const unsigned long, const struct sockaddr *); @@ -1008,10 +1091,21 @@ __END_DECLS #ifdef _KERNEL +extern kmutex_t ifnet_mtx; + #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_FOREACH_SAFE(__ifp, __next) \ + TAILQ_FOREACH_SAFE(__ifp, &ifnet_list, if_list, __next) +#define IFNET_RENTER(__s) do {(__s) = pserialize_read_enter();} while (0) +#define IFNET_REXIT(__s) pserialize_read_exit((__s)) +#define IFNET_LOCK() mutex_enter(&ifnet_mtx) +#define IFNET_UNLOCK() mutex_exit(&ifnet_mtx) +#define IFNET_LOCKED() mutex_owned(&ifnet_mtx) +#define IFNET_DYING_P(__ifp) (__ifp->if_dying) + #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, \ diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index f51edd3..8e3885a 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -869,27 +869,41 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) struct ifnet *ifs; int error = 0; - ifs = ifunit(req->ifbr_ifsname); + ifs = ifget(req->ifbr_ifsname); if (ifs == NULL) - return (ENOENT); + return ENOENT; - if (sc->sc_if.if_mtu != ifs->if_mtu) - return (EINVAL); + if (sc->sc_if.if_mtu != ifs->if_mtu) { + error = EINVAL; + goto out; + } - if (ifs->if_bridge == sc) - return (EEXIST); + if (ifs->if_bridge == sc) { + error = EEXIST; + goto out; + } - if (ifs->if_bridge != NULL) - return (EBUSY); + if (ifs->if_bridge != NULL) { + error = EBUSY; + goto out; + } - if (ifs->if_input != ether_input) - return EINVAL; + if (ifs->if_input != ether_input) { + error = EINVAL; + goto out; + } /* FIXME: doesn't work with non-IFF_SIMPLEX interfaces */ - if ((ifs->if_flags & IFF_SIMPLEX) == 0) - return EINVAL; + if ((ifs->if_flags & IFF_SIMPLEX) == 0) { + error = EINVAL; + goto out; + } bif = kmem_alloc(sizeof(*bif), KM_SLEEP); + if (bif == NULL) { + error = ENOMEM; + goto out; + } switch (ifs->if_type) { case IFT_ETHER: @@ -933,7 +947,8 @@ bridge_ioctl_add(struct bridge_softc *sc, void *arg) if (bif != NULL) kmem_free(bif, sizeof(*bif)); } - return (error); + ifput(ifs); + return error; } static int diff --git a/sys/net/if_pppoe.c b/sys/net/if_pppoe.c index 9c3ca64..a7ec234 100644 --- a/sys/net/if_pppoe.c +++ b/sys/net/if_pppoe.c @@ -887,11 +887,16 @@ pppoe_ioctl(struct ifnet *ifp, unsigned long cmd, void *data) if (parms->eth_ifname[0] != 0) { struct ifnet *eth_if; - eth_if = ifunit(parms->eth_ifname); - if (eth_if == NULL || eth_if->if_dlt != DLT_EN10MB) { + eth_if = ifget(parms->eth_ifname); + if (eth_if == NULL) { sc->sc_eth_if = NULL; return ENXIO; } + if (eth_if->if_dlt != DLT_EN10MB) { + sc->sc_eth_if = NULL; + ifput(eth_if); + return ENXIO; + } if (sc->sc_sppp.pp_if.if_mtu != eth_if->if_mtu - PPPOE_OVERHEAD) { @@ -899,6 +904,7 @@ pppoe_ioctl(struct ifnet *ifp, unsigned long cmd, void *data) PPPOE_OVERHEAD; } sc->sc_eth_if = eth_if; + ifput(eth_if); } if (parms->ac_name != NULL) { size_t s; diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 9ffaf1e..7aeca8a 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -495,11 +495,13 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, void *data) error = EINVAL; /* check for valid tag */ break; } - if ((pr = ifunit(vlr.vlr_parent)) == 0) { + if ((pr = ifget(vlr.vlr_parent)) == NULL) { error = ENOENT; break; } - if ((error = vlan_config(ifv, pr)) != 0) + error = vlan_config(ifv, pr); + ifput(pr); + if (error != 0) break; ifv->ifv_tag = vlr.vlr_tag; ifp->if_flags |= IFF_RUNNING; diff --git a/sys/net/npf/npf_if.c b/sys/net/npf/npf_if.c index 1352686..9bddd9e 100644 --- a/sys/net/npf/npf_if.c +++ b/sys/net/npf/npf_if.c @@ -117,8 +117,9 @@ npf_ifmap_register(const char *ifname) strlcpy(nim->n_ifname, ifname, IFNAMSIZ); KERNEL_LOCK(1, NULL); - if ((ifp = ifunit(ifname)) != NULL) { + if ((ifp = ifget(ifname)) != NULL) { ifp->if_pf_kif = (void *)(uintptr_t)i; + ifput(ifp); } KERNEL_UNLOCK_ONE(NULL); out: @@ -130,6 +131,7 @@ void npf_ifmap_flush(void) { ifnet_t *ifp; + int s; KASSERT(npf_config_locked_p()); @@ -139,9 +141,11 @@ npf_ifmap_flush(void) npf_ifmap_cnt = 0; KERNEL_LOCK(1, NULL); + IFNET_RENTER(s); IFNET_FOREACH(ifp) { ifp->if_pf_kif = (void *)(uintptr_t)0; } + IFNET_REXIT(s); KERNEL_UNLOCK_ONE(NULL); } diff --git a/sys/net/route.c b/sys/net/route.c index 77ab7e8..5023002 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -451,6 +451,7 @@ rtfree(struct rtentry *rt) rt->rt_ifa = NULL; ifafree(ifa); rt->rt_ifp = NULL; + rt->rt_if_index = -1; rt_destroy(rt); pool_put(&rtentry_pool, rt); } @@ -860,10 +861,13 @@ rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) 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) + ifa2->ifa_ifp != NULL) { rt->rt_ifp = ifa2->ifa_ifp; - else + rt->rt_if_index = ifa2->ifa_ifp->if_index; + } else { rt->rt_ifp = ifa->ifa_ifp; + rt->rt_if_index = ifa->ifa_ifp->if_index; + } if (req == RTM_RESOLVE) { rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ rt->rt_parent = *ret_nrt; @@ -1096,6 +1100,7 @@ rtinit(struct ifaddr *ifa, int cmd, int flags) } rt_replace_ifa(rt, ifa); rt->rt_ifp = ifa->ifa_ifp; + rt->rt_if_index = ifa->ifa_ifp->if_index; if (ifa->ifa_rtrequest != NULL) ifa->ifa_rtrequest(RTM_ADD, rt, &info); } diff --git a/sys/net/route.h b/sys/net/route.h index a4aa070..c91714a 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -106,6 +106,7 @@ struct rtentry { int rt_refcnt; /* # held references */ uint64_t rt_use; /* raw # packets forwarded */ struct ifnet *rt_ifp; /* the answer: interface to use */ + u_short rt_if_index; /* numeric abbreviation for the interface */ struct ifaddr *rt_ifa; /* the answer: interface to use */ uint32_t rt_ifa_seqno; void * rt_llinfo; /* pointer to link level info cache */ @@ -135,6 +136,7 @@ struct ortentry { int16_t rt_refcnt; /* # held references */ uint32_t rt_use; /* raw # packets forwarded */ struct ifnet *rt_ifp; /* the answer: interface to use */ + u_short rt_if_index; /* numeric abbreviation for the interface */ }; #define RTF_UP 0x1 /* route usable */ diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 2507c5a..6690616 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -635,10 +635,13 @@ COMPATNAME(route_output)(struct mbuf *m, ...) } rt_replace_ifa(rt, ifa); rt->rt_ifp = ifp; + rt->rt_if_index = ifp->if_index; } } - if (ifp && rt->rt_ifp != ifp) + if (ifp && rt->rt_ifp != ifp) { rt->rt_ifp = ifp; + rt->rt_if_index = ifp->if_index; + } rt_setmetrics(rtm->rtm_inits, rtm, rt); if (rt->rt_flags != info.rti_flags) rt->rt_flags = (info.rti_flags & ~PRESERVED_RTF) @@ -1225,6 +1228,8 @@ sysctl_dumpentry(struct rtentry *rt, void *v) struct rt_walkarg *w = v; int error = 0, size; struct rt_addrinfo info; + struct ifnet *ifp; + const struct ifaddr *rtifa; if (w->w_op == NET_RT_FLAGS && !(rt->rt_flags & w->w_arg)) return 0; @@ -1233,28 +1238,32 @@ sysctl_dumpentry(struct rtentry *rt, void *v) info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; info.rti_info[RTAX_NETMASK] = rt_mask(rt); info.rti_info[RTAX_TAG] = rt_gettag(rt); - if (rt->rt_ifp) { - const struct ifaddr *rtifa; - info.rti_info[RTAX_IFP] = rt->rt_ifp->if_dl->ifa_addr; - /* rtifa used to be simply rt->rt_ifa. If rt->rt_ifa != NULL, - * then rt_get_ifa() != NULL. So this ought to still be safe. - * --dyoung - */ - rtifa = rt_get_ifa(rt); - info.rti_info[RTAX_IFA] = rtifa->ifa_addr; - if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) - info.rti_info[RTAX_BRD] = rtifa->ifa_dstaddr; - } - if ((error = rt_msg2(RTM_GET, &info, 0, w, &size))) + + ifp = ifget_byindex(rt->rt_if_index); + if (ifp == NULL) + return ENXIO; + + info.rti_info[RTAX_IFP] = ifp->if_dl->ifa_addr; + /* rtifa used to be simply rt->rt_ifa. If rt->rt_ifa != NULL, + * then rt_get_ifa() != NULL. So this ought to still be safe. + * --dyoung + */ + rtifa = rt_get_ifa(rt); + info.rti_info[RTAX_IFA] = rtifa->ifa_addr; + if (ifp->if_flags & IFF_POINTOPOINT) + info.rti_info[RTAX_BRD] = rtifa->ifa_dstaddr; + + if ((error = rt_msg2(RTM_GET, &info, 0, w, &size))) { + ifput(ifp); return error; + } if (w->w_where && w->w_tmem && w->w_needed <= 0) { struct rt_xmsghdr *rtm = (struct rt_xmsghdr *)w->w_tmem; rtm->rtm_flags = rt->rt_flags; rtm->rtm_use = rt->rt_use; rtm_setmetrics(rt, rtm); - KASSERT(rt->rt_ifp != NULL); - rtm->rtm_index = rt->rt_ifp->if_index; + rtm->rtm_index = ifp->if_index; rtm->rtm_errno = rtm->rtm_pid = rtm->rtm_seq = 0; rtm->rtm_addrs = info.rti_addrs; if ((error = copyout(rtm, w->w_where, size)) != 0) @@ -1262,23 +1271,32 @@ sysctl_dumpentry(struct rtentry *rt, void *v) else w->w_where = (char *)w->w_where + size; } + ifput(ifp); return error; } static int sysctl_iflist(int af, struct rt_walkarg *w, int type) { - struct ifnet *ifp; + struct ifnet *ifp, *_next; struct ifaddr *ifa; struct rt_addrinfo info; int len, error = 0; + int s; + bool hold = false; memset(&info, 0, sizeof(info)); - IFNET_FOREACH(ifp) { - if (w->w_arg && w->w_arg != ifp->if_index) - continue; - if (IFADDR_EMPTY(ifp)) + + IFNET_RENTER(s); + IFNET_FOREACH_SAFE(ifp, _next) { + if (IFNET_DYING_P(ifp) || + (w->w_arg && w->w_arg != ifp->if_index) || + IFADDR_EMPTY(ifp)) continue; + ifhold(ifp); + hold = true; + IFNET_REXIT(s); + info.rti_info[RTAX_IFP] = ifp->if_dl->ifa_addr; switch (type) { case NET_RT_IFLIST: @@ -1298,7 +1316,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) { @@ -1312,7 +1330,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; } @@ -1321,14 +1339,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: @@ -1342,7 +1360,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; @@ -1353,14 +1371,22 @@ 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; + + ifput(ifp); + hold = false; + IFNET_RENTER(s); } - return 0; + IFNET_REXIT(s); +out: + if (ifp != NULL && hold) + ifput(ifp); + return error; } static int diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index 96ff802..d6065f3 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -925,21 +925,31 @@ carp_prepare_ad(struct mbuf *m, struct carp_softc *sc, static void carp_send_ad_all(void) { - struct ifnet *ifp; + struct ifnet *ifp, *_next; struct carp_if *cif; struct carp_softc *vh; + int s; - IFNET_FOREACH(ifp) { - if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP) + IFNET_RENTER(s); + IFNET_FOREACH_SAFE(ifp, _next) { + if (ifp->if_carp == NULL || ifp->if_type == IFT_CARP || + IFNET_DYING_P(ifp)) continue; + ifhold(ifp); + IFNET_REXIT(s); + cif = (struct carp_if *)ifp->if_carp; TAILQ_FOREACH(vh, &cif->vhif_vrs, sc_list) { if ((vh->sc_if.if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING) && vh->sc_state == MASTER) carp_send_ad(vh); } + + ifput(ifp); + IFNET_RENTER(s); } + IFNET_REXIT(s); } @@ -1977,10 +1987,12 @@ carp_ioctl(struct ifnet *ifp, u_long cmd, void *data) break; error = 1; if (carpr.carpr_carpdev[0] != '\0' && - (cdev = ifunit(carpr.carpr_carpdev)) == NULL) - return (EINVAL); - if ((error = carp_set_ifp(sc, cdev))) - return (error); + (cdev = ifget(carpr.carpr_carpdev)) == NULL) + return EINVAL; + error = carp_set_ifp(sc, cdev); + ifput(cdev); + if (error) + return error; if (sc->sc_state != INIT && carpr.carpr_state != sc->sc_state) { switch (carpr.carpr_state) { case BACKUP: diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 6a4c946..32877f9 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -1358,6 +1358,8 @@ ni6_input(struct mbuf *m, int off) MGETHDR(n, M_DONTWAIT, m->m_type); if (n == NULL) { m_freem(m); + if (ifp != NULL) + ifput(ifp); return (NULL); } M_MOVE_PKTHDR(n, m); /* just for rcvif */ @@ -1424,6 +1426,7 @@ ni6_input(struct mbuf *m, int off) sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo); lenlim = M_TRAILINGSPACE(n); copied = ni6_store_addrs(ni6, nni6, ifp, lenlim); + ifput(ifp); /* XXX: reset mbuf length */ n->m_pkthdr.len = n->m_len = sizeof(struct ip6_hdr) + sizeof(struct icmp6_nodeinfo) + copied; @@ -1441,6 +1444,8 @@ ni6_input(struct mbuf *m, int off) m_freem(m); if (n) m_freem(n); + if (ifp != NULL) + ifput(ifp); return (NULL); } #undef hostnamelen @@ -1635,6 +1640,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, struct sockaddr_in6 *subj_ip6 = NULL; /* XXX pedant */ int addrs = 0, addrsofif, iffound = 0; int niflags = ni6->ni_flags; + int s; if ((niflags & NI_NODEADDR_FLAG_ALL) == 0) { switch (ni6->ni_code) { @@ -1652,6 +1658,7 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, } } + IFNET_RENTER(s); IFNET_FOREACH(ifp) { addrsofif = 0; IFADDR_FOREACH(ifa, ifp) { @@ -1704,11 +1711,14 @@ ni6_addrs(struct icmp6_nodeinfo *ni6, struct mbuf *m, } if (iffound) { *ifpp = ifp; + ifhold(ifp); + IFNET_REXIT(s); return (addrsofif); } addrs += addrsofif; } + IFNET_REXIT(s); return (addrs); } diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index f45928d..52938a20 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -2141,7 +2141,9 @@ in6_setmaxmtu(void) { unsigned long maxmtu = 0; struct ifnet *ifp; + int s; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { /* this function can be called during ifnet initialization */ if (!ifp->if_afdata[AF_INET6]) @@ -2150,6 +2152,7 @@ in6_setmaxmtu(void) IN6_LINKMTU(ifp) > maxmtu) maxmtu = IN6_LINKMTU(ifp); } + IFNET_REXIT(s); 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 cca2481..1f14d17 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -456,6 +456,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, struct in6_addr *in6) { struct ifnet *ifp; + int s; /* first, try to get it from the interface itself */ if (in6_get_hw_ifid(ifp0, in6) == 0) { @@ -472,6 +473,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, } /* next, try to get it from some other hardware interface */ + IFNET_RENTER(s); IFNET_FOREACH(ifp) { if (ifp == ifp0) continue; @@ -489,6 +491,7 @@ get_ifid(struct ifnet *ifp0, struct ifnet *altifp, goto success; } } + IFNET_REXIT(s); #if 0 /* get from hostid - only for certain architectures */ @@ -957,6 +960,7 @@ in6_tmpaddrtimer(void *ignored_arg) struct nd_ifinfo *ndi; u_int8_t nullbuf[8]; struct ifnet *ifp; + int s; mutex_enter(softnet_lock); KERNEL_LOCK(1, NULL); @@ -966,6 +970,7 @@ in6_tmpaddrtimer(void *ignored_arg) ip6_temp_regen_advance) * hz, in6_tmpaddrtimer, NULL); memset(nullbuf, 0, sizeof(nullbuf)); + IFNET_RENTER(s); IFNET_FOREACH(ifp) { ndi = ND_IFINFO(ifp); if (memcmp(ndi->randomid, nullbuf, sizeof(nullbuf)) != 0) { @@ -977,6 +982,7 @@ in6_tmpaddrtimer(void *ignored_arg) ndi->randomseed1, ndi->randomid); } } + IFNET_REXIT(s); KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 7a01a9d..28133b7 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -2157,11 +2157,13 @@ nd6_slowtimo(void *ignored_arg) { struct nd_ifinfo *nd6if; struct ifnet *ifp; + int s; mutex_enter(softnet_lock); KERNEL_LOCK(1, NULL); callout_reset(&nd6_slowtimo_ch, ND6_SLOWTIMER_INTERVAL * hz, nd6_slowtimo, NULL); + IFNET_RENTER(s); IFNET_FOREACH(ifp) { nd6if = ND_IFINFO(ifp); if (nd6if->basereachable && /* already initialized */ @@ -2176,6 +2178,7 @@ nd6_slowtimo(void *ignored_arg) nd6if->reachable = ND_COMPUTE_RTIME(nd6if->basereachable); } } + IFNET_REXIT(s); KERNEL_UNLOCK_ONE(NULL); mutex_exit(softnet_lock); } diff --git a/sys/netipsec/xform_ipip.c b/sys/netipsec/xform_ipip.c index c125f47..64cb23b 100644 --- a/sys/netipsec/xform_ipip.c +++ b/sys/netipsec/xform_ipip.c @@ -338,6 +338,8 @@ _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) { + int s; + IFNET_RENTER(s); IFNET_FOREACH(ifp) { IFADDR_FOREACH(ifa, ifp) { #ifdef INET @@ -350,6 +352,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) if (sin->sin_addr.s_addr == ipo->ip_src.s_addr) { + IFNET_REXIT(s); IPIP_STATINC(IPIP_STAT_SPOOF); m_freem(m); return; @@ -366,6 +369,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_REXIT(s); IPIP_STATINC(IPIP_STAT_SPOOF); m_freem(m); return; @@ -375,6 +379,7 @@ _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp) #endif /* INET6 */ } } + IFNET_REXIT(s); } /* Statistics */ diff --git a/sys/netnatm/natm.c b/sys/netnatm/natm.c index da12c5c..3eb603e 100644 --- a/sys/netnatm/natm.c +++ b/sys/netnatm/natm.c @@ -151,11 +151,15 @@ natm_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) * convert interface string to ifp, validate. */ - ifp = ifunit(snatm->snatm_if); - if (ifp == NULL || (ifp->if_flags & IFF_RUNNING) == 0) { + ifp = ifget(snatm->snatm_if); + if (ifp == NULL) + return ENXIO; + if (ifp->if_flags & IFF_RUNNING == 0) { + ifput(ifp); return ENXIO; } if (ifp->if_output != atm_output) { + ifput(ifp); return EAFNOSUPPORT; } @@ -163,8 +167,10 @@ natm_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) * register us with the NATM PCB layer */ - if (npcb_add(npcb, ifp, snatm->snatm_vci, snatm->snatm_vpi) != npcb) + if (npcb_add(npcb, ifp, snatm->snatm_vci, snatm->snatm_vpi) != npcb) { + ifput(ifp); return EADDRINUSE; + } /* * enable rx @@ -181,6 +187,7 @@ natm_connect(struct socket *so, struct sockaddr *nam, struct lwp *l) return EIO; } splx(s2); + ifput(ifp); soisconnected(so); return error;