From 14f2537458c79637fc1f62a0593daa47936b7652 Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Tue, 27 Jun 2017 13:14:20 +0200
Subject: [PATCH 2/2] allow using custom file descriptor as tun

---
 src/openvpn/init.c    |  8 +++++---
 src/openvpn/options.c |  6 ++++++
 src/openvpn/options.h |  1 +
 src/openvpn/tun.c     | 41 ++++++++++++++++++++++++-----------------
 src/openvpn/tun.h     |  6 ++++--
 5 files changed, 40 insertions(+), 22 deletions(-)

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 10112870..6d7f4856 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -839,7 +839,7 @@ init_verb_mute (struct context *c, unsigned int flags)
 void
 init_options_dev (struct options *options)
 {
-  if (!options->dev && options->dev_node) {
+  if (!options->dev && options->dev_from_fd == -1 && options->dev_node) {
     char *dev_node = string_alloc(options->dev_node, NULL); /* POSIX basename() implementaions may modify its arguments */
     options->dev = basename (dev_node);
   }
@@ -924,7 +924,8 @@ do_persist_tuntap (const struct options *options)
 	msg (M_FATAL|M_OPTERR,
 	     "options --mktun or --rmtun should only be used together with --dev");
 #ifdef ENABLE_FEATURE_TUN_PERSIST
-      tuncfg (options->dev, options->dev_type, options->dev_node,
+		tuncfg (options->dev, options->dev_from_fd,
+			options->dev_type, options->dev_node,
 	      options->persist_mode,
 	      options->username, options->groupname, &options->tuntap_options);
       if (options->persist_mode && options->lladdr)
@@ -1509,7 +1510,8 @@ do_open_tun (struct context *c)
 	}
 
       /* open the tun device */
-      open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
+      open_tun (c->options.dev, c->options.dev_from_fd,
+		c->options.dev_type, c->options.dev_node,
 		c->c1.tuntap);
 
       /* set the hardware address */
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index 6faa2808..893068dd 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -775,6 +775,7 @@ init_options (struct options *o, const bool init_gc)
       gc_init (&o->gc);
       o->gc_owned = true;
     }
+  o->dev_from_fd = -1;
   o->mode = MODE_POINT_TO_POINT;
   o->topology = TOP_NET30;
   o->ce.proto = PROTO_UDPv4;
@@ -4455,6 +4456,11 @@ add_option (struct options *options,
       VERIFY_PERMISSION (OPT_P_GENERAL);
       options->dev = p[1];
     }
+  else if (streq (p[0], "dev-from-fd") && p[1])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->dev_from_fd = positive_atoi(p[1]);
+    }
   else if (streq (p[0], "dev-type") && p[1])
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index e4235e98..c5fa3bc3 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -220,6 +220,7 @@ struct options
   bool remote_random;
   const char *ipchange;
   const char *dev;
+  int dev_from_fd;
   const char *dev_type;
   const char *dev_node;
   const char *lladdr;
diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 536601bb..68c8d6ea 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -1521,7 +1521,7 @@ close_tun_generic (struct tuntap *tt)
 #if !PEDANTIC
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   struct ifreq ifr;
 
@@ -1544,10 +1544,16 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
       /*
        * Open the interface
        */
-      if ((tt->fd = open (node, O_RDWR)) < 0)
-	{
-	  msg (M_ERR, "ERROR: Cannot open TUN/TAP dev %s", node);
-	}
+      if (dev_from_fd >= 0) {
+	      tt->fd = dup(dev_from_fd);
+	      strncpynt (ifr.ifr_name, dev, IFNAMSIZ);
+	      goto done;
+      } else {
+	      if ((tt->fd = open (node, O_RDWR)) < 0)
+	      {
+		      msg (M_ERR, "ERROR: Cannot open TUN/TAP dev %s", node);
+	      }
+      }
 
       /*
        * Process --tun-ipv6
@@ -1620,6 +1626,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
       }
 #endif
 
+    done:
       set_nonblock (tt->fd);
       set_cloexec (tt->fd);
       tt->actual_name = string_alloc (ifr.ifr_name, NULL);
@@ -1630,7 +1637,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
 #else
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   ASSERT (0);
 }
@@ -1640,7 +1647,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
 #else
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 }
@@ -1662,7 +1669,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tu
 #endif
 
 void
-tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+tuncfg (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
 {
   struct tuntap *tt;
 
@@ -1670,7 +1677,7 @@ tuncfg (const char *dev, const char *dev_type, const char *dev_node, int persist
   clear_tuntap (tt);
   tt->type = dev_type_enum (dev, dev_type);
   tt->options = *options;
-  open_tun (dev, dev_type, dev_node, tt);
+  open_tun (dev, dev_from_fd, dev_type, dev_node, tt);
   if (ioctl (tt->fd, TUNSETPERSIST, persist_mode) < 0)
     msg (M_ERR, "Cannot ioctl TUNSETPERSIST(%d) %s", persist_mode, dev);
   if (username != NULL)
@@ -1835,7 +1842,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
 #endif
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   int if_fd, ip_muxid, arp_muxid, arp_fd, ppa = -1;
   struct lifreq ifr;
@@ -2141,7 +2148,7 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
 #elif defined(TARGET_OPENBSD)
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
@@ -2235,7 +2242,7 @@ read_tun (struct tuntap *tt, uint8_t *buf, int len)
  */
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
 #ifdef NETBSD_MULTI_AF
     open_tun_generic (dev, dev_type, dev_node, true, true, tt);
@@ -2382,7 +2389,7 @@ freebsd_modify_read_write_return (int len)
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
@@ -2493,7 +2500,7 @@ dragonfly_modify_read_write_return (int len)
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
@@ -2698,7 +2705,7 @@ open_darwin_utun (const char *dev, const char *dev_type, const char *dev_node, s
 #endif
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
 #ifdef HAVE_NET_IF_UTUN_H
   /* If dev_node does not start start with utun assume regular tun/tap */
@@ -4807,7 +4814,7 @@ dhcp_masq_addr (const in_addr_t local, const in_addr_t netmask, const int offset
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   struct gc_arena gc = gc_new ();
   char device_path[256];
@@ -5395,7 +5402,7 @@ ipset2ascii_all (struct gc_arena *gc)
 #else /* generic */
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+open_tun (const char *dev, int dev_from_fd, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 }
diff --git a/src/openvpn/tun.h b/src/openvpn/tun.h
index 138856d3..30e67cb6 100644
--- a/src/openvpn/tun.h
+++ b/src/openvpn/tun.h
@@ -206,7 +206,8 @@ tuntap_defined (const struct tuntap *tt)
  * Function prototypes
  */
 
-void open_tun (const char *dev, const char *dev_type, const char *dev_node,
+void open_tun (const char *dev,
+	       int dev_from_fd, const char *dev_type, const char *dev_node,
 	       struct tuntap *tt);
 
 void close_tun (struct tuntap *tt);
@@ -215,7 +216,8 @@ int write_tun (struct tuntap* tt, uint8_t *buf, int len);
 
 int read_tun (struct tuntap* tt, uint8_t *buf, int len);
 
-void tuncfg (const char *dev, const char *dev_type, const char *dev_node,
+void tuncfg (const char *dev, int dev_from_fd,
+	     const char *dev_type, const char *dev_node,
 	     int persist_mode, const char *username,
 	     const char *groupname, const struct tuntap_options *options);
 
-- 
2.13.0

