]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
- Win32 IPv6 ifconfig support, using "netsh" calls
authorGert Doering <gert@greenie.muc.de>
Sun, 28 Feb 2010 21:57:28 +0000 (22:57 +0100)
committerGert Doering <gert@greenie.muc.de>
Sun, 24 Apr 2011 15:22:40 +0000 (17:22 +0200)
- initialize tuntap->ipv6 in init.c::do_init_tun(), to make sure it's
  setup "early enough", no matter what ifconfig_order() wants

- change call convention for open_tun(): drop "ipv6" flag, because it's
  incompatible with windows/openbsd calling sequence (ifconfig first,
  open_tun later) - also affects open_tun_generic() and tuncfg().

- drop ipv6_support() helper function - has no useful purpose anymore

- introduce add_route_connected_v6_net() helper for Win32, Darwin, Netbsd
  (cleanup code)

- fix NetBSD tunnel setup - destroy/recreate before ifconfig'ing, to make
  sure no leftover configuration lingers on tunnel from previous call
  (NetBSD tunnels are always persistent unless explicitely destroyed)

- DARWIN (MacOS X) gets its own #ifdef section for open_tun()/close_tun()
  now, because close_tun() needs to cleanup IPv6 ifconfig

init.c
tun.c

diff --git a/init.c b/init.c
index d716495d9d7c534a3df23b34dc3e038cb9262b12..6ca3c55b73ec71c71c73c2a8a47e7245c92c3772 100644 (file)
--- a/init.c
+++ b/init.c
@@ -843,7 +843,7 @@ do_persist_tuntap (const struct options *options)
        msg (M_FATAL|M_OPTERR,
             "options --mktun or --rmtun should only be used together with --dev");
       tuncfg (options->dev, options->dev_type, options->dev_node,
-             options->tun_ipv6, options->persist_mode,
+             options->persist_mode,
              options->username, options->groupname, &options->tuntap_options);
       if (options->persist_mode && options->lladdr)
         set_lladdr(options->dev, options->lladdr, NULL);
@@ -1282,6 +1282,9 @@ do_init_tun (struct context *c)
                           !c->options.ifconfig_nowarn,
                           c->c2.es);
 
+  /* flag tunnel for IPv6 config if --tun-ipv6 is set */
+  c->c1.tuntap->ipv6 = c->options.tun_ipv6;
+
   init_tun_post (c->c1.tuntap,
                 &c->c2.frame,
                 &c->options.tuntap_options);
@@ -1331,7 +1334,7 @@ do_open_tun (struct context *c)
 
       /* open the tun device */
       open_tun (c->options.dev, c->options.dev_type, c->options.dev_node,
-               c->options.tun_ipv6, c->c1.tuntap);
+               c->c1.tuntap);
 
       /* set the hardware address */
       if (c->options.lladdr)
diff --git a/tun.c b/tun.c
index 9dcd9e6fbf8a2bb39bbb625686b8ded988e591f4..ef49ae099e497f2ccbf2362ab9872a7aac988838 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -56,6 +56,7 @@ static void netsh_ifconfig (const struct tuntap_options *to,
                            const in_addr_t ip,
                            const in_addr_t netmask,
                            const unsigned int flags);
+static void netsh_command (const struct argv *a, int n);
 
 static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
 
@@ -129,31 +130,6 @@ guess_tuntap_dev (const char *dev,
   return dev;
 }
 
-/*
- * Called by the open_tun function of OSes to check if we
- * explicitly support IPv6.
- *
- * In this context, explicit means that the OS expects us to
- * do something special to the tun socket in order to support
- * IPv6, i.e. it is not transparent.
- *
- * ipv6_explicitly_supported should be set to false if we don't
- * have any explicit IPv6 code in the tun device handler.
- *
- * If ipv6_explicitly_supported is true, then we have explicit
- * OS-specific tun dev code for handling IPv6.  If so, tt->ipv6
- * is set according to the --tun-ipv6 command line option.
- *
- * (enabling IPv6 on tun devices might work anyway, but since 
- * we don't know, we log a warning)
- */
-static void
-ipv6_support (bool ipv6, bool ipv6_explicitly_supported, struct tuntap* tt)
-{
-  tt->ipv6 = ipv6;
-  if (ipv6 && !ipv6_explicitly_supported)
-    msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
-}
 
 /* --ifconfig-nowarn disables some options sanity checking */
 static const char ifconfig_warn_how_to_silence[] = "(silence this warning with --ifconfig-nowarn)";
