[Openvpn-devel] Fix IPv6 configuration on Windows using netsh

Message ID 20191127134258.9162-1-l@libres.ch
State Changes Requested
Headers show
Series [Openvpn-devel] Fix IPv6 configuration on Windows using netsh | expand

Commit Message

Laurent Fasnacht Nov. 27, 2019, 2:43 a.m. UTC
Apparently, `netsh interface ipv6 set address ...` defaults to using
a subnet of /64, and therefore adds an onlink route of that size.

When using a tun tunnel, the tap adapter only replies to neighbor
discovery packets for fe80::8. This leads to the unfortunate situation
where all the hosts in the /64 are not reachable.

This patch fixes that situation by specifying a /128 netmask, as the real
route is added afterwards, via the gateway.
---
 src/openvpn/tun.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Comments

Arne Schwabe April 20, 2020, 2:28 a.m. UTC | #1
Am 27.11.19 um 14:43 schrieb Laurent Fasnacht:
> Apparently, `netsh interface ipv6 set address ...` defaults to using
> a subnet of /64, and therefore adds an onlink route of that size.
> 
> When using a tun tunnel, the tap adapter only replies to neighbor
> discovery packets for fe80::8. This leads to the unfortunate situation
> where all the hosts in the /64 are not reachable.
> 
> This patch fixes that situation by specifying a /128 netmask, as the real
> route is added afterwards, via the gateway.
> ---
>  src/openvpn/tun.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
> index 599fd817..8b054c80 100644
> --- a/src/openvpn/tun.c
> +++ b/src/openvpn/tun.c
> @@ -982,15 +982,15 @@ do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu,
>      else
>      {
>          /* example: netsh interface ipv6 set address interface=42
> -         *                  2001:608:8003::d store=active
> +         *                  2001:608:8003::d/128 store=active
>           */
>          char iface[64];
>  
>          openvpn_snprintf(iface, sizeof(iface), "interface=%lu",
>                           tt->adapter_index);
> -        argv_printf(&argv, "%s%sc interface ipv6 set address %s %s store=active",
> +        argv_printf(&argv, "%s%sc interface ipv6 set address %s %s/%d store=active",
>                      get_win_sys_path(), NETSH_PATH_SUFFIX, iface,
> -                    ifconfig_ipv6_local);
> +                    ifconfig_ipv6_local, 128);
>          netsh_command(&argv, 4, M_FATAL);
>          /* set ipv6 dns servers if any are specified */
>          netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, ifname);

Instead changing the implicit /64 to an explicit /128, wouldn't it be
better to use the correct tt->netbits_ipv6 like on other platforms?

Arne
Gert Doering April 20, 2020, 2:40 a.m. UTC | #2
Hi,

On Mon, Apr 20, 2020 at 02:28:28PM +0200, Arne Schwabe wrote:
> Am 27.11.19 um 14:43 schrieb Laurent Fasnacht:
> > Apparently, `netsh interface ipv6 set address ...` defaults to using
> > a subnet of /64, and therefore adds an onlink route of that size.

Ah, there the patch is.  I was looking for it recently and could not
find it...

> Instead changing the implicit /64 to an explicit /128, wouldn't it be
> better to use the correct tt->netbits_ipv6 like on other platforms?

No, due to the way we hacked ND in tun mode.

For Windows, this is still a multiaccess network, but the TAP driver
will only reply to ND queries for his gateway address (fe80::8).  So
if you use the correct netmask, Windows will send NDs for all hosts
in the /64 or /80 or whatnot, which will fail - no ND on the tun
interface, and nothing in the driver to translate.

(Now, my "tap-to-tun adapter inside OpenVPN" would do this, because
AIX only has tap - but that's still unfinished).


Laurent: I'll see that I can get this fully understood, tested and merged.

Sorry for the delay.

gert
Gert Doering Sept. 14, 2020, 3:55 a.m. UTC | #3
Hi,

On Wed, Nov 27, 2019 at 01:43:38PM +0000, Laurent Fasnacht wrote:
> Apparently, `netsh interface ipv6 set address ...` defaults to using
> a subnet of /64, and therefore adds an onlink route of that size.
> 
> When using a tun tunnel, the tap adapter only replies to neighbor
> discovery packets for fe80::8. This leads to the unfortunate situation
> where all the hosts in the /64 are not reachable.
> 
> This patch fixes that situation by specifying a /128 netmask, as the real
> route is added afterwards, via the gateway.

So, coming back to this.

I tried to reproduce this in the context of 

  https://community.openvpn.net/openvpn/ticket/1054

and actually couldn't.

I tested on Win10, and both on tun and tap mode, the existing code
always configured a "/128" on-link (and then added the /64 route to
what it thought was the right gateway - fe80::8 for tun, its own
address for tap).


I will need to touch the code now anyway, as the behaviour for tap is
*wrong* (for tap, it really needs to have a /64 on-link, and do ND 
inside the network) - but if I could reproduce the issue you saw 
for tun, it would make my life easier :-)

Arne: in "dev tun" mode on windows, we can only reach a single gateway
IP (fe80::8) which is the "spoof ND replies!" thing in the tun/tap driver
- everything else only works if sent via fe80::8 -> strip ethernet header
-> send as "tun" packets.  So, /128 is correct for tun.

gert

Patch

diff --git a/src/openvpn/tun.c b/src/openvpn/tun.c
index 599fd817..8b054c80 100644
--- a/src/openvpn/tun.c
+++ b/src/openvpn/tun.c
@@ -982,15 +982,15 @@  do_ifconfig_ipv6(struct tuntap *tt, const char *ifname, int tun_mtu,
     else
     {
         /* example: netsh interface ipv6 set address interface=42
-         *                  2001:608:8003::d store=active
+         *                  2001:608:8003::d/128 store=active
          */
         char iface[64];
 
         openvpn_snprintf(iface, sizeof(iface), "interface=%lu",
                          tt->adapter_index);
-        argv_printf(&argv, "%s%sc interface ipv6 set address %s %s store=active",
+        argv_printf(&argv, "%s%sc interface ipv6 set address %s %s/%d store=active",
                     get_win_sys_path(), NETSH_PATH_SUFFIX, iface,
-                    ifconfig_ipv6_local);
+                    ifconfig_ipv6_local, 128);
         netsh_command(&argv, 4, M_FATAL);
         /* set ipv6 dns servers if any are specified */
         netsh_set_dns6_servers(tt->options.dns6, tt->options.dns6_len, ifname);