diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-03-02 05:18:40 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2019-03-02 05:18:40 +0100 |
commit | 7d36922a3cb9d77e9c4ba4db3f70b34bc65b27dd (patch) | |
tree | e66ffb1e2b23aeba9f54c8f10eeaf02cdaa355cb /service | |
parent | ifaceconfig: allow for null defaults (diff) | |
download | wireguard-windows-7d36922a3cb9d77e9c4ba4db3f70b34bc65b27dd.tar.xz wireguard-windows-7d36922a3cb9d77e9c4ba4db3f70b34bc65b27dd.zip |
ifaceconfig: monitor for changes to default interface
Diffstat (limited to 'service')
-rw-r--r-- | service/ifaceconfig.go | 85 | ||||
-rw-r--r-- | service/service_tunnel.go | 6 |
2 files changed, 49 insertions, 42 deletions
diff --git a/service/ifaceconfig.go b/service/ifaceconfig.go index 71cb7e5b..f8737128 100644 --- a/service/ifaceconfig.go +++ b/service/ifaceconfig.go @@ -21,20 +21,36 @@ const ( sockoptIPV6_UNICAST_IF = 31 ) -func htonl(val int) int { +func htonl(val uint32) uint32 { bytes := make([]byte, 4) - binary.BigEndian.PutUint32(bytes, uint32(val)) - return int(*(*uint32)(unsafe.Pointer(&bytes[0]))) + binary.BigEndian.PutUint32(bytes, val) + return *(*uint32)(unsafe.Pointer(&bytes[0])) } -func bindSocketRoutes(bind *NativeBind, index4 int, index6 int) error { - if index4 != -1 { +func bindSocketRoute(family winipcfg.AddressFamily, bind *NativeBind, ourLuid uint64) error { + routes, err := winipcfg.GetRoutes(family) + if err != nil { + return err + } + lowestMetric := ^uint32(0) + index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want. + for _, route := range routes { + if route.DestinationPrefix.PrefixLength != 0 || route.InterfaceLuid == ourLuid { + continue + } + if route.Metric < lowestMetric { + lowestMetric = route.Metric + index = route.InterfaceIndex + } + } + + if family == winipcfg.AF_INET { sysconn, err := bind.ipv4.SyscallConn() if err != nil { return err } err2 := sysconn.Control(func(fd uintptr) { - err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, htonl(index4)) + err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptIP_UNICAST_IF, int(htonl(index))) }) if err2 != nil { return err2 @@ -42,16 +58,15 @@ func bindSocketRoutes(bind *NativeBind, index4 int, index6 int) error { if err != nil { return err } - } - - if index6 != -1 { + return nil + } else if family == winipcfg.AF_INET6 { sysconn, err := bind.ipv6.SyscallConn() if err != nil { return err } err2 := sysconn.Control(func(fd uintptr) { // The lack of htonl here is not a bug. MSDN actually specifies big endian for one and little endian for the other. - err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, index6) + err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(index)) }) if err2 != nil { return err2 @@ -60,46 +75,36 @@ func bindSocketRoutes(bind *NativeBind, index4 int, index6 int) error { return err } } - return nil - } -func getDefaultInterfaces() (index4 int, index6 int, err error) { - //TODO: this should be expanded to be able to exclude our current interface index - - index4 = -1 - index6 = -1 - defaultIface, err := winipcfg.DefaultInterface(winipcfg.AF_INET) +func monitorDefaultRoutes(bind *NativeBind, guid *windows.GUID) (*winipcfg.RouteChangeCallback, error) { + ourLuid, err := winipcfg.InterfaceGuidToLuid(guid) if err != nil { - return -1, -1, err + return nil, err } - if defaultIface != nil { - index4 = int(defaultIface.Index) - } - - defaultIface, err = winipcfg.DefaultInterface(winipcfg.AF_INET6) - if err != nil { - return -1, -1, err - } - if defaultIface != nil { - index6 = int(defaultIface.Ipv6IfIndex) + doIt := func() error { + err = bindSocketRoute(winipcfg.AF_INET, bind, ourLuid) + if err != nil { + return err + } + err = bindSocketRoute(winipcfg.AF_INET6, bind, ourLuid) + if err != nil { + return err + } + return nil } - return -} - -func monitorDefaultRoutes(bind *NativeBind) error { - index4, index6, err := getDefaultInterfaces() + err = doIt() if err != nil { - return err + return nil, err } - err = bindSocketRoutes(bind, index4, index6) + cb, err := winipcfg.RegisterRouteChangeCallback(func(notificationType winipcfg.MibNotificationType, route *winipcfg.Route) { + _ = doIt() + }) if err != nil { - return err + return nil, err } - - return nil - //TODO: monitor for changes, and make sure we're using default modulo us + return cb, nil } func configureInterface(conf *conf.Config, guid *windows.GUID) error { diff --git a/service/service_tunnel.go b/service/service_tunnel.go index 750a4302..12a0d709 100644 --- a/service/service_tunnel.go +++ b/service/service_tunnel.go @@ -8,6 +8,7 @@ package service import ( "bufio" "fmt" + "golang.zx2c4.com/winipcfg" "log" "strings" @@ -131,8 +132,9 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, return } ipcSetOperation(device, bufio.NewReader(strings.NewReader(uapiConf))) + guid := wintun.(*tun.NativeTun).GUID() - err = monitorDefaultRoutes(device.net.bind.(*NativeBind)) + routeMonitorCallback, err := monitorDefaultRoutes(device.net.bind.(*NativeBind), &guid) if err != nil { logger.Error.Println("Unable to bind sockets to default route:", err) changes <- svc.Status{State: svc.StopPending} @@ -141,7 +143,6 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, return } - guid := wintun.(*tun.NativeTun).GUID() err = configureInterface(conf, &guid) if err != nil { logger.Error.Println("Unable to set interface addresses, routes, DNS, or IP settings:", err) @@ -174,6 +175,7 @@ loop: changes <- svc.Status{State: svc.StopPending} logger.Info.Println("Shutting down") + winipcfg.UnregisterRouteChangeCallback(routeMonitorCallback) uapi.Close() device.Close() return |