git: d74b7baeb0d4 - main - ifnet_byindex() actually requires network epoch
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 06 Dec 2021 17:32:49 UTC
The branch main has been updated by glebius:
URL: https://cgit.FreeBSD.org/src/commit/?id=d74b7baeb0d419fce46994075b6ccf944a0fae9a
commit d74b7baeb0d419fce46994075b6ccf944a0fae9a
Author: Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2021-12-04 17:49:36 +0000
Commit: Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2021-12-06 17:32:31 +0000
ifnet_byindex() actually requires network epoch
Sweep over potentially unsafe calls to ifnet_byindex() and wrap them
in epoch. Most of the code touched remains unsafe, as the returned
pointer is being used after epoch exit. Mark that with a comment.
Validate the index argument inside the function, reducing argument
validation requirement from the callers and making V_if_index
private to if.c.
Reviewed by: melifaro
Differential revision: https://reviews.freebsd.org/D33263
---
sys/dev/hyperv/netvsc/if_hn.c | 6 ++++
sys/net/if.c | 16 ++++------
sys/net/if_var.h | 10 +++---
sys/netinet/igmp.c | 8 ++---
sys/netinet/in_mcast.c | 49 +++++++++++++---------------
sys/netinet/udp_usrreq.c | 11 ++++---
sys/netinet6/in6_mcast.c | 74 +++++++++++++++++++++++++++----------------
sys/netinet6/ip6_mroute.c | 9 ++++--
sys/netinet6/ip6_output.c | 6 ++--
sys/netinet6/mld6.c | 10 ++----
sys/netinet6/nd6_rtr.c | 21 ++++++------
sys/netinet6/scope6.c | 47 +++++++++++++++++----------
12 files changed, 147 insertions(+), 120 deletions(-)
diff --git a/sys/dev/hyperv/netvsc/if_hn.c b/sys/dev/hyperv/netvsc/if_hn.c
index de464662c2ef..025baaa60152 100644
--- a/sys/dev/hyperv/netvsc/if_hn.c
+++ b/sys/dev/hyperv/netvsc/if_hn.c
@@ -4736,11 +4736,13 @@ hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
first = true;
for (i = 0; i < hn_vfmap_size; ++i) {
+ struct epoch_tracker et;
struct ifnet *ifp;
if (hn_vfmap[i] == NULL)
continue;
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(i);
if (ifp != NULL) {
if (first)
@@ -4749,6 +4751,7 @@ hn_vflist_sysctl(SYSCTL_HANDLER_ARGS)
sbuf_printf(sb, " %s", ifp->if_xname);
first = false;
}
+ NET_EPOCH_EXIT(et);
}
rm_runlock(&hn_vfmap_lock, &pt);
@@ -4778,12 +4781,14 @@ hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
first = true;
for (i = 0; i < hn_vfmap_size; ++i) {
+ struct epoch_tracker et;
struct ifnet *ifp, *hn_ifp;
hn_ifp = hn_vfmap[i];
if (hn_ifp == NULL)
continue;
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(i);
if (ifp != NULL) {
if (first) {
@@ -4795,6 +4800,7 @@ hn_vfmap_sysctl(SYSCTL_HANDLER_ARGS)
}
first = false;
}
+ NET_EPOCH_EXIT(et);
}
rm_runlock(&hn_vfmap_lock, &pt);
diff --git a/sys/net/if.c b/sys/net/if.c
index ad6d0bcf827a..87c3e4af4380 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -343,9 +343,11 @@ MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
struct ifnet *
-ifnet_byindex(u_short idx)
+ifnet_byindex(u_int idx)
{
+ NET_EPOCH_ASSERT();
+
if (__predict_false(idx > V_if_index))
return (NULL);
@@ -353,12 +355,10 @@ ifnet_byindex(u_short idx)
}
struct ifnet *
-ifnet_byindex_ref(u_short idx)
+ifnet_byindex_ref(u_int idx)
{
struct ifnet *ifp;
- NET_EPOCH_ASSERT();
-
ifp = ifnet_byindex(idx);
if (ifp == NULL || (ifp->if_flags & IFF_DYING))
return (NULL);
@@ -679,9 +679,7 @@ if_free(struct ifnet *ifp)
*/
CURVNET_SET_QUIET(ifp->if_vnet);
IFNET_WLOCK();
- KASSERT(ifp == ifnet_byindex(ifp->if_index),
- ("%s: freeing unallocated ifnet", ifp->if_xname));
-
+ MPASS(V_ifindex_table[ifp->if_index] == ifp);
ifindex_free(ifp->if_index);
IFNET_WUNLOCK();
@@ -831,9 +829,7 @@ if_attach_internal(struct ifnet *ifp, int vmove, struct if_clone *ifc)
struct sockaddr_dl *sdl;
struct ifaddr *ifa;
- if (ifp->if_index == 0 || ifp != ifnet_byindex(ifp->if_index))
- panic ("%s: BUG: if_attach called without if_alloc'd input()\n",
- ifp->if_xname);
+ MPASS(V_ifindex_table[ifp->if_index] == ifp);
#ifdef VIMAGE
ifp->if_vnet = curvnet;
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index a0739fd6b76b..1647eb351db3 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -613,12 +613,12 @@ extern struct sx ifnet_sxlock;
#define IFNET_RUNLOCK() sx_sunlock(&ifnet_sxlock)
/*
- * Look up an ifnet given its index; the _ref variant also acquires a
- * reference that must be freed using if_rele(). It is almost always a bug
- * to call ifnet_byindex() instead of ifnet_byindex_ref().
+ * Look up an ifnet given its index. The returned value protected from
+ * being freed by the network epoch. The _ref variant also acquires a
+ * reference that must be freed using if_rele().
*/
-struct ifnet *ifnet_byindex(u_short idx);
-struct ifnet *ifnet_byindex_ref(u_short idx);
+struct ifnet *ifnet_byindex(u_int);
+struct ifnet *ifnet_byindex_ref(u_int);
/*
* Given the index, ifaddr_byindex() returns the one and only
diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
index e7636330d267..58d66ebafe64 100644
--- a/sys/netinet/igmp.c
+++ b/sys/netinet/igmp.c
@@ -482,6 +482,7 @@ out_locked:
static int
sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS)
{
+ struct epoch_tracker et;
int *name;
int error;
u_int namelen;
@@ -504,14 +505,11 @@ sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS)
IN_MULTI_LIST_LOCK();
IGMP_LOCK();
- if (name[0] <= 0 || name[0] > V_if_index) {
- error = ENOENT;
- goto out_locked;
- }
-
error = ENOENT;
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(name[0]);
+ NET_EPOCH_EXIT(et);
if (ifp == NULL)
goto out_locked;
diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c
index 6ac81aa98e44..3f25471f0858 100644
--- a/sys/netinet/in_mcast.c
+++ b/sys/netinet/in_mcast.c
@@ -1376,6 +1376,7 @@ in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
static int
inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct group_source_req gsr;
sockunion_t *gsa, *ssa;
struct ifnet *ifp;
@@ -1414,8 +1415,6 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
ssa->sin.sin_addr = mreqs.imr_sourceaddr;
if (!in_nullhost(mreqs.imr_interface)) {
- struct epoch_tracker et;
-
NET_EPOCH_ENTER(et);
INADDR_TO_IFP(mreqs.imr_interface, ifp);
/* XXXGL: ifref? */
@@ -1445,10 +1444,11 @@ inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
ssa->sin.sin_len != sizeof(struct sockaddr_in))
return (EINVAL);
- if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
- return (EADDRNOTAVAIL);
-
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(gsr.gsr_interface);
+ NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
doblock = 1;
@@ -1624,6 +1624,7 @@ inp_freemoptions(struct ip_moptions *imo)
static int
inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct __msfilterreq msfr;
sockunion_t *gsa;
struct ifnet *ifp;
@@ -1649,10 +1650,9 @@ inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
if (error)
return (error);
- if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
- return (EINVAL);
-
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(msfr.msfr_ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifnet pointer left */
if (ifp == NULL)
return (EINVAL);
@@ -2026,11 +2026,11 @@ inp_join_group(struct inpcb *inp, struct sockopt *sopt)
if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
return (EINVAL);
- if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
- return (EADDRNOTAVAIL);
NET_EPOCH_ENTER(et);
ifp = ifnet_byindex_ref(gsr.gsr_interface);
NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
break;
default:
@@ -2243,6 +2243,7 @@ out_inp_unlocked:
static int
inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct group_source_req gsr;
struct ip_mreq_source mreqs;
sockunion_t *gsa, *ssa;
@@ -2304,8 +2305,6 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
* using an IPv4 address as a key is racy.
*/
if (!in_nullhost(mreqs.imr_interface)) {
- struct epoch_tracker et;
-
NET_EPOCH_ENTER(et);
INADDR_TO_IFP(mreqs.imr_interface, ifp);
/* XXXGL ifref? */
@@ -2340,11 +2339,9 @@ inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
return (EINVAL);
}
- if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
- return (EADDRNOTAVAIL);
-
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(gsr.gsr_interface);
-
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (ifp == NULL)
return (EADDRNOTAVAIL);
break;
@@ -2481,13 +2478,17 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
if (error)
return (error);
- if (mreqn.imr_ifindex < 0 || V_if_index < mreqn.imr_ifindex)
+ if (mreqn.imr_ifindex < 0)
return (EINVAL);
if (mreqn.imr_ifindex == 0) {
ifp = NULL;
} else {
+ struct epoch_tracker et;
+
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(mreqn.imr_ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (ifp == NULL)
return (EADDRNOTAVAIL);
}
@@ -2536,6 +2537,7 @@ inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
static int
inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct __msfilterreq msfr;
sockunion_t *gsa;
struct ifnet *ifp;
@@ -2566,10 +2568,9 @@ inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
gsa->sin.sin_port = 0; /* ignore port */
- if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
- return (EADDRNOTAVAIL);
-
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(msfr.msfr_ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (ifp == NULL)
return (EADDRNOTAVAIL);
@@ -2881,13 +2882,6 @@ sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
if (namelen != 2)
return (EINVAL);
- ifindex = name[0];
- if (ifindex <= 0 || ifindex > V_if_index) {
- CTR2(KTR_IGMPV3, "%s: ifindex %u out of range",
- __func__, ifindex);
- return (ENOENT);
- }
-
group.s_addr = name[1];
if (!IN_MULTICAST(ntohl(group.s_addr))) {
CTR2(KTR_IGMPV3, "%s: group 0x%08x is not multicast",
@@ -2895,6 +2889,7 @@ sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
return (EINVAL);
}
+ ifindex = name[0];
NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(ifindex);
if (ifp == NULL) {
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 237287fef1e6..fe5327b3bd3c 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1100,15 +1100,16 @@ udp_v4mapped_pktinfo(struct cmsghdr *cm, struct sockaddr_in * src,
return (EINVAL);
/* Validate the interface index if specified. */
- if (pktinfo->ipi6_ifindex > V_if_index)
- return (ENXIO);
-
- ifp = NULL;
if (pktinfo->ipi6_ifindex) {
+ struct epoch_tracker et;
+
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (ifp == NULL)
return (ENXIO);
- }
+ } else
+ ifp = NULL;
if (ifp != NULL && !IN6_IS_ADDR_UNSPECIFIED(&pktinfo->ipi6_addr)) {
ia.s_addr = pktinfo->ipi6_addr.s6_addr32[3];
if (in_ifhasaddr(ifp, ia) == 0)
diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c
index 7326befc6d01..b1b161ace1b8 100644
--- a/sys/netinet6/in6_mcast.c
+++ b/sys/netinet6/in6_mcast.c
@@ -1402,6 +1402,7 @@ static int
in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
{
struct group_source_req gsr;
+ struct epoch_tracker et;
sockunion_t *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
@@ -1439,10 +1440,16 @@ in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
return (EINVAL);
- if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
- return (EADDRNOTAVAIL);
-
+ /*
+ * XXXGL: this function should use ifnet_byindex_ref, or
+ * expand the epoch section all the way to where we put
+ * the reference.
+ */
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(gsr.gsr_interface);
+ NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
doblock = 1;
@@ -1629,6 +1636,7 @@ ip6_freemoptions(struct ip6_moptions *imo)
static int
in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct __msfilterreq msfr;
sockunion_t *gsa;
struct ifnet *ifp;
@@ -1662,9 +1670,13 @@ in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
return (EINVAL);
- if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
- return (EADDRNOTAVAIL);
+ /*
+ * XXXGL: this function should use ifnet_byindex_ref, or expand the
+ * epoch section all the way to where the interface is referenced.
+ */
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(msfr.msfr_ifindex);
+ NET_EPOCH_EXIT(et);
if (ifp == NULL)
return (EADDRNOTAVAIL);
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
@@ -1858,12 +1870,16 @@ in6p_lookup_mcast_ifp(const struct inpcb *inp, const struct sockaddr_in6 *gsin6)
*
* FIXME: The KAME use of the unspecified address (::)
* to join *all* multicast groups is currently unsupported.
+ *
+ * XXXGL: this function multiple times uses ifnet_byindex() without
+ * proper protection - staying in epoch, or putting reference on ifnet.
*/
static int
in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
{
struct in6_multi_head inmh;
struct group_source_req gsr;
+ struct epoch_tracker et;
sockunion_t *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
@@ -1905,9 +1921,11 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
if (mreq.ipv6mr_interface == 0) {
ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
} else {
- if (V_if_index < mreq.ipv6mr_interface)
- return (EADDRNOTAVAIL);
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(mreq.ipv6mr_interface);
+ NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
}
CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p",
__func__, mreq.ipv6mr_interface, ifp);
@@ -1946,10 +1964,11 @@ in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
ssa->sin6.sin6_port = 0;
ssa->sin6.sin6_scope_id = 0;
}
-
- if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface)
- return (EADDRNOTAVAIL);
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(gsr.gsr_interface);
+ NET_EPOCH_EXIT(et);
+ if (ifp == NULL)
+ return (EADDRNOTAVAIL);
break;
default:
@@ -2168,6 +2187,7 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
{
struct ipv6_mreq mreq;
struct group_source_req gsr;
+ struct epoch_tracker et;
sockunion_t *gsa, *ssa;
struct ifnet *ifp;
struct in6_mfilter *imf;
@@ -2266,9 +2286,9 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
* XXX SCOPE6 lock potentially taken here.
*/
if (ifindex != 0) {
- if (V_if_index < ifindex)
- return (EADDRNOTAVAIL);
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (ifp == NULL)
return (EADDRNOTAVAIL);
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
@@ -2293,7 +2313,9 @@ in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
ip6_sprintf(ip6tbuf, &gsa->sin6.sin6_addr));
ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
} else {
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(ifindex);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
}
if (ifp == NULL)
return (EADDRNOTAVAIL);
@@ -2410,6 +2432,7 @@ out_in6p_locked:
static int
in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
{
+ struct epoch_tracker et;
struct ifnet *ifp;
struct ip6_moptions *imo;
u_int ifindex;
@@ -2421,19 +2444,19 @@ in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
error = sooptcopyin(sopt, &ifindex, sizeof(u_int), sizeof(u_int));
if (error)
return (error);
- if (V_if_index < ifindex)
- return (EINVAL);
+ NET_EPOCH_ENTER(et);
if (ifindex == 0)
ifp = NULL;
else {
ifp = ifnet_byindex(ifindex);
- if (ifp == NULL)
- return (EINVAL);
- if ((ifp->if_flags & IFF_MULTICAST) == 0)
+ if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
+ NET_EPOCH_EXIT(et);
return (EADDRNOTAVAIL);
+ }
}
imo = in6p_findmoptions(inp);
- imo->im6o_multicast_ifp = ifp;
+ imo->im6o_multicast_ifp = ifp; /* XXXGL: reference?! */
+ NET_EPOCH_EXIT(et);
INP_WUNLOCK(inp);
return (0);
@@ -2442,12 +2465,13 @@ in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
/*
* Atomically set source filters on a socket for an IPv6 multicast group.
*
- * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held.
+ * XXXGL: unsafely exits epoch with ifnet pointer
*/
static int
in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
{
struct __msfilterreq msfr;
+ struct epoch_tracker et;
sockunion_t *gsa;
struct ifnet *ifp;
struct in6_mfilter *imf;
@@ -2477,9 +2501,9 @@ in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
gsa->sin6.sin6_port = 0; /* ignore port */
- if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex)
- return (EADDRNOTAVAIL);
+ NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(msfr.msfr_ifindex);
+ NET_EPOCH_EXIT(et);
if (ifp == NULL)
return (EADDRNOTAVAIL);
(void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
@@ -2758,13 +2782,6 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
if (namelen != 5)
return (EINVAL);
- ifindex = name[0];
- if (ifindex <= 0 || ifindex > V_if_index) {
- CTR2(KTR_MLD, "%s: ifindex %u out of range",
- __func__, ifindex);
- return (ENOENT);
- }
-
memcpy(&mcaddr, &name[1], sizeof(struct in6_addr));
if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) {
CTR2(KTR_MLD, "%s: group %s is not multicast",
@@ -2772,6 +2789,7 @@ sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS)
return (EINVAL);
}
+ ifindex = name[0];
NET_EPOCH_ENTER(et);
ifp = ifnet_byindex(ifindex);
if (ifp == NULL) {
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
index 087e0c5059fd..05324dfb94bf 100644
--- a/sys/netinet6/ip6_mroute.c
+++ b/sys/netinet6/ip6_mroute.c
@@ -677,6 +677,7 @@ static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 };
static int
add_m6if(struct mif6ctl *mifcp)
{
+ struct epoch_tracker et;
struct mif6 *mifp;
struct ifnet *ifp;
int error;
@@ -692,12 +693,14 @@ add_m6if(struct mif6ctl *mifcp)
MIF6_UNLOCK();
return (EADDRINUSE); /* XXX: is it appropriate? */
}
- if (mifcp->mif6c_pifi == 0 || mifcp->mif6c_pifi > V_if_index) {
+
+ NET_EPOCH_ENTER(et);
+ if ((ifp = ifnet_byindex(mifcp->mif6c_pifi)) == NULL) {
+ NET_EPOCH_EXIT(et);
MIF6_UNLOCK();
return (ENXIO);
}
-
- ifp = ifnet_byindex(mifcp->mif6c_pifi);
+ NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */
if (mifcp->mif6c_flags & MIFF_REGISTER) {
if (reg_mif_num == (mifi_t)-1) {
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 6091951e2ba2..7d8793b691b4 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -2819,8 +2819,8 @@ ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt,
return (EINVAL);
/*
- * ip6_setpktopt can call ifnet_by_index(), so it's imperative that we are
- * in the net epoch here.
+ * ip6_setpktopt can call ifnet_byindex(), so it's imperative that we
+ * are in the network epoch here.
*/
NET_EPOCH_ASSERT();
@@ -2959,8 +2959,6 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
if (IN6_IS_ADDR_MULTICAST(&pktinfo->ipi6_addr))
return (EINVAL);
/* validate the interface index if specified. */
- if (pktinfo->ipi6_ifindex > V_if_index)
- return (ENXIO);
if (pktinfo->ipi6_ifindex) {
ifp = ifnet_byindex(pktinfo->ipi6_ifindex);
if (ifp == NULL)
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index c4948158bba8..1f79ef39e40e 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -356,13 +356,13 @@ out_locked:
* Expose struct mld_ifsoftc to userland, keyed by ifindex.
* For use by ifmcstat(8).
*
- * SMPng: NOTE: Does an unlocked ifindex space read.
* VIMAGE: Assume curvnet set by caller. The node handler itself
* is not directly virtualized.
*/
static int
sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS)
{
+ struct epoch_tracker et;
int *name;
int error;
u_int namelen;
@@ -385,14 +385,9 @@ sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS)
IN6_MULTI_LOCK();
IN6_MULTI_LIST_LOCK();
MLD_LOCK();
-
- if (name[0] <= 0 || name[0] > V_if_index) {
- error = ENOENT;
- goto out_locked;
- }
+ NET_EPOCH_ENTER(et);
error = ENOENT;
-
ifp = ifnet_byindex(name[0]);
if (ifp == NULL)
goto out_locked;
@@ -415,6 +410,7 @@ sysctl_mld_ifinfo(SYSCTL_HANDLER_ARGS)
}
out_locked:
+ NET_EPOCH_EXIT(et);
MLD_UNLOCK();
IN6_MULTI_LIST_UNLOCK();
IN6_MULTI_UNLOCK();
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index cec9fccd63c4..07a4c2cbe7e5 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -2425,18 +2425,21 @@ rt6_flush(struct in6_addr *gateway, struct ifnet *ifp)
int
nd6_setdefaultiface(int ifindex)
{
- int error = 0;
-
- if (ifindex < 0 || V_if_index < ifindex)
- return (EINVAL);
- if (ifindex != 0 && !ifnet_byindex(ifindex))
- return (EINVAL);
if (V_nd6_defifindex != ifindex) {
V_nd6_defifindex = ifindex;
- if (V_nd6_defifindex > 0)
+ if (V_nd6_defifindex != 0) {
+ struct epoch_tracker et;
+
+ /*
+ * XXXGL: this function should use ifnet_byindex_ref!
+ */
+ NET_EPOCH_ENTER(et);
V_nd6_defifp = ifnet_byindex(V_nd6_defifindex);
- else
+ NET_EPOCH_EXIT(et);
+ if (V_nd6_defifp == NULL)
+ return (EINVAL);
+ } else
V_nd6_defifp = NULL;
/*
@@ -2447,7 +2450,7 @@ nd6_setdefaultiface(int ifindex)
scope6_setdefault(V_nd6_defifp);
}
- return (error);
+ return (0);
}
bool
diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c
index 099f8a78e14d..7957cec44f79 100644
--- a/sys/netinet6/scope6.c
+++ b/sys/netinet6/scope6.c
@@ -177,16 +177,22 @@ scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
return (EINVAL);
}
- if (i == IPV6_ADDR_SCOPE_LINKLOCAL &&
- idlist->s6id_list[i] > V_if_index) {
- /*
- * XXX: theoretically, there should be no
- * relationship between link IDs and interface
- * IDs, but we check the consistency for
- * safety in later use.
- */
- IF_AFDATA_WUNLOCK(ifp);
- return (EINVAL);
+ if (i == IPV6_ADDR_SCOPE_LINKLOCAL) {
+ struct epoch_tracker et;
+
+ NET_EPOCH_ENTER(et);
+ if (!ifnet_byindex(idlist->s6id_list[i])) {
+ /*
+ * XXX: theoretically, there should be
+ * no relationship between link IDs and
+ * interface IDs, but we check the
+ * consistency for safety in later use.
+ */
+ NET_EPOCH_EXIT(et);
+ IF_AFDATA_WUNLOCK(ifp);
+ return (EINVAL);
+ }
+ NET_EPOCH_EXIT(et);
}
/*
@@ -325,14 +331,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))) {
+ struct epoch_tracker et;
+
/*
* 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.
*/
- if (V_if_index < zoneid || ifnet_byindex(zoneid) == NULL)
+ NET_EPOCH_ENTER(et);
+ if (ifnet_byindex(zoneid) == NULL) {
+ NET_EPOCH_EXIT(et);
return (ENXIO);
+ }
+ NET_EPOCH_EXIT(et);
/* XXX assignment to 16bit from 32bit variable */
sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
@@ -358,14 +370,15 @@ sa6_recoverscope(struct sockaddr_in6 *sin6)
*/
zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
if (zoneid) {
+ struct epoch_tracker et;
+
+ NET_EPOCH_ENTER(et);
/* sanity check */
- if (V_if_index < zoneid)
- return (ENXIO);
-#if 0
- /* XXX: Disabled due to possible deadlock. */
- if (!ifnet_byindex(zoneid))
+ if (!ifnet_byindex(zoneid)) {
+ NET_EPOCH_EXIT(et);
return (ENXIO);
-#endif
+ }
+ NET_EPOCH_EXIT(et);
if (sin6->sin6_scope_id != 0 &&
zoneid != sin6->sin6_scope_id) {
log(LOG_NOTICE,