Skip to content

Commit

Permalink
Introduce DRIVER_AFUNIX backend for use with lwipovpn
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Gert Doering <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg29379.html
Signed-off-by: Gert Doering <[email protected]>
  • Loading branch information
schwabe authored and cron2 committed Sep 24, 2024
1 parent d849073 commit d0a9362
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 16 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ set(SOURCE_FILES
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
Expand Down
13 changes: 13 additions & 0 deletions Changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ TLS alerts
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 unix:/path/to/lwipovpn`` can emulate a full client that
can be pinged, can serve 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.
Expand Down
10 changes: 10 additions & 0 deletions doc/man-sections/vpn-network-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,16 @@ routing.
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 start the program and create a temporary unix domain socket that
will be passed to the program together with the tun configuration as
environment variables. The temporary unix domain socket will be be passed
in the environment variable :code:`TUNTAP_SOCKET_FD`.

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
Expand Down
1 change: 1 addition & 0 deletions src/openvpn/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ openvpn_SOURCES = \
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 \
Expand Down
7 changes: 7 additions & 0 deletions src/openvpn/dco.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "ssl_common.h"
#include "ssl_ncp.h"
#include "tun.h"
#include "tun_afunix.h"

#ifdef HAVE_LIBCAPNG
#include <cap-ng.h>
Expand Down Expand Up @@ -298,6 +299,12 @@ dco_check_startup_option(int msglevel, const struct options *o)
return false;
}

if (is_tun_afunix(o->dev_node))
{
msg(msglevel, "Note: afunix tun type selected, disabling data channel offload");
return false;
}

if (o->connection_list)
{
const struct connection_list *l = o->connection_list;
Expand Down
19 changes: 17 additions & 2 deletions src/openvpn/forward.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "ssl_verify.h"
#include "dco.h"
#include "auth_token.h"
#include "tun_afunix.h"

#include "memdbg.h"

Expand Down Expand Up @@ -1319,7 +1320,14 @@ read_incoming_tun(struct context *c)
#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 = read_tun_afunix(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
Expand Down Expand Up @@ -1926,7 +1934,14 @@ process_outgoing_tun(struct context *c)
#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 = write_tun_afunix(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)
Expand Down
58 changes: 47 additions & 11 deletions src/openvpn/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "mss.h"
#include "mudp.h"
#include "dco.h"
#include "tun_afunix.h"

#include "memdbg.h"

Expand Down Expand Up @@ -1758,10 +1759,18 @@ do_init_tun(struct context *c)
&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;
}
Expand All @@ -1786,6 +1795,10 @@ do_init_tun(struct context *c)
static bool
can_preserve_tun(struct tuntap *tt)
{
if (tt && tt->backend_driver == DRIVER_AFUNIX)
{
return false;
}
#ifdef TARGET_ANDROID
return false;
#else
Expand Down Expand Up @@ -1841,6 +1854,22 @@ del_wfp_block(struct context *c, unsigned long adapter_index)
#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)
{
Expand All @@ -1863,7 +1892,8 @@ do_open_tun(struct context *c, int *error_flags)
}
#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 */
Expand Down Expand Up @@ -1898,7 +1928,7 @@ do_open_tun(struct context *c, int *error_flags)

/* 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 */
Expand All @@ -1911,7 +1941,7 @@ do_open_tun(struct context *c, int *error_flags)
}

