Browse code

Handle --dhcp-option DNS6 on Windows using netsh

v2: On closing tun delete the ipv6 dns addresses (if any were set).
Also use "validate=no" only in Windows 7 and higher where it is
supported. Its used to skip the time consuming automatic address
validation which is on by default on those platforms.

Tested on Windows Server 2008 (i686), Win 7 (x64) and Win 10 (x64)

TODO: set dns servers using the interactive service

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1479784332-21680-1-git-send-email-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg13193.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Selva Nair authored on 2016/11/22 12:12:12
Showing 4 changed files
... ...
@@ -5634,13 +5634,12 @@ this option to set secondary DNS server addresses.
5634 5634
 Set primary domain name server IPv6 address.  Repeat
5635 5635
 this option to set secondary DNS server IPv6 addresses.
5636 5636
 
5637
-Note: currently this is somewhat of a placeholder option - it is
5638
-understood, but OpenVPN has no code to tell Windows about it (the
5637
+Note: currently this is handled using netsh and requires admin rights (the
5639 5638
 existing DHCP code can only do IPv4 DHCP, and that protocol only
5640 5639
 permits IPv4 addresses anywhere).  The option will be put into the
5641 5640
 environment, so an
5642 5641
 .B \-\-up
5643
-script could act upon it.
5642
+script could act upon it if needed.
5644 5643
 
5645 5644
 .B WINS addr \-\-
5646 5645
 Set primary WINS server address (NetBIOS over TCP/IP Name Server).
... ...
@@ -6407,13 +6407,19 @@ add_option (struct options *options,
6407 6407
 	{
6408 6408
 	  dhcp_option_address_parse ("DNS", p[2], o->dns, &o->dns_len, msglevel);
6409 6409
 	}
6410
-      else if (streq (p[1], "DNS6") && p[2])
6410
+      else if (streq (p[1], "DNS6") && p[2] && ipv6_addr_safe(p[2]))
6411 6411
 	{
6412
-	  /* this is somewhat of a placeholder - we understand the option,
6413
-	   * but cannot act upon it - so we'll just accept it and put it
6414
-	   * into the environment, as we would do on all non-win32 platforms
6415
-	   */
6412
+	  struct in6_addr addr;
6416 6413
 	  foreign_option (options, p, 3, es);
6414
+	  if (o->dns6_len >= N_DHCP_ADDR)
6415
+	    {
6416
+	      msg (msglevel, "--dhcp-option DNS6: maximum of %d dns servers can be specified",
6417
+		   N_DHCP_ADDR);
6418
+	    }
6419
+	  else if (get_ipv6_addr (p[2], &addr, NULL, msglevel))
6420
+	    {
6421
+	      o->dns6[o->dns6_len++] = addr;
6422
+	    }
6417 6423
 	}
6418 6424
       else if (streq (p[1], "WINS") && p[2])
6419 6425
 	{
... ...
@@ -68,6 +68,9 @@ static void netsh_ifconfig (const struct tuntap_options *to,
68 68
 			    const in_addr_t ip,
69 69
 			    const in_addr_t netmask,
70 70
 			    const unsigned int flags);
71
+static void netsh_set_dns6_servers (const struct in6_addr *addr_list,
72
+				    const int addr_len,
73
+				    const char *flex_name);
71 74
 static void netsh_command (const struct argv *a, int n, int msglevel);
72 75
 
73 76
 static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
... ...
@@ -1381,6 +1384,7 @@ do_ifconfig (struct tuntap *tt,
1381 1381
 	else if (tt->options.msg_channel)
1382 1382
 	  {
1383 1383
 	    do_address_service (true, AF_INET6, tt);
1384
+	    /* TODO: do_dns6_service() */
1384 1385
 	  }
1385 1386
 	else
1386 1387
 	  {
... ...
@@ -1394,6 +1398,8 @@ do_ifconfig (struct tuntap *tt,
1394 1394
 			 iface,
1395 1395
 			 ifconfig_ipv6_local );
1396 1396
 	    netsh_command (&argv, 4, M_FATAL);
1397
+	    /* set ipv6 dns servers if any are specified */
1398
+	    netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, actual);
1397 1399
 	  }
1398 1400
 
1399 1401
 	/* explicit route needed */
... ...
@@ -4626,6 +4632,41 @@ ip_addr_member_of (const in_addr_t addr, const IP_ADDR_STRING *ias)
4626 4626
   return false;
4627 4627
 }
4628 4628
 
4629
+/**
4630
+ * Set the ipv6 dns servers on the specified interface.
4631
+ * The list of dns servers currently set on the interface
4632
+ * are cleared first.
4633
+ * No action is taken if number of addresses (addr_len) < 1.
4634
+ */
4635
+static void
4636
+netsh_set_dns6_servers (const struct in6_addr *addr_list,
4637
+			const int addr_len,
4638
+			const char *flex_name)
4639
+{
4640
+    struct gc_arena gc = gc_new ();
4641
+    struct argv argv = argv_new ();
4642
+
4643
+    for (int i = 0; i < addr_len; ++i)
4644
+    {
4645
+	const char *fmt = (i == 0) ?
4646
+	    "%s%sc interface ipv6 set dns %s static %s"
4647
+	    : "%s%sc interface ipv6 add dns %s %s";
4648
+	argv_printf (&argv, fmt, get_win_sys_path(),
4649
+		     NETSH_PATH_SUFFIX, flex_name,
4650
+		     print_in6_addr (addr_list[i], 0, &gc));
4651
+
4652
+	/* disable slow address validation on Windows 7 and higher */
4653
+	if (win32_version_info() >= WIN_7)
4654
+	    argv_printf_cat (&argv, "%s", "validate=no");
4655
+
4656
+	/* Treat errors while adding as non-fatal as we do not check for duplicates */
4657
+	netsh_command (&argv, 1, (i==0)? M_FATAL : M_NONFATAL);
4658
+    }
4659
+
4660
+    argv_reset (&argv);
4661
+    gc_free (&gc);
4662
+}
4663
+
4629 4664
 static void
4630 4665
 netsh_ifconfig_options (const char *type,
4631 4666
 			const in_addr_t *addr_list,
... ...
@@ -5572,6 +5613,17 @@ close_tun (struct tuntap *tt)
5572 5572
                            ifconfig_ipv6_local);
5573 5573
 
5574 5574
               netsh_command (&argv, 1, M_WARN);
5575
+
5576
+	      /* delete ipv6 dns servers if any were set */
5577
+	      if (tt->options.dns6_len > 0)
5578
+		{
5579
+		  argv_printf (&argv,
5580
+			       "%s%sc interface ipv6 delete dns %s all",
5581
+			       get_win_sys_path(),
5582
+			       NETSH_PATH_SUFFIX,
5583
+			       tt->actual_name);
5584
+		  netsh_command (&argv, 1, M_WARN);
5585
+		}
5575 5586
               argv_reset (&argv);
5576 5587
             }
5577 5588
 	}
... ...
@@ -107,6 +107,9 @@ struct tuntap_options {
107 107
   bool dhcp_release;
108 108
 
109 109
   bool register_dns;
110
+
111
+  struct in6_addr dns6[N_DHCP_ADDR];
112
+  int dns6_len;
110 113
 };
111 114
 
112 115
 #elif TARGET_LINUX