From e9178b2e6e10100cc26751e8240d8e1cdd936fe5 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 1 Mar 2019 02:05:13 +0100 Subject: tunnel: solve "the tunnel routing problem" on windows This incredible trick lets roaming work and also keeps our routing rules significantly simpler. --- service/service_tunnel.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'service/service_tunnel.go') diff --git a/service/service_tunnel.go b/service/service_tunnel.go index f600aa36..c3ddf9e4 100644 --- a/service/service_tunnel.go +++ b/service/service_tunnel.go @@ -7,12 +7,14 @@ package service import ( "bufio" + "encoding/binary" "golang.org/x/sys/windows" "golang.zx2c4.com/winipcfg" "log" "net" "os" "strings" + "unsafe" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/debug" @@ -45,6 +47,12 @@ type tunnelService struct { debug bool } +func htonl(val uint32) uint32 { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, val) + return *(*uint32)(unsafe.Pointer(&bytes[0])) +} + func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { changes <- svc.Status{State: svc.StartPending} @@ -129,6 +137,32 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, } ipcSetOperation(device, bufio.NewReader(strings.NewReader(uapiConf))) + //TODO: This needs more error checking and to be done in a notifier whenever the default route changes. + defaultV4, err := winipcfg.DefaultInterface(winipcfg.AF_INET) + if err == nil { + sysconn, _ := device.net.bind.(*NativeBind).ipv4.SyscallConn() + sysconn.Control(func(fd uintptr) { + err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, 31 /* IP_UNICAST_IF */, int(htonl(defaultV4.Index))) + if err != nil { + logger.Error.Println("Failed to set IPv4 default interface on socket:", err) + } + }) + } else { + logger.Error.Println("Unable to determine default IPv4 interface:", err) + } + defaultV6, err := winipcfg.DefaultInterface(winipcfg.AF_INET6) + if err == nil { + sysconn, _ := device.net.bind.(*NativeBind).ipv6.SyscallConn() + sysconn.Control(func(fd uintptr) { + err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, 31 /* IPV6_UNICAST_IF */, int(defaultV6.Ipv6IfIndex)) + if err != nil { + logger.Error.Println("Failed to set IPv6 default interface on socket:", err) + } + }) + } else { + logger.Error.Println("Unable to determine default IPv6 interface:", err) + } + guid := wintun.(*tun.NativeTun).GUID() iface, err := winipcfg.InterfaceFromGUID(&guid) if err != nil { -- cgit v1.2.3-59-g8ed1b