@@ -536,6 +536,8 @@
src/openvpn/tls_crypt.c
src/openvpn/tun.c
src/openvpn/tun.h
+ src/openvpn/tun_afunix.c
+ src/openvpn/tun_afunix.h
src/openvpn/networking_sitnl.c
src/openvpn/networking_freebsd.c
src/openvpn/auth_token.c
@@ -9,6 +9,19 @@
the user experience as the client shows an error instead of running into
a timeout when the server just stops responding completely.
+Support for tun/tap via unix domain socket and lwipovpn support
+ To allow better testing and emulating a full client with a full
+ network stack OpenVPN now allows a program executed to provide
+ a tun/tap device instead of opening a device.
+
+ The co-developed lwipovpn program based on lwIP stack allows to
+ simulate full IP stack and an OpenVPN client using
+ ``dev-node:/path/to/lwipovpn`` can emulate a full client that
+ can be pinged, can server a website and more without requiring any
+ elevated permission. This can make testing OpenVPN much easier.
+
+ For more details see [lwipovpn on Gihtub](https://github.com/OpenVPN/lwipovpn).
+
Deprecated features
-------------------
``secret`` support has been removed by default.
@@ -117,6 +117,16 @@
figure out whether ``node`` is a TUN or TAP device based on the name,
you should also specify ``--dev-type tun`` or ``--dev-type tap``.
+ If ``node`` starts with the string ``unix:`` openvpn will treat the rest
+ of the argument as a program.
+ OpenVPN will create a temporary unix domain socket and start the program
+ will pass the tun configuration as environment variables.
+ The temporary unix will be be passed in the environment variable
+ :code:`TUN_UNIXAF_PATH`.
+
+ This ``unix:`` mode is designed mainly to use with the lwipovpn network
+ emulator (https://github.com/OpenVPN/lwipovpn).
+
--dev-type device-type
Which device type are we using? ``device-type`` should be :code:`tun`
(OSI Layer 3) or :code:`tap` (OSI Layer 2). Use this option only if
@@ -138,6 +138,7 @@
syshead.h \
tls_crypt.c tls_crypt.h \
tun.c tun.h \
+ tun_afunix.c tun_afunix.h \
vlan.c vlan.h \
xkey_provider.c xkey_common.h \
xkey_helper.c \
@@ -41,6 +41,7 @@
#include "ssl_verify.h"
#include "dco.h"
#include "auth_token.h"
+#include "tun_afunix.h"
#include "memdbg.h"
@@ -1319,7 +1320,14 @@
#else /* ifdef _WIN32 */
ASSERT(buf_init(&c->c2.buf, c->c2.frame.buf.headroom));
ASSERT(buf_safe(&c->c2.buf, c->c2.frame.buf.payload_size));
- c->c2.buf.len = read_tun(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size);
+ if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX)
+ {
+ c->c2.buf.len = tun_afunix_read(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size);
+ }
+ else
+ {
+ c->c2.buf.len = read_tun(c->c1.tuntap, BPTR(&c->c2.buf), c->c2.frame.buf.payload_size);
+ }
#endif /* ifdef _WIN32 */
#ifdef PACKET_TRUNCATION_CHECK
@@ -1926,7 +1934,14 @@
#ifdef _WIN32
size = write_tun_buffered(c->c1.tuntap, &c->c2.to_tun);
#else
- size = write_tun(c->c1.tuntap, BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun));
+ if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX)
+ {
+ size = tun_afunix_write(c->c1.tuntap, BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun));
+ }
+ else
+ {
+ size = write_tun(c->c1.tuntap, BPTR(&c->c2.to_tun), BLEN(&c->c2.to_tun));
+ }
#endif
if (size > 0)
@@ -54,6 +54,7 @@
#include "mss.h"
#include "mudp.h"
#include "dco.h"
+#include "tun_afunix.h"
#include "memdbg.h"
@@ -1758,10 +1759,18 @@
&c->net_ctx,
c->c1.tuntap);
+ if (is_tun_afunix(c->options.dev_node))
+ {
+ /* Using AF_UNIX trumps using DCO */
+ c->c1.tuntap->backend_driver = DRIVER_AFUNIX;
+ }
#ifdef _WIN32
- c->c1.tuntap->backend_driver = c->options.windows_driver;
+ else
+ {
+ c->c1.tuntap->backend_driver = c->options.windows_driver;
+ }
#else
- if (dco_enabled(&c->options))
+ else if (dco_enabled(&c->options))
{
c->c1.tuntap->backend_driver = DRIVER_DCO;
}
@@ -1786,6 +1795,10 @@
static bool
can_preserve_tun(struct tuntap *tt)
{
+ if (tt && tt->backend_driver == DRIVER_AFUNIX)
+ {
+ return false;
+ }
#ifdef TARGET_ANDROID
return false;
#else
@@ -1841,6 +1854,22 @@
#endif
}
+static void
+open_tun_backend(struct context *c)
+{
+ struct tuntap *tt = c->c1.tuntap;
+ if (tt->backend_driver == DRIVER_AFUNIX)
+ {
+ open_tun_afunix(&c->options, c->c2.frame.tun_mtu, tt, c->c2.es);
+ }
+ else
+ {
+ open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
+ tt, &c->net_ctx);
+ }
+}
+
+
static bool
do_open_tun(struct context *c, int *error_flags)
{
@@ -1863,7 +1892,8 @@
}
#endif
- /* initialize (but do not open) tun/tap object */
+ /* initialize (but do not open) tun/tap object, this also sets
+ * the backend driver type */
do_init_tun(c);
/* inherit the dco context from the tuntap object */
@@ -1898,7 +1928,7 @@
/* do ifconfig */
if (!c->options.ifconfig_noexec
- && ifconfig_order() == IFCONFIG_BEFORE_TUN_OPEN)
+ && ifconfig_order(c->c1.tuntap) == IFCONFIG_BEFORE_TUN_OPEN)
{
/* guess actual tun/tap unit number that will be returned
* by open_tun */
@@ -1911,7 +1941,7 @@
}
/* possibly add routes */
- if (route_order() == ROUTE_BEFORE_TUN)
+ if (route_order(c->c1.tuntap) == ROUTE_BEFORE_TUN)
{
/* Ignore route_delay, would cause ROUTE_BEFORE_TUN to be ignored */
bool status = do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
@@ -1928,8 +1958,7 @@
}
/* open the tun device */
- open_tun(c->options.dev, c->options.dev_type, c->options.dev_node,
- c->c1.tuntap, &c->net_ctx);
+ open_tun_backend(c);
/* set the hardware address */
if (c->options.lladdr)
@@ -1940,7 +1969,7 @@
/* do ifconfig */
if (!c->options.ifconfig_noexec
- && ifconfig_order() == IFCONFIG_AFTER_TUN_OPEN)
+ && ifconfig_order(c->c1.tuntap) == IFCONFIG_AFTER_TUN_OPEN)
{
do_ifconfig(c->c1.tuntap, c->c1.tuntap->actual_name,
c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
@@ -1966,7 +1995,7 @@
add_wfp_block(c);
/* possibly add routes */
- if ((route_order() == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
+ if ((route_order(c->c1.tuntap) == ROUTE_AFTER_TUN) && (!c->options.route_delay_defined))
{
int status = do_route(&c->options, c->c1.route_list, c->c1.route_ipv6_list,
c->c1.tuntap, c->plugins, c->c2.es, &c->net_ctx);
@@ -2026,6 +2055,10 @@
{
undo_ifconfig(c->c1.tuntap, &c->net_ctx);
}
+ if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX)
+ {
+ tun_afunix_close(c->c1.tuntap);
+ }
close_tun(c->c1.tuntap, &c->net_ctx);
c->c1.tuntap = NULL;
}
@@ -2466,7 +2499,7 @@
c->c1.pulled_options_digest_save = c->c2.pulled_options_digest;
/* if --route-delay was specified, start timer */
- if ((route_order() == ROUTE_AFTER_TUN) && c->options.route_delay_defined)
+ if ((route_order(c->c1.tuntap) == ROUTE_AFTER_TUN) && c->options.route_delay_defined)
{
event_timeout_init(&c->c2.route_wakeup, c->options.route_delay, now);
event_timeout_init(&c->c2.route_wakeup_expire, c->options.route_delay + c->options.route_delay_window, now);
@@ -204,6 +204,11 @@
goto done;
}
}
+ else if (flags & S_NOWAITPID && (stat > 0))
+ {
+ ret = stat;
+ goto done;
+ }
else if (platform_system_ok(stat))
{
ret = true;
@@ -47,6 +47,9 @@
/** Instead of returning 1/0 for success/fail,
* return exit code when between 0 and 255 and -1 otherwise */
#define S_EXITCODE (1<<2)
+/** instead of waiting for child process exist status return the
+ * pid of the child process */
+#define S_NOWAITPID (1<<3)
/* wrapper around the execve() call */
int openvpn_popen(const struct argv *a, const struct env_set *es);
@@ -219,7 +219,7 @@
void tls_multi_free(struct tls_multi *multi, bool clear);
/** @} name Functions for initialization and cleanup of tls_multi structures */
-
+\
/** @} addtogroup control_processor */
#define TLSMP_INACTIVE 0
@@ -75,6 +75,9 @@
case DRIVER_DCO:
return "ovpn-dco";
+ case DRIVER_AFUNIX:
+ return "unix";
+
#ifdef HAVE_NET_IF_UTUN_H
case DRIVER_UTUN:
return "utun";
@@ -50,6 +50,10 @@
#else
DRIVER_GENERIC_TUNTAP,
#endif
+ /** using an AF_UNIX socket to pass packet from/to an external program
+ * this is always defined. We error out if a user tries to open this type
+ * on unsupported platforms*/
+ DRIVER_AFUNIX,
DRIVER_DCO,
#ifdef HAVE_NET_IF_UTUN_H
DRIVER_UTUN
@@ -165,6 +169,15 @@
/*
* Define a TUN/TAP dev.
*/
+#ifdef AF_UNIX
+typedef struct afunix_context
+{
+ pid_t childprocess;
+} afunix_context_t;
+
+#else /* ifdef AF_UNIX */
+typedef struct {} afunix_context_t;
+#endif
struct tuntap
{
@@ -179,7 +192,12 @@
*/
enum tun_driver_type backend_driver;
+ /** if the internal variables related to ifconfig of this struct have
+ * been set up. This does NOT mean ifconfig has been called */
bool did_ifconfig_setup;
+
+ /** if the internal variables related to ifconfig-ipv6 of this struct have
+ * been set up. This does NOT mean ifconfig has been called */
bool did_ifconfig_ipv6_setup;
bool persistent_if; /* if existed before, keep on program end */
@@ -231,6 +249,7 @@
unsigned int rwflags_debug;
dco_context_t dco;
+ afunix_context_t afunix;
};
static inline bool
@@ -354,8 +373,14 @@
#define IFCONFIG_DEFAULT IFCONFIG_AFTER_TUN_OPEN
static inline int
-ifconfig_order(void)
+ifconfig_order(struct tuntap *tt)
{
+#if defined AF_UNIX
+ if (tt->backend_driver == DRIVER_AFUNIX)
+ {
+ return IFCONFIG_BEFORE_TUN_OPEN;
+ }
+#endif
#if defined(TARGET_LINUX)
return IFCONFIG_AFTER_TUN_OPEN;
#elif defined(TARGET_SOLARIS)
@@ -380,8 +405,14 @@
#define ROUTE_ORDER_DEFAULT ROUTE_AFTER_TUN
static inline int
-route_order(void)
+route_order(struct tuntap *tt)
{
+#if defined AF_UNIX
+ if (tt->backend_driver == DRIVER_AFUNIX)
+ {
+ return ROUTE_BEFORE_TUN;
+ }
+#endif
#if defined(TARGET_ANDROID)
return ROUTE_BEFORE_TUN;
#else
@@ -759,5 +790,4 @@
{
return tt && tt->type != DEV_TYPE_UNDEF;
}
-
#endif /* TUN_H */
new file mode 100644
@@ -0,0 +1,180 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include "tun.h"
+#include "fdmisc.h"
+#include "run_command.h"
+#include "manage.h"
+#include "win32.h"
+#include "wfp_block.h"
+#include "argv.h"
+#include "options.h"
+
+#include "memdbg.h"
+#include "openssl_compat.h"
+
+#if defined(AF_UNIX) && !defined(WIN32)
+/* Windows does implement some AF_UNIX functionality but key features
+ * like socketpair() and SOCK_DGRAM are missing */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdlib.h>
+
+static void
+tun_afunix_exec_child(const char *dev_node, struct tuntap *tt, struct env_set *env)
+{
+ struct argv argv = argv_new();
+
+ /* since we know that dev-node starts with unix: we can just skip that
+ * to get the program name */
+ const char *program = dev_node + strlen("unix:");
+
+ argv_printf(&argv, "%s", program);
+
+ argv_msg(M_INFO, &argv);
+ tt->afunix.childprocess = openvpn_execve_check(&argv, env, S_NOWAITPID,
+ "ERROR: failure executing "
+ "process for tun");
+ argv_free(&argv);
+}
+
+void
+open_tun_afunix(struct options *o,
+ int mtu,
+ struct tuntap *tt,
+ struct env_set *orig_env)
+{
+ struct gc_arena gc = gc_new();
+
+ int fds[2];
+ if (!(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == 0))
+ {
+ msg(M_ERR, "Cannot create socket pair for AF_UNIX socket to external "
+ "program");
+ return;
+ }
+
+ /* Use the first file descriptor for our side and avoid passing it
+ * to the child */
+ tt->fd = fds[1];
+ set_cloexec(tt->fd);
+
+ /* Make a copy of the env, so we do not need to delete our custom
+ * environment variables later */
+ struct env_set *env = env_set_create(&gc);
+ env_set_inherit(env, orig_env);
+
+ setenv_int(env, "TUNTAP_SOCKET_FD", fds[0]);
+ setenv_str(env, "TUNTAP_DEV_TYPE", dev_type_string(o->dev, o->dev_type));
+ setenv_int(env, "TUNTAP_MTU", mtu);
+ if (o->route_default_gateway)
+ {
+ setenv_str(env, "ifconfig_gateway", o->route_default_gateway);
+ }
+ if (o->lladdr)
+ {
+ setenv_str(env, "TUNTAP_LLADDR", o->lladdr);
+ }
+
+ tun_afunix_exec_child(o->dev_node, tt, env);
+
+ close(fds[0]);
+
+ /* tt->actual_name is passed to up and down scripts and used as the ifconfig dev name */
+ tt->actual_name = string_alloc("internal:af_unix", NULL);
+
+ gc_free(&gc);
+}
+
+void
+tun_afunix_close(struct tuntap *tt)
+{
+ ASSERT(tt);
+ if (tt->fd >= 0)
+ {
+ close(tt->fd);
+ tt->fd = 0;
+ }
+ kill(tt->afunix.childprocess, SIGINT);
+
+ free(tt->actual_name);
+ free(tt);
+}
+
+ssize_t
+tun_afunix_write(struct tuntap *tt, uint8_t *buf, int len)
+{
+ int ret;
+ waitpid(tt->afunix.childprocess, &ret, WNOHANG);
+ if (ret != 0)
+ {
+ msg(M_INFO, "Child process for afunix dead");
+ return EIO;
+ }
+ return write(tt->fd, buf, len);
+}
+
+ssize_t
+tun_afunix_read(struct tuntap *tt, uint8_t *buf, int len)
+{
+ return read(tt->fd, buf, len);
+}
+#else /* ifdef AF_UNIX */
+void
+open_tun_afunix(const char *dev, const char *dev_type, int mtu,
+ struct tuntap *tt, struct env_set env)
+{
+ msg(M_ERR, "AF_UNIX socket support not available on this platform");
+}
+
+void
+tun_afunix_close(struct tuntap *tt)
+{
+ /* should never be called as open_tun_afunix always fails */
+ ASSERT(0);
+}
+
+ssize_t
+tun_afunix_write(struct tuntap *tt, uint8_t *buf, int len)
+{
+ /* should never be called as open_tun_afunix always fails */
+ ASSERT(0);
+}
+
+ssize_t
+tun_afunix_read(struct tuntap *tt, uint8_t *buf, int len)
+{
+ /* should never be called as open_tun_afunix always fails */
+ ASSERT(0);
+}
+
+#endif /* ifdef AF_UNIX */
new file mode 100644
@@ -0,0 +1,72 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single TCP/UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2024 OpenVPN Inc <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AFUNIX_TUN_H
+#define AFUNIX_TUN_H
+#include <sys/types.h>
+
+#include "tun.h"
+
+/**
+ * Opens an AF_UNIX based tun device. This also executes the command that
+ * the user provided to that will take care of implementing the actual tun
+ * device
+ */
+void
+open_tun_afunix(struct options *o,
+ int mtu,
+ struct tuntap *tt,
+ struct env_set *env);
+
+
+/**
+ * Closes the socket used for the AF_UNIX based device. Also sends a
+ * SIGINT to the child process that was spawned to handle the tun device
+ */
+void
+tun_afunix_close(struct tuntap *tt);
+
+/**
+ * Writes a packet to a AF_UNIX based tun device
+ */
+ssize_t
+tun_afunix_write(struct tuntap *tt, uint8_t *buf, int len);
+
+/**
+ * Reads a packet from a AF_UNIX based tun devices
+ */
+ssize_t
+tun_afunix_read(struct tuntap *tt, uint8_t *buf, int len);
+
+#endif /* AFUNIX_TUN_H */
+
+/**
+ * Checks whether a --dev-node parameter specifies a AF_UNIX device
+ * @param devnode the string to check
+ * @return true if the string starts with unix:
+ */
+static inline bool
+is_tun_afunix(const char *devnode)
+{
+ return devnode && strprefix(devnode, "unix:");
+}
Attention is currently required from: flichtenheld. Hello flichtenheld, I'd like you to do a code review. Please visit http://gerrit.openvpn.net/c/openvpn/+/747?usp=email to review the following change. Change subject: Introduce DRIVER_AFUNIX backend for use with lwipovpn ...................................................................... Introduce DRIVER_AFUNIX backend for use with lwipovpn lwipovpn is a using lwip TCP/IP implementation with an AF_UNIX implementation to emulate a tun/tap device without messing with the TCP/IP stack of the host. For more information about lwipovpn see https://github.com/OpenVPN/lwipovpn Change-Id: I65099ef00822d08fd3f5480c80892f3bf86c56e7 Signed-off-by: Arne Schwabe <arne@rfc2549.org> --- M CMakeLists.txt M Changes.rst M doc/man-sections/vpn-network-options.rst M src/openvpn/Makefile.am M src/openvpn/forward.c M src/openvpn/init.c M src/openvpn/run_command.c M src/openvpn/run_command.h M src/openvpn/ssl.h M src/openvpn/tun.c M src/openvpn/tun.h A src/openvpn/tun_afunix.c A src/openvpn/tun_afunix.h 13 files changed, 383 insertions(+), 16 deletions(-) git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/47/747/1