diff --git a/sys/compat/linux/common/linux_socket.c b/sys/compat/linux/common/linux_socket.c index 75e41eb..90fdb57 100644 --- a/sys/compat/linux/common/linux_socket.c +++ b/sys/compat/linux/common/linux_socket.c @@ -1092,16 +1092,21 @@ linux_getifname(struct lwp *l, register_t *retval, void *data) struct ifnet *ifp; struct linux_ifreq ifr; int error; + int s; error = copyin(data, &ifr, sizeof(ifr)); if (error) return error; + s = pserialize_read_enter(); ifp = if_byindex(ifr.ifr_ifru.ifru_ifindex); - if (ifp == NULL) + if (ifp == NULL) { + pserialize_read_exit(s); return ENODEV; + } strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); + pserialize_read_exit(s); return copyout(&ifr, data, sizeof(ifr)); } diff --git a/sys/compat/linux32/common/linux32_socket.c b/sys/compat/linux32/common/linux32_socket.c index 237a12a..ad678c1 100644 --- a/sys/compat/linux32/common/linux32_socket.c +++ b/sys/compat/linux32/common/linux32_socket.c @@ -393,16 +393,21 @@ linux32_getifname(struct lwp *l, register_t *retval, void *data) struct ifnet *ifp; struct linux32_ifreq ifr; int error; + int s; error = copyin(data, &ifr, sizeof(ifr)); if (error) return error; + s = pserialize_read_enter(); ifp = if_byindex(ifr.ifr_ifru.ifru_ifindex); - if (ifp == NULL) + if (ifp == NULL) { + pserialize_read_exit(s); return ENODEV; + } strncpy(ifr.ifr_name, ifp->if_xname, sizeof(ifr.ifr_name)); + pserialize_read_exit(s); return copyout(&ifr, data, sizeof(ifr)); } diff --git a/sys/net/if.c b/sys/net/if.c index a11be3e..adeb724 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -2194,6 +2194,26 @@ if_byindex(u_int idx) return (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; } +/* + * Get a reference of an ifnet object by an interface index. + * The returned reference is protected by psref(9). The caller + * must release a returned reference by if_put after use. + */ +ifnet_t * +if_get_byindex(u_int idx, struct psref *psref) +{ + ifnet_t *ifp; + int s; + + s = pserialize_read_enter(); + ifp = (idx < if_indexlim) ? ifindex2ifnet[idx] : NULL; + if (ifp != NULL) + psref_acquire(psref, &ifp->if_psref, ifnet_psref_class); + pserialize_read_exit(s); + + return ifp; +} + /* common */ int ifioctl_common(struct ifnet *ifp, u_long cmd, void *data) @@ -3047,28 +3067,33 @@ if_sdl_sysctl(SYSCTLFN_ARGS) { struct ifnet *ifp; const struct sockaddr_dl *sdl; + struct psref psref; + int error = 0; if (namelen != 1) return EINVAL; - ifp = if_byindex(name[0]); + ifp = if_get_byindex(name[0], &psref); 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); + error = sysctl_copyout(l, &sdl->sdl_data[sdl->sdl_nlen], oldp, *oldlenp); +out: + if_put(ifp, &psref); + return error; } SYSCTL_SETUP(sysctl_net_sdl_setup, "sysctl net.sdl subtree setup") diff --git a/sys/net/if.h b/sys/net/if.h index 407edcc..020318f 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -892,6 +892,8 @@ int ifioctl_common(struct ifnet *, u_long, void *); int ifpromisc(struct ifnet *, int); struct ifnet *ifunit(const char *); struct ifnet *if_get(const char *, struct psref *); +ifnet_t *if_byindex(u_int); +ifnet_t *if_get_byindex(u_int, struct psref *); void if_put(const struct ifnet *, struct psref *); int if_addr_init(ifnet_t *, struct ifaddr *, bool); int if_do_dad(struct ifnet *); @@ -1007,8 +1009,6 @@ extern kmutex_t ifnet_mtx; extern struct ifnet *lo0ifp; -ifnet_t * if_byindex(u_int); - /* * ifq sysctl support */ diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 95a248f..39cd6ee 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1475,24 +1475,31 @@ ether_multicast_sysctl(SYSCTLFN_ARGS) struct ether_multi_sysctl addr; struct ifnet *ifp; struct ethercom *ec; - int error; + int error = 0; size_t written; + struct psref psref; + int bound = curlwp->l_pflag & LP_BOUND; if (namelen != 1) return EINVAL; - ifp = if_byindex(name[0]); - if (ifp == NULL) - return ENODEV; + curlwp->l_pflag |= LP_BOUND; + ifp = if_get_byindex(name[0], &psref); + if (ifp == NULL) { + error = ENODEV; + goto out; + } if (ifp->if_type != IFT_ETHER) { + if_put(ifp, &psref); *oldlenp = 0; - return 0; + goto out; } ec = (struct ethercom *)ifp; if (oldp == NULL) { + if_put(ifp, &psref); *oldlenp = ec->ec_multicnt * sizeof(addr); - return 0; + goto out; } memset(&addr, 0, sizeof(addr)); @@ -1511,8 +1518,11 @@ ether_multicast_sysctl(SYSCTLFN_ARGS) written += sizeof(addr); oldp = (char *)oldp + sizeof(addr); } + if_put(ifp, &psref); *oldlenp = written; +out: + curlwp->l_pflag ^= bound ^ LP_BOUND; return error; } diff --git a/sys/net/if_llatbl.c b/sys/net/if_llatbl.c index a3d5769e..ed9b30f 100644 --- a/sys/net/if_llatbl.c +++ b/sys/net/if_llatbl.c @@ -592,14 +592,18 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir struct llentry *lle; u_int laflags; int error; + struct psref psref; + int bound = curlwp->l_pflag & LP_BOUND; KASSERTMSG(dl != NULL && dl->sdl_family == AF_LINK, "invalid dl"); + curlwp->l_pflag |= LP_BOUND; if (sdl_index != 0) - ifp = if_byindex(sdl_index); + ifp = if_get_byindex(sdl_index, &psref); else - ifp = if_byindex(dl->sdl_index); + ifp = if_get_byindex(dl->sdl_index, &psref); if (ifp == NULL) { + curlwp->l_pflag ^= bound ^ LP_BOUND; log(LOG_INFO, "%s: invalid ifp (sdl_index %d)\n", __func__, sdl_index != 0 ? sdl_index : dl->sdl_index); return EINVAL; @@ -628,7 +632,8 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir (lle->la_flags & LLE_STATIC || lle->la_expire == 0)) { LLE_RUNLOCK(lle); IF_AFDATA_WUNLOCK(ifp); - return EEXIST; + error = EEXIST; + goto out; } if (lle != NULL) LLE_RUNLOCK(lle); @@ -636,7 +641,8 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir lle = lla_create(llt, 0, dst); if (lle == NULL) { IF_AFDATA_WUNLOCK(ifp); - return (ENOMEM); + error = ENOMEM; + goto out; } KASSERT(ifp->if_addrlen <= sizeof(lle->ll_addr)); @@ -680,12 +686,16 @@ lla_rt_output(const u_char rtm_type, const int rtm_flags, const time_t rtm_expir IF_AFDATA_WLOCK(ifp); error = lla_delete(llt, 0, dst); IF_AFDATA_WUNLOCK(ifp); - return (error == 0 ? 0 : ENOENT); + error = (error == 0 ? 0 : ENOENT); + break; default: error = EINVAL; } +out: + if_put(ifp, &psref); + curlwp->l_pflag ^= bound ^ LP_BOUND; return (error); } diff --git a/sys/net/npf/npf_ext_log.c b/sys/net/npf/npf_ext_log.c index 56069ab..1f9a794 100644 --- a/sys/net/npf/npf_ext_log.c +++ b/sys/net/npf/npf_ext_log.c @@ -85,6 +85,7 @@ npf_log(npf_cache_t *npc, void *meta, int *decision) const npf_ext_log_t *log = meta; ifnet_t *ifp; int family; + struct psref psref; /* Set the address family. */ if (npf_iscached(npc, NPC_IP4)) { @@ -98,7 +99,7 @@ npf_log(npf_cache_t *npc, void *meta, int *decision) KERNEL_LOCK(1, NULL); /* Find a pseudo-interface to log. */ - ifp = if_byindex(log->if_idx); + ifp = if_get_byindex(log->if_idx, &psref); if (ifp == NULL) { /* No interface. */ KERNEL_UNLOCK_ONE(NULL); @@ -109,6 +110,7 @@ npf_log(npf_cache_t *npc, void *meta, int *decision) ifp->if_opackets++; ifp->if_obytes += m->m_pkthdr.len; bpf_mtap_af(ifp, family, m); + if_put(ifp, &psref); KERNEL_UNLOCK_ONE(NULL); return true; diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c index 8a2764d..b180f59 100644 --- a/sys/netinet6/mld6.c +++ b/sys/netinet6/mld6.c @@ -979,13 +979,18 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) uint32_t tmp; int error; size_t written; + struct psref psref; + int bound = curlwp->l_pflag & LP_BOUND; if (namelen != 1) return EINVAL; - ifp = if_byindex(name[0]); - if (ifp == NULL) + curlwp->l_pflag |= LP_BOUND; + ifp = if_get_byindex(name[0], &psref); + if (ifp == NULL) { + curlwp->l_pflag ^= bound ^ LP_BOUND; return ENODEV; + } if (oldp == NULL) { *oldlenp = 0; @@ -1000,6 +1005,8 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) sizeof(uint32_t); } } + if_put(ifp, &psref); + curlwp->l_pflag ^= bound ^ LP_BOUND; return 0; } @@ -1036,7 +1043,9 @@ in6_multicast_sysctl(SYSCTLFN_ARGS) } } done: + if_put(ifp, &psref); *oldlenp = written; + curlwp->l_pflag ^= bound ^ LP_BOUND; return error; } diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index b6795ba..b3529a4 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -2105,8 +2105,12 @@ nd6_setdefaultiface(int ifindex) { ifnet_t *ifp; int error = 0; + int s; - if ((ifp = if_byindex(ifindex)) == NULL) { + s = pserialize_read_enter(); + ifp = if_byindex(ifindex); + if (ifp == NULL) { + pserialize_read_exit(s); return EINVAL; } if (nd6_defifindex != ifindex) { @@ -2120,6 +2124,7 @@ nd6_setdefaultiface(int ifindex) */ scope6_setdefault(nd6_defifp); } + pserialize_read_exit(s); return (error); } diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c index 2485085..df5e5ca 100644 --- a/sys/netinet6/scope6.c +++ b/sys/netinet6/scope6.c @@ -120,6 +120,7 @@ scope6_set(struct ifnet *ifp, const struct scope6_id *idlist) for (i = 0; i < 16; i++) { if (idlist->s6id_list[i] && idlist->s6id_list[i] != sid->s6id_list[i]) { + int s; /* * An interface zone ID must be the corresponding * interface index by definition. @@ -128,6 +129,7 @@ scope6_set(struct ifnet *ifp, const struct scope6_id *idlist) idlist->s6id_list[i] != ifp->if_index) return (EINVAL); + s = pserialize_read_enter(); if (i == IPV6_ADDR_SCOPE_LINKLOCAL && !if_byindex(idlist->s6id_list[i])) { /* @@ -136,8 +138,10 @@ scope6_set(struct ifnet *ifp, const struct scope6_id *idlist) * IDs, but we check the consistency for * safety in later use. */ + pserialize_read_exit(s); return (EINVAL); } + pserialize_read_exit(s); /* * XXX: we must need lots of work in this case, @@ -295,15 +299,20 @@ sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok) if (zoneid != 0 && (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) || IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) { + int s; /* * At this moment, we only check interface-local and * link-local scope IDs, and use interface indices as the * zone IDs assuming a one-to-one mapping between interfaces * and links. */ + s = pserialize_read_enter(); ifp = if_byindex(zoneid); - if (ifp == NULL) + if (ifp == NULL) { + pserialize_read_exit(s); return (ENXIO); + } + pserialize_read_exit(s); /* XXX assignment to 16bit from 32bit variable */ sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff); @@ -349,8 +358,12 @@ sa6_recoverscope(struct sockaddr_in6 *sin6) */ zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]); if (zoneid) { - if (!if_byindex(zoneid)) + int s = pserialize_read_enter(); + if (!if_byindex(zoneid)) { + pserialize_read_exit(s); return (ENXIO); + } + pserialize_read_exit(s); sin6->sin6_addr.s6_addr16[1] = 0; sin6->sin6_scope_id = zoneid; }