aboutsummaryrefslogtreecommitdiff
path: root/sys/net/if_tuntap.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net/if_tuntap.c')
-rw-r--r--sys/net/if_tuntap.c88
1 files changed, 74 insertions, 14 deletions
diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c
index 5e6f65c04b2f..c8dbb6aa8893 100644
--- a/sys/net/if_tuntap.c
+++ b/sys/net/if_tuntap.c
@@ -132,6 +132,7 @@ struct tuntap_softc {
#define TUN_DYING 0x0200
#define TUN_L2 0x0400
#define TUN_VMNET 0x0800
+#define TUN_TRANSIENT 0x1000
#define TUN_DRIVER_IDENT_MASK (TUN_L2 | TUN_VMNET)
#define TUN_READY (TUN_OPEN | TUN_INITED)
@@ -443,6 +444,18 @@ tuntap_name2info(const char *name, int *outunit, int *outflags)
return (0);
}
+static struct if_clone *
+tuntap_cloner_from_flags(int tun_flags)
+{
+
+ for (u_int i = 0; i < NDRV; i++)
+ if ((tun_flags & TUN_DRIVER_IDENT_MASK) ==
+ tuntap_drivers[i].ident_flags)
+ return (V_tuntap_driver_cloners[i]);
+
+ return (NULL);
+}
+
/*
* Get driver information from a set of flags specified. Masks the identifying
* part of the flags and compares it against all of the available
@@ -615,19 +628,39 @@ out:
CURVNET_RESTORE();
}
-static void
-tun_destroy(struct tuntap_softc *tp)
+static int
+tun_destroy(struct tuntap_softc *tp, bool may_intr)
{
+ int error;
TUN_LOCK(tp);
+
+ /*
+ * Transient tunnels may have set TUN_DYING if we're being destroyed as
+ * a result of the last close, which we'll allow.
+ */
+ MPASS((tp->tun_flags & (TUN_DYING | TUN_TRANSIENT)) != TUN_DYING);
tp->tun_flags |= TUN_DYING;
- if (tp->tun_busy != 0)
- cv_wait_unlock(&tp->tun_cv, &tp->tun_mtx);
- else
- TUN_UNLOCK(tp);
+ error = 0;
+ while (tp->tun_busy != 0) {
+ if (may_intr)
+ error = cv_wait_sig(&tp->tun_cv, &tp->tun_mtx);
+ else
+ cv_wait(&tp->tun_cv, &tp->tun_mtx);
+ if (error != 0) {
+ tp->tun_flags &= ~TUN_DYING;
+ TUN_UNLOCK(tp);
+ return (error);
+ }
+ }
+ TUN_UNLOCK(tp);
CURVNET_SET(TUN2IFP(tp)->if_vnet);
+ mtx_lock(&tunmtx);
+ TAILQ_REMOVE(&tunhead, tp, tun_list);
+ mtx_unlock(&tunmtx);
+
/* destroy_dev will take care of any alias. */
destroy_dev(tp->tun_dev);
seldrain(&tp->tun_rsel);
@@ -648,6 +681,8 @@ tun_destroy(struct tuntap_softc *tp)
cv_destroy(&tp->tun_cv);
free(tp, M_TUN);
CURVNET_RESTORE();
+
+ return (0);
}
static int
@@ -655,12 +690,7 @@ tun_clone_destroy(struct if_clone *ifc __unused, struct ifnet *ifp, uint32_t fla
{
struct tuntap_softc *tp = ifp->if_softc;
- mtx_lock(&tunmtx);
- TAILQ_REMOVE(&tunhead, tp, tun_list);
- mtx_unlock(&tunmtx);
- tun_destroy(tp);
-
- return (0);
+ return (tun_destroy(tp, true));
}
static void
@@ -702,9 +732,9 @@ tun_uninit(const void *unused __unused)
mtx_lock(&tunmtx);
while ((tp = TAILQ_FIRST(&tunhead)) != NULL) {
- TAILQ_REMOVE(&tunhead, tp, tun_list);
mtx_unlock(&tunmtx);
- tun_destroy(tp);
+ /* tun_destroy() will remove it from the tailq. */
+ tun_destroy(tp, false);
mtx_lock(&tunmtx);
}
mtx_unlock(&tunmtx);
@@ -1217,6 +1247,23 @@ out:
tun_vnethdr_set(ifp, 0);
tun_unbusy_locked(tp);
+ if ((tp->tun_flags & TUN_TRANSIENT) != 0) {
+ struct if_clone *cloner;
+ int error __diagused;
+
+ /* Mark it busy so that nothing can re-open it. */
+ tp->tun_flags |= TUN_DYING;
+ TUN_UNLOCK(tp);
+
+ CURVNET_SET_QUIET(ifp->if_home_vnet);
+ cloner = tuntap_cloner_from_flags(tp->tun_flags);
+ CURVNET_RESTORE();
+
+ error = if_clone_destroyif(cloner, ifp);
+ MPASS(error == 0 || error == EINTR || error == ERESTART);
+ return;
+ }
+
TUN_UNLOCK(tp);
}
@@ -1668,6 +1715,19 @@ tunioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
case TUNGDEBUG:
*(int *)data = tundebug;
break;
+ case TUNSTRANSIENT:
+ TUN_LOCK(tp);
+ if (*(int *)data)
+ tp->tun_flags |= TUN_TRANSIENT;
+ else
+ tp->tun_flags &= ~TUN_TRANSIENT;
+ TUN_UNLOCK(tp);
+ break;
+ case TUNGTRANSIENT:
+ TUN_LOCK(tp);
+ *(int *)data = (tp->tun_flags & TUN_TRANSIENT) != 0;
+ TUN_UNLOCK(tp);
+ break;
case FIONBIO:
break;
case FIOASYNC: