tun(4)/tap(4): allow devices to be configured as transient
Transient tunnel devices are removed immediately after last close, so that an application that's created the tunnel could eliminate the need to manually destroy the tunnel whose lifetime it's already managing. Reviewed by: zlei Differential Revision: https://reviews.freebsd.org/D44200
This commit is contained in:
@@ -203,6 +203,21 @@ The argument should be a pointer to a
|
||||
The interface name will be returned in the
|
||||
.Va ifr_name
|
||||
field.
|
||||
.It Dv TAPSTRANSIENT
|
||||
The argument should be a pointer to an
|
||||
.Va int ;
|
||||
this sets the transient flag on
|
||||
the
|
||||
.Nm
|
||||
device.
|
||||
A transient
|
||||
.Nm
|
||||
will be destroyed upon last close.
|
||||
.It Dv TAPGTRANSIENT
|
||||
The argument should be a pointer to an
|
||||
.Va int ;
|
||||
this stores the current state (enabled or disabled) of the transient flag into
|
||||
it.
|
||||
.It Dv FIONBIO
|
||||
Turn non-blocking I/O for reads off or on, according as the argument
|
||||
.Va int Ns 's
|
||||
|
||||
@@ -282,6 +282,21 @@ The argument should be a pointer to an
|
||||
the ioctl sets the value to one if the device is in
|
||||
.Dq multi-af
|
||||
mode, and zero otherwise.
|
||||
.It Dv TUNSTRANSIENT
|
||||
The argument should be a pointer to an
|
||||
.Va int ;
|
||||
this sets the transient flag on
|
||||
the
|
||||
.Nm
|
||||
device.
|
||||
A transient
|
||||
.Nm
|
||||
will be destroyed upon last close.
|
||||
.It Dv TUNGTRANSIENT
|
||||
The argument should be a pointer to an
|
||||
.Va int ;
|
||||
this stores the current state (enabled or disabled) of the transient flag into
|
||||
it.
|
||||
.It Dv FIONBIO
|
||||
Turn non-blocking I/O for reads off or on, according as the argument
|
||||
.Vt int Ns 's
|
||||
|
||||
+1
-1
@@ -153,7 +153,7 @@ int if_clone_destroy(const char *);
|
||||
int if_clone_list(struct if_clonereq *);
|
||||
void if_clone_restoregroup(struct ifnet *);
|
||||
|
||||
/* The below interfaces are used only by epair(4). */
|
||||
/* The below interfaces are used only by epair(4) and tun(4)/tap(4). */
|
||||
void if_clone_addif(struct if_clone *, struct ifnet *);
|
||||
int if_clone_destroyif(struct if_clone *, struct ifnet *);
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
#define TAPGIFNAME TUNGIFNAME
|
||||
#define TAPSVNETHDR _IOW('t', 91, int)
|
||||
#define TAPGVNETHDR _IOR('t', 94, int)
|
||||
#define TAPSTRANSIENT TUNSTRANSIENT
|
||||
#define TAPGTRANSIENT TUNGTRANSIENT
|
||||
|
||||
/* VMware ioctl's */
|
||||
#define VMIO_SIOCSIFFLAGS _IOWINT('V', 0)
|
||||
|
||||
@@ -43,5 +43,7 @@ struct tuninfo {
|
||||
#define TUNSIFPID _IO('t', 95)
|
||||
#define TUNSIFHEAD _IOW('t', 96, int)
|
||||
#define TUNGIFHEAD _IOR('t', 97, int)
|
||||
#define TUNSTRANSIENT _IOW('t', 98, int)
|
||||
#define TUNGTRANSIENT _IOR('t', 99, int)
|
||||
|
||||
#endif /* !_NET_IF_TUN_H_ */
|
||||
|
||||
+49
-1
@@ -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
|
||||
@@ -621,7 +634,12 @@ tun_destroy(struct tuntap_softc *tp, bool may_intr)
|
||||
int error;
|
||||
|
||||
TUN_LOCK(tp);
|
||||
MPASS((tp->tun_flags & TUN_DYING) == 0);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
error = 0;
|
||||
while (tp->tun_busy != 0) {
|
||||
@@ -1229,6 +1247,23 @@ tundtor(void *data)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1680,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:
|
||||
|
||||
@@ -40,6 +40,7 @@ ${PACKAGE}FILESMODE_stp.py= 0555
|
||||
|
||||
MAN=
|
||||
PROGS+= randsleep
|
||||
PROGS+= transient_tuntap
|
||||
|
||||
CFLAGS+= -I${.CURDIR:H:H}
|
||||
|
||||
|
||||
@@ -56,8 +56,30 @@ basic_cleanup()
|
||||
vnet_cleanup
|
||||
}
|
||||
|
||||
atf_test_case "transient" "cleanup"
|
||||
transient_head()
|
||||
{
|
||||
atf_set descr "Test transient tunnel support"
|
||||
atf_set require.user root
|
||||
}
|
||||
transient_body()
|
||||
{
|
||||
vnet_init
|
||||
vnet_mkjail one
|
||||
|
||||
tun=$(jexec one ifconfig tun create)
|
||||
atf_check -s exit:0 -o not-empty jexec one ifconfig ${tun}
|
||||
jexec one $(atf_get_srcdir)/transient_tuntap /dev/${tun}
|
||||
atf_check -s not-exit:0 -e not-empty jexec one ifconfig ${tun}
|
||||
}
|
||||
transient_cleanup()
|
||||
{
|
||||
vnet_cleanup
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case "235704"
|
||||
atf_add_test_case "basic"
|
||||
atf_add_test_case "transient"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*-
|
||||
* Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* This test simply configures the tunnel as transient and exits. By the time
|
||||
* we return, the tunnel should be gone because the last reference disappears.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if_tun.h>
|
||||
#include <net/if_tap.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
unsigned long tunreq;
|
||||
const char *tundev;
|
||||
int one = 1, tunfd;
|
||||
|
||||
assert(argc > 1);
|
||||
tundev = argv[1];
|
||||
|
||||
tunfd = open(tundev, O_RDWR);
|
||||
assert(tunfd >= 0);
|
||||
|
||||
/*
|
||||
* These are technically the same request, but we'll use the technically
|
||||
* correct one just in case.
|
||||
*/
|
||||
if (strstr(tundev, "tun") != NULL) {
|
||||
tunreq = TUNSTRANSIENT;
|
||||
} else {
|
||||
assert(strstr(tundev, "tap") != NULL);
|
||||
tunreq = TAPSTRANSIENT;
|
||||
}
|
||||
|
||||
if (ioctl(tunfd, tunreq, &one) == -1)
|
||||
err(1, "ioctl");
|
||||
|
||||
/* Final close should destroy the tunnel automagically. */
|
||||
close(tunfd);
|
||||
|
||||
return (0);
|
||||
}
|
||||
Reference in New Issue
Block a user