From nobody Thu Aug 21 14:22:48 2025 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4c75Bd5qngz65DrX; Thu, 21 Aug 2025 14:22:49 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R12" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4c75Bc6m7Gz43ll; Thu, 21 Aug 2025 14:22:48 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1755786168; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6mZNm9UkQo8rzhJANdS8UNyf4dFyasn1h8SMiZyoBT4=; b=VQS667EM3nPn51hk08+5L8Eq82lu0TAEdeo7l5ilvBwOs7y0HhVhyek2ToBrd8A8tWz/sO jaY89JSd65KSXN80fEMcWQfpRoLsQZeS9Iy90w4HFGC1HB2BaZ/NKtG1tndllE4/mHn59O NnwAMd7HH3UfIo55AP0Y7WClxIC7cEYdXd2S+q3N10XUldGSW6A7iSzgtB1njeVWUgbHxd gQ30aeqoxsPMgt0mxeshgFkZ56wGUc6Uu5NqaWVxlwHOQYh2s6/3wMhl/Mae2lbnvv5ywR P7H8jLXJEHRABeDQOOH4NgZ5MN8AsBR2kAoISsIJakQePBN6RBAnKSVdfgK4yA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1755786168; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6mZNm9UkQo8rzhJANdS8UNyf4dFyasn1h8SMiZyoBT4=; b=DEbNutvKYmo6rXCPWkNAu4oUdr+Sh9Il7Fr8PkvxToTq7O3OjRNkRSewe1TmTiasTlQxo1 meKFzdl05LGwXKCqlJFMExZkjU8bwRRdRDcLb/Jp2DaEfNJ2v2kOnEXZcSETqWKifmIAZJ q67x4YK7rKM7rlcXAf4u//zVuRSh4oEQS1shF9/SDLQz7VxZZDGDK4WV7NI281BdHoiJ81 rTaqL8TBjctRP3QBLHoJDyMd0mpt+gmFt/unPAzDPTZ1O5GC12SBWj3Uu4ynKTkY5Jw1YY Xbq7KiyD4h8iUL6rsju0MeR2rrkikyI5twxBE54ZPT3mebkOfzjNueNmUvtCyg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1755786168; a=rsa-sha256; cv=none; b=IPiMKA5XQOwKnjylgnZwPTwfJPzguJXZbQdbIO3vWrozmvy64ZG7HT3UkpGmEYbdl1lzt5 aWHZ9WUq7lMXH+JXHHLsdRuuX3pQfo6qc8qYCtKNa/3XM+msQIhrjnpxdvI2q6P6rhVOwb ggzd0xGv+B1efGCcj2QX6jjBQcmlOQmGItOL4qX2/KOXZmUuGNtTRHtr/1R8WiqKCTxyqU PQaoEe+Tw/ppx8ZMnKM+HW0Xz6RNLDtPzAiRmgBuevQEZZPkx8Ekgpx4qkWyA8tUqFKCxA 27+vJ9UKSifu7Cipth99rYsp2eap1QxgtBSyaiDocsGauSxOCOXbOxOfGhd/MQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4c75Bc6MJHz13Mk; Thu, 21 Aug 2025 14:22:48 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 57LEMm3K092962; Thu, 21 Aug 2025 14:22:48 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 57LEMmgp092959; Thu, 21 Aug 2025 14:22:48 GMT (envelope-from git) Date: Thu, 21 Aug 2025 14:22:48 GMT Message-Id: <202508211422.57LEMmgp092959@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: a1174b3b1174 - main - tun(4)/tap(4): allow devices to be configured as transient List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a1174b3b1174754b1f69406bff4456d002e8f583 Auto-Submitted: auto-generated The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=a1174b3b1174754b1f69406bff4456d002e8f583 commit a1174b3b1174754b1f69406bff4456d002e8f583 Author: Kyle Evans AuthorDate: 2025-08-21 14:21:42 +0000 Commit: Kyle Evans CommitDate: 2025-08-21 14:22:44 +0000 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 --- share/man/man4/tap.4 | 15 +++++++++++ share/man/man4/tun.4 | 15 +++++++++++ sys/net/if_clone.h | 2 +- sys/net/if_tap.h | 2 ++ sys/net/if_tun.h | 2 ++ sys/net/if_tuntap.c | 50 ++++++++++++++++++++++++++++++++++++- tests/sys/net/Makefile | 1 + tests/sys/net/if_tun_test.sh | 22 ++++++++++++++++ tests/sys/net/transient_tuntap.c | 54 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 161 insertions(+), 2 deletions(-) diff --git a/share/man/man4/tap.4 b/share/man/man4/tap.4 index 95a681a923d2..a4fe98cdfecf 100644 --- a/share/man/man4/tap.4 +++ b/share/man/man4/tap.4 @@ -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 diff --git a/share/man/man4/tun.4 b/share/man/man4/tun.4 index 58f67cb20acb..1c5bd35f0ab8 100644 --- a/share/man/man4/tun.4 +++ b/share/man/man4/tun.4 @@ -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 diff --git a/sys/net/if_clone.h b/sys/net/if_clone.h index 5a74ffa1cc2f..d780e49af25f 100644 --- a/sys/net/if_clone.h +++ b/sys/net/if_clone.h @@ -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 *); diff --git a/sys/net/if_tap.h b/sys/net/if_tap.h index d84cd2eba6f3..8297b8d9e3d2 100644 --- a/sys/net/if_tap.h +++ b/sys/net/if_tap.h @@ -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) diff --git a/sys/net/if_tun.h b/sys/net/if_tun.h index a8fb61db45a2..ccdc25944823 100644 --- a/sys/net/if_tun.h +++ b/sys/net/if_tun.h @@ -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_ */ diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c index 275581ea2d4e..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 @@ -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 @@ 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); } @@ -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: diff --git a/tests/sys/net/Makefile b/tests/sys/net/Makefile index 65cc99a3e932..e390c6e8059d 100644 --- a/tests/sys/net/Makefile +++ b/tests/sys/net/Makefile @@ -40,6 +40,7 @@ ${PACKAGE}FILESMODE_stp.py= 0555 MAN= PROGS+= randsleep +PROGS+= transient_tuntap CFLAGS+= -I${.CURDIR:H:H} diff --git a/tests/sys/net/if_tun_test.sh b/tests/sys/net/if_tun_test.sh index a4ffe66e04ce..f4ce7800272e 100755 --- a/tests/sys/net/if_tun_test.sh +++ b/tests/sys/net/if_tun_test.sh @@ -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" } diff --git a/tests/sys/net/transient_tuntap.c b/tests/sys/net/transient_tuntap.c new file mode 100644 index 000000000000..b0cf43064317 --- /dev/null +++ b/tests/sys/net/transient_tuntap.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2024 Kyle Evans + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +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); +}