diff --git a/sys/net/if.h b/sys/net/if.h --- a/sys/net/if.h +++ b/sys/net/if.h @@ -163,6 +163,7 @@ #define IFF_DYING 0x200000 /* (n) interface is winding down */ #define IFF_RENAMING 0x400000 /* (n) interface is being renamed */ #define IFF_NOGROUP 0x800000 /* (n) interface is not part of any groups */ +#define IFF_IOCTL_DRAINED 0x1000000 /* (n) control path has been drained */ /* * Old names for driver flags so that user space tools can continue to use @@ -177,7 +178,7 @@ #define IFF_CANTCHANGE \ (IFF_BROADCAST|IFF_POINTOPOINT|IFF_DRV_RUNNING|IFF_DRV_OACTIVE|\ IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI|IFF_PROMISC|\ - IFF_DYING|IFF_CANTCONFIG|IFF_KNOWSEPOCH) + IFF_DYING|IFF_CANTCONFIG|IFF_KNOWSEPOCH|IFF_IOCTL_DRAINED) /* * Values for if_link_state. diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -325,6 +325,9 @@ SX_SYSINIT_FLAGS(ifnet_detach, &ifnet_detach_sxlock, "ifnet_detach_sx", SX_RECURSE); +static struct sx ifnet_ioctl_sxlock; +SX_SYSINIT_FLAGS(ifnet_ioctl, &ifnet_ioctl_sxlock, "ifnet_ioctl_sx", SX_RECURSE); + /* * The allocation of network interfaces is a rather non-atomic affair; we * need to select an index before we are ready to expose the interface for @@ -769,6 +772,39 @@ NET_EPOCH_CALL(if_destroy, &ifp->if_epoch_ctx); } +/* + * Keep track of ifioctl() syscalls from user-space. + * Returns true on success and false on failure. + */ +bool +if_ioctl_ref(struct ifnet *ifp) +{ + bool retval; + + if (ifp->if_flags & IFF_IOCTL_DRAINED) + return (false); + + sx_slock(&ifnet_ioctl_sxlock); + retval = (ifp->if_flags & IFF_IOCTL_DRAINED) == 0; + if (!retval) + sx_sunlock(&ifnet_ioctl_sxlock); + return (retval); +} + +void +if_ioctl_drain(struct ifnet *ifp) +{ + sx_xlock(&ifnet_ioctl_sxlock); + ifp->if_flags |= IFF_IOCTL_DRAINED; + sx_xunlock(&ifnet_ioctl_sxlock); +} + +void +if_ioctl_unref(struct ifnet *ifp) +{ + sx_sunlock(&ifnet_ioctl_sxlock); +} + void ifq_init(struct ifaltq *ifq, struct ifnet *ifp) { @@ -3039,7 +3075,13 @@ goto out_noref; } - error = ifhwioctl(cmd, ifp, data, td); + if (if_ioctl_ref(ifp)) { + error = ifhwioctl(cmd, ifp, data, td); + if_ioctl_unref(ifp); + } else { + error = ENXIO; + } + if (error != ENOIOCTL) goto out_ref; diff --git a/sys/net/if_var.h b/sys/net/if_var.h --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -663,6 +663,9 @@ void if_ref(struct ifnet *); void if_rele(struct ifnet *); bool __result_use_check if_try_ref(struct ifnet *); +bool __result_use_check if_ioctl_ref(struct ifnet *); +void if_ioctl_drain(struct ifnet *); +void if_ioctl_unref(struct ifnet *); int if_setlladdr(struct ifnet *, const u_char *, int); int if_tunnel_check_nesting(struct ifnet *, struct mbuf *, uint32_t, int); void if_up(struct ifnet *);