/* 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,
Expand All @@ -1928,8 +1958,7 @@ do_open_tun(struct context *c, int *error_flags)
}

/* 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)
Expand All @@ -1940,7 +1969,7 @@ do_open_tun(struct context *c, int *error_flags)

/* 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);
Expand All @@ -1966,7 +1995,7 @@ do_open_tun(struct context *c, int *error_flags)
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);
Expand Down Expand Up @@ -2026,7 +2055,14 @@ do_close_tun_simple(struct context *c)
{
undo_ifconfig(c->c1.tuntap, &c->net_ctx);
}
close_tun(c->c1.tuntap, &c->net_ctx);
if (c->c1.tuntap->backend_driver == DRIVER_AFUNIX)
{
close_tun_afunix(c->c1.tuntap);
}
else
{
close_tun(c->c1.tuntap, &c->net_ctx);
}
c->c1.tuntap = NULL;
}
c->c1.tuntap_owned = false;
Expand Down Expand Up @@ -2466,7 +2502,7 @@ do_up(struct context *c, bool pulled_options, unsigned int option_types_found)
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);
Expand Down
9 changes: 9 additions & 0 deletions src/openvpn/run_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned in
{
msg(M_ERR, "openvpn_execve: unable to fork");
}
else if (flags & S_NOWAITPID)
{
ret = pid;
}
else /* parent side */
{
if (waitpid(pid, &ret, 0) != pid)
Expand Down Expand Up @@ -204,6 +208,11 @@ openvpn_execve_check(const struct argv *a, const struct env_set *es, const unsig
goto done;
}
}
else if (flags & S_NOWAITPID && (stat > 0))
{
ret = stat;
goto done;
}
else if (platform_system_ok(stat))
{
ret = true;
Expand Down
3 changes: 3 additions & 0 deletions src/openvpn/run_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ void script_security_set(int level);
/** 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 to exit and report the 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);
Expand Down
3 changes: 3 additions & 0 deletions src/openvpn/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ print_tun_backend_driver(enum tun_driver_type driver)
case DRIVER_DCO:
return "ovpn-dco";

case DRIVER_AFUNIX:
return "unix";

case DRIVER_UTUN:
return "utun";

Expand Down
34 changes: 31 additions & 3 deletions src/openvpn/tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ enum tun_driver_type {
WINDOWS_DRIVER_TAP_WINDOWS6,
WINDOWS_DRIVER_WINTUN,
DRIVER_GENERIC_TUNTAP,
/** using an AF_UNIX socket to pass packets from/to an external program.
* This is always defined. We error out if a user tries to open this type
* of backend on unsupported platforms. */
DRIVER_AFUNIX,
DRIVER_DCO,
/** macOS internal tun driver */
DRIVER_UTUN
Expand Down Expand Up @@ -161,6 +165,17 @@ struct tuntap_options {
/*
* Define a TUN/TAP dev.
*/
#ifndef WIN32
typedef struct afunix_context
{
pid_t childprocess;
} afunix_context_t;

#else /* ifndef WIN32 */
typedef struct {
int dummy;
} afunix_context_t;
#endif

struct tuntap
{
Expand All @@ -175,7 +190,12 @@ struct tuntap
*/
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 */
Expand Down Expand Up @@ -227,6 +247,7 @@ struct tuntap
unsigned int rwflags_debug;

dco_context_t dco;
afunix_context_t afunix;
};

static inline bool
Expand Down Expand Up @@ -350,8 +371,12 @@ void warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx);
#define IFCONFIG_DEFAULT IFCONFIG_AFTER_TUN_OPEN

static inline int
ifconfig_order(void)
ifconfig_order(struct tuntap *tt)
{
if (tt->backend_driver == DRIVER_AFUNIX)
{
return IFCONFIG_BEFORE_TUN_OPEN;
}
#if defined(TARGET_LINUX)
return IFCONFIG_AFTER_TUN_OPEN;
#elif defined(TARGET_SOLARIS)
Expand All @@ -376,8 +401,12 @@ ifconfig_order(void)
#define ROUTE_ORDER_DEFAULT ROUTE_AFTER_TUN

static inline int
route_order(void)
route_order(struct tuntap *tt)
{
if (tt->backend_driver == DRIVER_AFUNIX)
{
return ROUTE_BEFORE_TUN;
}
#if defined(TARGET_ANDROID)
return ROUTE_BEFORE_TUN;
#else
Expand Down Expand Up @@ -755,5 +784,4 @@ is_tun_type_set(const struct tuntap *tt)
{
return tt && tt->type != DEV_TYPE_UNDEF;
}

#endif /* TUN_H */
Loading

0 comments on commit d0a9362

Please sign in to comment.