@@ -596,6 +572,28 @@ init_tun_post (struct tuntap *tt,
 #endif
 }
 
+#if defined(TARGET_WIN32) || \
+    defined(TARGET_DARWIN) || defined(TARGET_NETBSD)
+
+/* some of the platforms will auto-add a "network route" pointing
+ * to the interface on "ifconfig tunX 2001:db8::1/64", others need
+ * an extra call to "route add..."
+ * -> helper function to simplify code below
+ */
+void add_route_connected_v6_net(struct tuntap * tt,
+                               const struct env_set *es)
+{
+    struct route_ipv6 r6;
+
+    r6.defined = true;
+    r6.network = tt->local_ipv6;
+    r6.netbits = tt->netbits_ipv6;
+    r6.gateway = tt->local_ipv6;
+    add_route_ipv6 (&r6, tt, 0, es);
+}
+#endif
+
+
 /* execute the ifconfig command through the shell */
 void
 do_ifconfig (struct tuntap *tt,
@@ -618,7 +616,7 @@ do_ifconfig (struct tuntap *tt,
 
       argv_init (&argv);
 
-      msg( M_INFO, "do_ifconfig, ipv6=%d", tt->ipv6 );
+      msg( M_INFO, "do_ifconfig, tt->ipv6=%d", tt->ipv6 );
 
       /*
        * We only handle TUN/TAP devices here, not --dev null devices.
@@ -684,7 +682,7 @@ do_ifconfig (struct tuntap *tt,
                                  );
                  argv_msg (M_INFO, &argv);
                  openvpn_execve_check (&argv, es, S_FATAL, "Linux ip addr add failed");
-                 if ( do_ipv6 )                /* GERT-TODO: yet UNTESTED! */
+                 if ( do_ipv6 )
                    {
                      argv_printf( &argv,
                                  "%s -6 addr add %s/%d dev %s",
@@ -895,10 +893,15 @@ do_ifconfig (struct tuntap *tt,
 # define NETBSD_MULTI_AF
 #endif
 
-      /* as on OpenBSD and Darwin, destroy and re-create tun0 interface
+      /* as on OpenBSD and Darwin, destroy and re-create tun<x> interface
        */
       argv_printf (&argv, "%s %s destroy", IFCONFIG_PATH, actual );
       argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, 0, "NetBSD ifconfig destroy failed");
+
+      argv_printf (&argv, "%s %s create", IFCONFIG_PATH, actual );
+      argv_msg (M_INFO, &argv);
+      openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig create failed");
 
       if (tun)
        argv_printf (&argv,
@@ -930,7 +933,6 @@ do_ifconfig (struct tuntap *tt,
       if ( do_ipv6 )
        {
 #ifdef NETBSD_MULTI_AF
-         struct route_ipv6 r6;
          argv_printf (&argv,
                          "%s %s inet6 %s/%d",
                          IFCONFIG_PATH,
@@ -942,11 +944,7 @@ do_ifconfig (struct tuntap *tt,
          openvpn_execve_check (&argv, es, S_FATAL, "NetBSD ifconfig failed");
 
          /* and, hooray, we explicitely need to add a route... */
-         r6.defined = true;
-         r6.network = tt->local_ipv6;
-         r6.netbits = tt->netbits_ipv6;
-         r6.gateway = tt->local_ipv6;
-         add_route_ipv6 (&r6, tt, 0, es);
+         add_route_connected_v6_net(tt, es);
 #else
          msg( M_INFO, "no IPv6 support for tun interfaces on NetBSD before 4.0 (if your system is newer, recompile openvpn)" );
          tt->ipv6 = false;
@@ -1019,7 +1017,6 @@ do_ifconfig (struct tuntap *tt,
 
       if ( do_ipv6 )
        {
-         struct route_ipv6 r6;
           argv_printf (&argv,
                               "%s %s inet6 %s/%d",
                               IFCONFIG_PATH,
@@ -1031,11 +1028,7 @@ do_ifconfig (struct tuntap *tt,
          openvpn_execve_check (&argv, es, S_FATAL, "MacOS X ifconfig inet6 failed");
 
          /* and, hooray, we explicitely need to add a route... */
-         r6.defined = true;
-         r6.network = tt->local_ipv6;
-         r6.netbits = tt->netbits_ipv6;
-         r6.gateway = tt->local_ipv6;
-         add_route_ipv6 (&r6, tt, 0, es);
+         add_route_connected_v6_net(tt, es);
        }
 
 #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
@@ -1128,10 +1121,34 @@ do_ifconfig (struct tuntap *tt,
        tt->did_ifconfig = true;
       }
 
-      if ( do_ipv6 )
-       {
-         msg( M_FATAL, "can't configure IPv6 on Win32 yet - unimplemented" );
-       }
+    /* IPv6 always uses "netsh" interface */
+    if ( do_ipv6 )
+      {
+       char * saved_actual;
+
+       if (!strcmp (actual, "NULL"))
+         msg (M_FATAL, "Error: When using --tun-ipv6, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
+
+       /* example: netsh interface ipv6 add address MyTap 2001:608:8003::d */
+       argv_printf (&argv,
+                   "%s%sc interface ipv6 add address %s %s",
+                    get_win_sys_path(),
+                    NETSH_PATH_SUFFIX,
+                    actual,
+                    ifconfig_ipv6_local );
+
+       netsh_command (&argv, 4);
+
+       /* explicit route needed */
+       /* on windows, OpenVPN does ifconfig first, open_tun later, so
+        * tt->actual_name might not yet be initialized, but routing code
+        * needs to know interface name - point to "actual", restore later
+        */
+       saved_actual = tt->actual_name;
+       tt->actual_name = (char*) actual;
+       add_route_connected_v6_net(tt, es);
+       tt->actual_name = saved_actual;
+      }
 #else
       msg (M_FATAL, "Sorry, but I don't know how to do 'ifconfig' commands on this operating system.  You should ifconfig your TUN/TAP device manually or use an --up script.");
 #endif
@@ -1164,14 +1181,16 @@ open_null (struct tuntap *tt)
 #ifndef WIN32
 static void
 open_tun_generic (const char *dev, const char *dev_type, const char *dev_node,
-                 bool ipv6, bool ipv6_explicitly_supported, bool dynamic,
+                 bool ipv6_explicitly_supported, bool dynamic,
                  struct tuntap *tt)
 {
   char tunname[256];
   char dynamic_name[256];
   bool dynamic_opened = false;
 
-  ipv6_support (ipv6, ipv6_explicitly_supported, tt);
+
+  if ( tt->ipv6 && ! ipv6_explicitly_supported )
+    msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
 
   if (tt->type == DEV_TYPE_NULL)
     {
@@ -1267,16 +1286,16 @@ close_tun_generic (struct tuntap *tt)
 #if !PEDANTIC
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   struct ifreq ifr;
 
-  /*
-   * Set tt->ipv6 to true if
-   * (a) we have the capability of supporting --tun-ipv6, and
-   * (b) --tun-ipv6 was specified.
+  /* warn if a very old linux version is used & --tun-ipv6 set
    */
-  ipv6_support (ipv6, LINUX_IPV6, tt);
+#if LINUX_IPV6 == 0
+  if ( tt->ipv6 )
+    msg (M_WARN, "NOTE: explicit support for IPv6 tun devices is not provided for this OS");
+#endif
 
   /*
    * We handle --dev null specially, we do not open /dev/null for this.
@@ -1388,13 +1407,13 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
       close (tt->fd);
       tt->fd = -1;
     }
-  open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 }
 
 #else
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   ASSERT (0);
 }
@@ -1404,9 +1423,9 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 #else
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
-  open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 }
 
 #endif /* HAVE_LINUX_IF_TUN_H */
@@ -1426,7 +1445,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 #endif
 
 void
-tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, int persist_mode, const char *username, const char *groupname, const struct tuntap_options *options)
+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)
 {
   struct tuntap *tt;
 
@@ -1434,7 +1453,7 @@ tuncfg (const char *dev, const char *dev_type, const char *dev_node, bool ipv6,
   clear_tuntap (tt);
   tt->type = dev_type_enum (dev, dev_type);
   tt->options = *options;
-  open_tun (dev, dev_type, dev_node, ipv6, tt);
+  open_tun (dev, 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)
@@ -1577,7 +1596,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, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, 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;
@@ -1592,7 +1611,6 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
    * http://www.whiteboard.ne.jp/~admin2/tuntap/
    * has IPv6 support
    */
-  ipv6_support (ipv6, true, tt);
   memset(&ifr, 0x0, sizeof(ifr));
 
   if (tt->type == DEV_TYPE_NULL)
@@ -1869,9 +1887,9 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
  */
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
   /* Enable multicast on the interface */
   if (tt->fd >= 0)
@@ -1974,12 +1992,12 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
  */
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
 #ifdef NETBSD_MULTI_AF
-    open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+    open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 #else
-    open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+    open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 #endif
 
     if (tt->fd >= 0)
@@ -2096,9 +2114,9 @@ freebsd_modify_read_write_return (int len)
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
   if (tt->fd >= 0 && tt->type == DEV_TYPE_TUN)
     {
@@ -2184,9 +2202,9 @@ dragonfly_modify_read_write_return (int len)
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
-  open_tun_generic (dev, dev_type, dev_node, ipv6, true, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, true, true, tt);
 
   if (tt->fd >= 0)
     {
@@ -2255,6 +2273,61 @@ read_tun (struct tuntap* tt, uint8_t *buf, int len)
     return read (tt->fd, buf, len);
 }
 
+#elif defined(TARGET_DARWIN)
+
+/* Darwin (MacOS X) is mostly "just use the generic stuff", but there
+ * is always one caveat...:
+ *
+ * If IPv6 is configured, and the tun device is closed, the IPv6 address
+ * configured to the tun interface changes to a lingering /128 route
+ * pointing to lo0.  Need to unconfigure...  (observed on 10.5)
+ */
+
+void
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
+{
+  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
+}
+
+void
+close_tun (struct tuntap* tt)
+{
+  if (tt)
+    {
+      struct gc_arena gc = gc_new ();
+      struct argv argv;
+      argv_init (&argv);
+
+      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
+       {
+         const char * ifconfig_ipv6_local =
+                               print_in6_addr (tt->local_ipv6, 0, &gc);
+
+          argv_printf (&argv, "%s delete -inet6 %s",
+                              ROUTE_PATH, ifconfig_ipv6_local );
+         argv_msg (M_INFO, &argv);
+         openvpn_execve_check (&argv, NULL, 0, "MacOS X 'remove inet6 route' failed (non-critical)");
+       }
+
+      close_tun_generic (tt);
+      free (tt);
+      argv_reset (&argv);
+      gc_free (&gc);
+    }
+}
+
+int
+write_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+  return write (tt->fd, buf, len);
+}
+
+int
+read_tun (struct tuntap* tt, uint8_t *buf, int len)
+{
+  return read (tt->fd, buf, len);
+}
+
 #elif defined(WIN32)
 
 int
@@ -4240,7 +4313,7 @@ fork_register_dns_action (struct tuntap *tt)
 }
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
   struct gc_arena gc = gc_new ();
   char device_path[256];
@@ -4251,7 +4324,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 
   /*netcmd_semaphore_lock ();*/
 
-  ipv6_support (ipv6, false, tt);
+  msg( M_INFO, "open_tun, tt->ipv6=%d", tt->ipv6 );
 
   if (tt->type == DEV_TYPE_NULL)
     {
@@ -4697,6 +4770,12 @@ close_tun (struct tuntap *tt)
 
   if (tt)
     {
+      if ( tt->ipv6 && tt->did_ifconfig_ipv6_setup )
+        {
+         /* netsh interface ipv6 delete address \"%s\" %s */
+         const char * ifconfig_ipv6_local = print_in6_addr (tt->local_ipv6, 0, &gc);
+         msg( M_WARN, "TODO: remove IPv6 address %s", ifconfig_ipv6_local );
+       }
 #if 1
       if (tt->ipapi_context_defined)
        {
@@ -4800,9 +4879,9 @@ ipset2ascii_all (struct gc_arena *gc)
 #else /* generic */
 
 void
-open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6, struct tuntap *tt)
+open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
 {
-  open_tun_generic (dev, dev_type, dev_node, ipv6, false, true, tt);
+  open_tun_generic (dev, dev_type, dev_node, false, true, tt);
 }
 
 void