[Openvpn-devel,v2,1/2] Add --bind-dev option.
Commit Message
From: Maximilian Wilhelm <max@sdn.clinic>
This options allows the user to specify a network device the OpenVPN process
should use when making a connection or binding to an address. This translates
in setting the SO_BINDTODEVICE option to the corresponding socket (on Linux).
When for example using VRFs on Linux [0] this allows making connections using
the non-default VRF and having the tun/tap interface in the default VRF.
Thanks to David Ahern (Cumulus Networks) for insights on this.
[0] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/vrf.txt
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
---
src/openvpn/init.c | 1 +
src/openvpn/options.c | 10 ++++++++++
src/openvpn/options.h | 1 +
src/openvpn/socket.c | 14 ++++++++++++++
src/openvpn/socket.h | 2 ++
5 files changed, 28 insertions(+)
Comments
Acked-by: Gert Doering <gert@greenie.muc.de>
Your patch has been applied to the master branch.
I have stared at the code (looks reasonable) and run t_client tests on
Linux and FreeBSD (pass, no major surprise).
I have not actually tested the functionality, because I do not have a test
rig with VRF (or multiple ethernet links) around. From what I found so far,
what SO_BINDTODEVICE does is "ensure that packets sent via that socket go
out via the specified interface, and only that interface" and "packets
coming in have been received on that interface". Besides "a network
interface" you can also specify the name of a "VRF device" (which seems
to be a Linux abstraction to group together "Interfaces and Routes"
under one umbrella).
So you can use this for VRFs ("--bind-dev outer-vrf") or to select one
particular interface (*and* routes) inside "global" or "VRF".
I have tested the latter ("--bind-to lo") which makes connection setup
*fail* - obviously. Trying to bind to nonexistant devices gives a proper
error message in v2 now
2020-06-29 13:27:03 WARN: setsockopt SO_BINDTODEVICE=MyTunnelIsLongerThanYours failed: No such device (errno=19)
Googling for SO_BINDTODEVICE shows lots of confusion on how this is used
and what it does, but I consider the kernel documentation to be authoritative
(Documentation/networking/vrf.txt) and that says "what you do is correct":
Applications
------------
Applications that are to work within a VRF need to bind their socket to the
VRF device:
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen(dev)+1);
(note the "strlen(dev)+1" part, and "not a struct ifreq")
Your patch has been applied to the master branch.
commit 19d3c602e7a3881cf7c2244b7c40b9958c0b7ebc
Author: Maximilian Wilhelm
Date: Mon Jun 29 12:49:07 2020 +0200
Add --bind-dev option.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1593427748-29801-2-git-send-email-max@rfc2324.org>
URL: https://www.mail-archive.com/search?l=mid&q=1593427748-29801-2-git-send-email-max@rfc2324.org
Signed-off-by: Gert Doering <gert@greenie.muc.de>
--
kind regards,
Gert Doering
@@ -3445,6 +3445,7 @@ do_init_socket_1(struct context *c, const int mode)
c->options.rcvbuf,
c->options.sndbuf,
c->options.mark,
+ c->options.bind_dev,
&c->c2.server_poll_interval,
sockflags);
}
@@ -294,6 +294,9 @@ static const char usage_message[] =
#if defined(TARGET_LINUX) && HAVE_DECL_SO_MARK
"--mark value : Mark encrypted packets being sent with value. The mark value\n"
" can be matched in policy routing and packetfilter rules.\n"
+ "--bind-dev dev : Bind to the given device when making connection to a peer or\n"
+ " listening for connections. This allows sending encrypted packets\n"
+ " via a VRF present on the system.\n"
#endif
"--txqueuelen n : Set the tun/tap TX queue length to n (Linux only).\n"
#ifdef ENABLE_MEMSTATS
@@ -6084,6 +6087,13 @@ add_option(struct options *options,
}
}
}
+#ifdef TARGET_LINUX
+ else if (streq (p[0], "bind-dev") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_SOCKFLAGS);
+ options->bind_dev = p[1];
+ }
+#endif
else if (streq(p[0], "txqueuelen") && p[1] && !p[2])
{
VERIFY_PERMISSION(OPT_P_GENERAL);
@@ -352,6 +352,7 @@ struct options
/* mark value */
int mark;
+ char *bind_dev;
/* socket flags */
unsigned int sockflags;
@@ -1138,6 +1138,18 @@ create_socket(struct link_socket *sock, struct addrinfo *addr)
/* set socket to --mark packets with given value */
socket_set_mark(sock->sd, sock->mark);
+#if defined(TARGET_LINUX)
+ if (sock->bind_dev)
+ {
+ msg (M_INFO, "Using bind-dev %s", sock->bind_dev);
+ if (setsockopt (sock->sd, SOL_SOCKET, SO_BINDTODEVICE, sock->bind_dev, strlen (sock->bind_dev) + 1) != 0)
+ {
+ msg(M_WARN|M_ERRNO, "WARN: setsockopt SO_BINDTODEVICE=%s failed", sock->bind_dev);
+ }
+
+ }
+#endif
+
bind_local(sock, addr->ai_family);
}
@@ -1880,6 +1892,7 @@ link_socket_init_phase1(struct link_socket *sock,
int rcvbuf,
int sndbuf,
int mark,
+ const char *bind_dev,
struct event_timeout *server_poll_timeout,
unsigned int sockflags)
{
@@ -1906,6 +1919,7 @@ link_socket_init_phase1(struct link_socket *sock,
sock->sockflags = sockflags;
sock->mark = mark;
+ sock->bind_dev = bind_dev;
sock->info.proto = proto;
sock->info.af = af;
@@ -212,6 +212,7 @@ struct link_socket
#define SF_GETADDRINFO_DGRAM (1<<4)
unsigned int sockflags;
int mark;
+ const char *bind_dev;
/* for stream sockets */
struct stream_buf stream_buf;
@@ -326,6 +327,7 @@ link_socket_init_phase1(struct link_socket *sock,
int rcvbuf,
int sndbuf,
int mark,
+ const char *bind_dev,
struct event_timeout *server_poll_timeout,
unsigned int sockflags);
/* Reenable uncrustify *INDENT-ON* */