aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--service/ifaceconfig.go184
-rw-r--r--service/service_tunnel.go126
2 files changed, 188 insertions, 122 deletions
diff --git a/service/ifaceconfig.go b/service/ifaceconfig.go
new file mode 100644
index 00000000..c405939c
--- /dev/null
+++ b/service/ifaceconfig.go
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ */
+
+package service
+
+import (
+ "errors"
+ "golang.org/x/sys/windows"
+ "golang.zx2c4.com/winipcfg"
+ "golang.zx2c4.com/wireguard/windows/conf"
+ "net"
+ "os"
+)
+
+const (
+ sockoptIP_UNICAST_IF = 31
+ sockoptIPV6_UNICAST_IF = 31
+)
+
+func bindSocketToMonitoredDefault(bind *NativeBind) error {
+ defaultIface, err := winipcfg.DefaultInterface(winipcfg.AF_INET)
+ if err != nil {
+ return err
+ }
+ 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, int(htonl(defaultIface.Index)))
+ })
+ if err2 != nil {
+ return err2
+ }
+ if err != nil {
+ return err
+ }
+ defaultIface, err = winipcfg.DefaultInterface(winipcfg.AF_INET6)
+ if err != nil {
+ return err
+ }
+ sysconn, err = bind.ipv6.SyscallConn()
+ if err != nil {
+ return err
+ }
+ err2 = sysconn.Control(func(fd uintptr) {
+ err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptIPV6_UNICAST_IF, int(defaultIface.Ipv6IfIndex))
+ })
+ if err2 != nil {
+ return err2
+ }
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+ //TODO: monitor for changes, and make sure we're using default modulo us
+}
+
+func configureInterface(conf *conf.Config, guid *windows.GUID) error {
+ iface, err := winipcfg.InterfaceFromGUID(guid)
+ if err != nil {
+ return err
+ }
+
+ routeCount := len(conf.Interface.Addresses)
+ for _, peer := range conf.Peers {
+ routeCount += len(peer.AllowedIPs)
+ }
+ routes := make([]winipcfg.RouteData, routeCount)
+ routeCount = 0
+ var firstGateway4 *net.IP
+ var firstGateway6 *net.IP
+ addresses := make([]*net.IPNet, len(conf.Interface.Addresses))
+ for i, addr := range conf.Interface.Addresses {
+ ipnet := addr.IPNet()
+ addresses[i] = &ipnet
+ gateway := ipnet.IP.Mask(ipnet.Mask)
+ if addr.Bits() == 32 && firstGateway4 == nil {
+ firstGateway4 = &gateway
+ } else if addr.Bits() == 128 && firstGateway6 == nil {
+ firstGateway6 = &gateway
+ }
+ routes[routeCount] = winipcfg.RouteData{
+ Destination: net.IPNet{
+ IP: gateway,
+ Mask: ipnet.Mask,
+ },
+ NextHop: gateway,
+ Metric: 0,
+ }
+ routeCount++
+ }
+
+ foundDefault4 := false
+ foundDefault6 := false
+ for _, peer := range conf.Peers {
+ for _, allowedip := range peer.AllowedIPs {
+ if (allowedip.Bits() == 32 && firstGateway4 == nil) || (allowedip.Bits() == 128 && firstGateway6 == nil) {
+ return errors.New("Due to a Windows limitation, one cannot have interface routes without an interface address")
+ }
+ routes[routeCount] = winipcfg.RouteData{
+ Destination: allowedip.IPNet(),
+ Metric: 0,
+ }
+ if allowedip.Bits() == 32 {
+ if allowedip.Cidr == 0 {
+ foundDefault4 = true
+ }
+ routes[routeCount].NextHop = *firstGateway4
+ } else if allowedip.Bits() == 128 {
+ if allowedip.Cidr == 0 {
+ foundDefault6 = true
+ }
+ routes[routeCount].NextHop = *firstGateway6
+ }
+ routeCount++
+ }
+ }
+
+ err = iface.SetAddresses(addresses)
+ if err != nil {
+ return err
+ }
+
+ err = iface.FlushRoutes()
+ if err != nil {
+ return nil
+ }
+ for _, route := range routes {
+ err = iface.AddRoute(&route, false)
+
+ //TODO: Ignoring duplicate errors like this maybe isn't very reasonable.
+ // instead we should make sure we're not adding duplicates ourselves when
+ // inserting the gateway routes.
+ if syserr, ok := err.(*os.SyscallError); ok {
+ if syserr.Err == windows.Errno(ERROR_OBJECT_ALREADY_EXISTS) {
+ err = nil
+ }
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+
+ err = iface.SetDNS(conf.Interface.Dns)
+ if err != nil {
+ return err
+ }
+
+ ipif, err := iface.GetIpInterface(winipcfg.AF_INET)
+ if err != nil {
+ return err
+ }
+ if foundDefault4 {
+ ipif.UseAutomaticMetric = false
+ ipif.Metric = 0
+ }
+ err = ipif.Set()
+ if err != nil {
+ return err
+ }
+
+ ipif, err = iface.GetIpInterface(winipcfg.AF_INET6)
+ if err != nil {
+ return err
+ }
+ if foundDefault6 {
+ ipif.UseAutomaticMetric = false
+ ipif.Metric = 0
+ }
+ ipif.DadTransmits = 0
+ ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
+ err = ipif.Set()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/service/service_tunnel.go b/service/service_tunnel.go
index 72fb7ca0..02be70aa 100644
--- a/service/service_tunnel.go
+++ b/service/service_tunnel.go
@@ -9,11 +9,7 @@ import (
"bufio"
"encoding/binary"
"fmt"
- "golang.org/x/sys/windows"
- "golang.zx2c4.com/winipcfg"
"log"
- "net"
- "os"
"strings"
"unsafe"
@@ -144,131 +140,17 @@ 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)
+ err = bindSocketToMonitoredDefault(device.net.bind.(*NativeBind))
if err != nil {
- logger.Error.Println("Unable to find Wintun device:", err)
+ logger.Error.Println("Unable to bind sockets to default route:", err)
changes <- svc.Status{State: svc.StopPending}
exitCode = ERROR_NETWORK_BUSY
device.Close()
return
}
- routeCount := len(conf.Interface.Addresses)
- for _, peer := range conf.Peers {
- routeCount += len(peer.AllowedIPs)
- }
- routes := make([]winipcfg.RouteData, routeCount)
- routeCount = 0
- var firstGateway *net.IP
- addresses := make([]*net.IPNet, len(conf.Interface.Addresses))
- for i, addr := range conf.Interface.Addresses {
- ipnet := addr.IPNet()
- addresses[i] = &ipnet
- gateway := ipnet.IP.Mask(ipnet.Mask)
- if firstGateway == nil {
- firstGateway = &gateway
- }
- routes[routeCount] = winipcfg.RouteData{
- Destination: net.IPNet{
- IP: gateway,
- Mask: ipnet.Mask,
- },
- NextHop: gateway,
- Metric: 0,
- }
- routeCount++
- }
-
- foundDefault := false
- for _, peer := range conf.Peers {
- for _, allowedip := range peer.AllowedIPs {
- routes[routeCount] = winipcfg.RouteData{
- Destination: allowedip.IPNet(),
- NextHop: *firstGateway,
- Metric: 0,
- }
- if allowedip.Cidr == 0 {
- foundDefault = true
- }
- routeCount++
- }
- }
-
- err = iface.SetAddresses(addresses)
- if err == nil {
- err = iface.FlushRoutes()
- if err == nil {
- for _, route := range routes {
- err = iface.AddRoute(&route, false)
-
- //TODO: Ignoring duplicate errors like this maybe isn't very reasonable.
- // instead we should make sure we're not adding duplicates ourselves when
- // inserting the gateway routes.
- if syserr, ok := err.(*os.SyscallError); ok {
- if syserr.Err == windows.Errno(ERROR_OBJECT_ALREADY_EXISTS) {
- err = nil
- }
- }
-
- if err != nil {
- break
- }
- }
- }
- }
- if err == nil {
- err = iface.SetDNS(conf.Interface.Dns)
- }
- if err == nil {
- ipif, err := iface.GetIpInterface(winipcfg.AF_INET)
- if err == nil {
- if foundDefault {
- ipif.UseAutomaticMetric = false
- ipif.Metric = 0
- }
- err = ipif.Set()
- }
- }
- if err == nil {
- ipif, err := iface.GetIpInterface(winipcfg.AF_INET6)
- if err == nil {
- if foundDefault {
- ipif.UseAutomaticMetric = false
- ipif.Metric = 0
- }
- ipif.DadTransmits = 0
- ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
- ipif.LinkLocalAddressBehavior = winipcfg.LinkLocalAlwaysOff
- err = ipif.Set()
- }
- }
+ 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)
changes <- svc.Status{State: svc.StopPending}