From 616f56bd5d9640212a1f8beccb7b7208068d2b7c Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Thu, 28 Feb 2019 17:41:30 +0100 Subject: tunnel: make winipcfg sort of work The duplicate route thing is silly, and we currently don't do DNS or the fancy socket routing. But this is a step in the right direction, perhaps. Signed-off-by: Jason A. Donenfeld --- conf/config.go | 15 +++++++++ conf/parser.go | 8 +++-- service/errors.go | 1 + service/service_tunnel.go | 84 ++++++++++++++++++++++++++++++++++++++++------- wireguard-go | 2 +- 5 files changed, 96 insertions(+), 14 deletions(-) diff --git a/conf/config.go b/conf/config.go index a321bc0c..d802ec61 100644 --- a/conf/config.go +++ b/conf/config.go @@ -64,6 +64,21 @@ func (r *IPCidr) String() string { return fmt.Sprintf("%s/%d", r.IP.String(), r.Cidr) } +func (r *IPCidr) Bits() uint8 { + if r.IP.To4() != nil { + return 32 + } else { + return 128 + } +} + +func (r *IPCidr) IPNet() net.IPNet { + return net.IPNet{ + IP: r.IP, + Mask: net.CIDRMask(int(r.Cidr), int(r.Bits())), + } +} + func (e *Endpoint) String() string { if strings.IndexByte(e.Host, ':') > 0 { return fmt.Sprintf("[%s]:%d", e.Host, e.Port) diff --git a/conf/parser.go b/conf/parser.go index 6a397a9d..48cf88b6 100644 --- a/conf/parser.go +++ b/conf/parser.go @@ -40,17 +40,21 @@ func parseIPCidr(s string) (ipcidr *IPCidr, err error) { if addr == nil { return } + maybeV4 := addr.To4() + if maybeV4 != nil { + addr = maybeV4 + } if len(cidrStr) > 0 { err = &ParseError{"Invalid network prefix length", s} cidr, err = strconv.Atoi(cidrStr) if err != nil || cidr < 0 || cidr > 128 { return } - if cidr > 32 && addr.To4() != nil { + if cidr > 32 && maybeV4 != nil { return } } else { - if addr.To4() != nil { + if maybeV4 != nil { cidr = 32 } else { cidr = 128 diff --git a/service/errors.go b/service/errors.go index 04f0638c..e2d17ab9 100644 --- a/service/errors.go +++ b/service/errors.go @@ -17,4 +17,5 @@ const ( ERROR_SERVER_SID_MISMATCH uint32 = 0x00000274 ERROR_NETWORK_BUSY uint32 = 0x00000036 ERROR_NO_TRACKING_SERVICE uint32 = 0x00000494 + ERROR_OBJECT_ALREADY_EXISTS uint32 = 0x00001392 ) diff --git a/service/service_tunnel.go b/service/service_tunnel.go index 37e0de4c..f600aa36 100644 --- a/service/service_tunnel.go +++ b/service/service_tunnel.go @@ -7,15 +7,17 @@ package service import ( "bufio" + "golang.org/x/sys/windows" + "golang.zx2c4.com/winipcfg" "log" "net" + "os" "strings" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/debug" "golang.org/x/sys/windows/svc/eventlog" - "golang.zx2c4.com/winipcfg" "golang.zx2c4.com/wireguard/windows/conf" "golang.zx2c4.com/wireguard/windows/service/tun" ) @@ -79,9 +81,9 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, logger.Info.Println("Starting wireguard-go version", WireGuardGoVersion) logger.Debug.Println("Debug log enabled") - tun, err := tun.CreateTUN(conf.Name) + wintun, err := tun.CreateTUN(conf.Name) if err == nil { - realInterfaceName, err2 := tun.Name() + realInterfaceName, err2 := wintun.Name() if err2 == nil { conf.Name = realInterfaceName } @@ -92,7 +94,7 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, return } - device := NewDevice(tun, logger) + device := NewDevice(wintun, logger) device.Up() logger.Info.Println("Device started") @@ -127,17 +129,77 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, } ipcSetOperation(device, bufio.NewReader(strings.NewReader(uapiConf))) - //TODO: configure addresses, routes, and DNS with winipcfg - iface, err := winipcfg.InterfaceFromFriendlyName(conf.Name) + guid := wintun.(*tun.NativeTun).GUID() + iface, err := winipcfg.InterfaceFromGUID(&guid) + if err != nil { + logger.Error.Println("Unable to find Wintun device:", 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: 1, + } + routeCount++ + } + + for _, peer := range conf.Peers { + for _, allowedip := range peer.AllowedIPs { + routes[routeCount] = winipcfg.RouteData{ + Destination: allowedip.IPNet(), + NextHop: *firstGateway, + Metric: 1, + } + routeCount++ + } + } + + err = iface.SetAddresses(addresses) if err == nil { - a := make([]*net.IPNet, len(conf.Interface.Addresses)) - for i, addr := range conf.Interface.Addresses { - a[i] = &net.IPNet{addr.IP, net.CIDRMask(int(addr.Cidr), len(addr.IP))} + err = iface.FlushRoutes() + if err == nil { + for _, route := range routes { + err = iface.AddRoute(&route, true) + + //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 + } + } } - err = iface.SetAddresses(a) } if err != nil { - logger.Error.Println("Unable to setup interface addresses:", err) + logger.Error.Println("Unable to set interface addresses and routes:", err) changes <- svc.Status{State: svc.StopPending} exitCode = ERROR_NETWORK_BUSY device.Close() diff --git a/wireguard-go b/wireguard-go index 88ff67fb..d435be35 160000 --- a/wireguard-go +++ b/wireguard-go @@ -1 +1 @@ -Subproject commit 88ff67fb6f55456e46877b71aa5d33060468f95e +Subproject commit d435be35cac49af9367b2005d831d55e570c4b1b -- cgit v1.2.3-59-g8ed1b