From 6e48647318edad7540c4e99be5aed10ac898893f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 17 Jun 2019 13:08:13 +0200 Subject: tunnel: wait for IP service to attach to wintun This helps fix startup races without needing to poll, as well as reconfiguring interfaces after wintun destroys and re-adds. It also deals gracefully with IPv6 being disabled. Signed-off-by: Jason A. Donenfeld --- tunnel/ifaceconfig.go | 244 -------------------------------------------------- 1 file changed, 244 deletions(-) delete mode 100644 tunnel/ifaceconfig.go (limited to 'tunnel/ifaceconfig.go') diff --git a/tunnel/ifaceconfig.go b/tunnel/ifaceconfig.go deleted file mode 100644 index a71b612e..00000000 --- a/tunnel/ifaceconfig.go +++ /dev/null @@ -1,244 +0,0 @@ -/* SPDX-License-Identifier: MIT - * - * Copyright (C) 2019 WireGuard LLC. All Rights Reserved. - */ - -package tunnel - -import ( - "bytes" - "log" - "net" - "sort" - "time" - - "golang.org/x/sys/windows" - "golang.zx2c4.com/wireguard/tun" - - "golang.zx2c4.com/wireguard/windows/conf" - "golang.zx2c4.com/wireguard/windows/tunnel/firewall" - "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" -) - -func cleanupAddressesOnDisconnectedInterfaces(addresses []net.IPNet) { - if len(addresses) == 0 { - return - } - includedInAddresses := func(a net.IPNet) bool { - // TODO: this makes the whole algorithm O(n^2). But we can't stick net.IPNet in a Go hashmap. Bummer! - for _, addr := range addresses { - ip := addr.IP - if ip4 := ip.To4(); ip4 != nil { - ip = ip4 - } - mA, _ := addr.Mask.Size() - mB, _ := a.Mask.Size() - if bytes.Equal(ip, a.IP) && mA == mB { - return true - } - } - return false - } - interfaces, err := winipcfg.GetAdaptersAddresses(windows.AF_UNSPEC, winipcfg.GAAFlagDefault) - if err != nil { - return - } - for _, iface := range interfaces { - if iface.OperStatus == winipcfg.IfOperStatusUp { - continue - } - for address := iface.FirstUnicastAddress; address != nil; address = address.Next { - ip := address.Address.IP() - ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))} - if includedInAddresses(ipnet) { - log.Printf("Cleaning up stale address %s from interface '%s'", ipnet.String(), iface.FriendlyName()) - iface.LUID.DeleteIPAddress(ipnet) - } - } - } -} - -func configureInterface(conf *conf.Config, tun *tun.NativeTun) error { - luid := winipcfg.LUID(tun.LUID()) - - estimatedRouteCount := len(conf.Interface.Addresses) - for _, peer := range conf.Peers { - estimatedRouteCount += len(peer.AllowedIPs) - } - routes := make([]winipcfg.RouteData, 0, estimatedRouteCount) - 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 = append(routes, winipcfg.RouteData{ - Destination: net.IPNet{ - IP: gateway, - Mask: ipnet.Mask, - }, - NextHop: gateway, - Metric: 0, - }) - } - - 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) { - continue - } - route := winipcfg.RouteData{ - Destination: allowedip.IPNet(), - Metric: 0, - } - if allowedip.Bits() == 32 { - if allowedip.Cidr == 0 { - foundDefault4 = true - } - route.NextHop = *firstGateway4 - } else if allowedip.Bits() == 128 { - if allowedip.Cidr == 0 { - foundDefault6 = true - } - route.NextHop = *firstGateway6 - } - routes = append(routes, route) - } - } - - err := luid.SetIPAddresses(addresses) - if err == windows.ERROR_OBJECT_ALREADY_EXISTS { - cleanupAddressesOnDisconnectedInterfaces(addresses) - err = luid.SetIPAddresses(addresses) - } - if err != nil { - return err - } - - deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes)) - sort.Slice(routes, func(i, j int) bool { - return routes[i].Metric < routes[j].Metric || - bytes.Compare(routes[i].NextHop, routes[j].NextHop) == -1 || - bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP) == -1 || - bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask) == -1 - }) - for i := 0; i < len(routes); i++ { - if i > 0 && routes[i].Metric == routes[i-1].Metric && - bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) && - bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) && - bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) { - continue - } - deduplicatedRoutes = append(deduplicatedRoutes, &routes[i]) - } - - err = luid.SetRoutes(deduplicatedRoutes) - if err != nil { - return nil - } - - ipif, err := luid.IPInterface(windows.AF_INET) - if err != nil { - return err - } - if foundDefault4 { - ipif.UseAutomaticMetric = false - ipif.Metric = 0 - } - if conf.Interface.MTU > 0 { - ipif.NLMTU = uint32(conf.Interface.MTU) - tun.ForceMTU(int(ipif.NLMTU)) - } - err = ipif.Set() - if err != nil { - return err - } - - ipif, err = luid.IPInterface(windows.AF_INET6) - if err != nil && firstGateway6 != nil { - log.Printf("Is IPv6 disabled by Windows?") - return err - } else if err == nil { // People seem to like to disable IPv6, so we make this non-fatal. - if foundDefault6 { - ipif.UseAutomaticMetric = false - ipif.Metric = 0 - } - if conf.Interface.MTU > 0 { - ipif.NLMTU = uint32(conf.Interface.MTU) - } - ipif.DadTransmits = 0 - ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled - err = ipif.Set() - if err != nil { - return err - } - } - - err = luid.SetDNS(conf.Interface.DNS) - if err != nil { - return err - } - - return nil -} - -func unconfigureInterface(tun *tun.NativeTun) { - // It seems that the Windows networking stack doesn't like it when we destroy interfaces that have active - // routes, so to be certain, just remove everything before destroying. - luid := winipcfg.LUID(tun.LUID()) - luid.FlushRoutes(windows.AF_INET) - luid.FlushIPAddresses(windows.AF_INET) - luid.FlushRoutes(windows.AF_INET6) - luid.FlushIPAddresses(windows.AF_INET6) - luid.FlushDNS() - - firewall.DisableFirewall() -} - -func enableFirewall(conf *conf.Config, tun *tun.NativeTun) error { - restrictAll := false - if len(conf.Peers) == 1 { - nextallowedip: - for _, allowedip := range conf.Peers[0].AllowedIPs { - if allowedip.Cidr == 0 { - for _, b := range allowedip.IP { - if b != 0 { - continue nextallowedip - } - } - restrictAll = true - break - } - } - } - if restrictAll && len(conf.Interface.DNS) == 0 { - log.Println("Warning: no DNS server specified, despite having an allowed IPs of 0.0.0.0/0 or ::/0. There may be connectivity issues.") - } - return firewall.EnableFirewall(tun.LUID(), conf.Interface.DNS, restrictAll) -} - -func waitForFamilies(tun *tun.NativeTun) { - // TODO: This whole thing is a disgusting hack that shouldn't be neccessary. - - f := func(luid winipcfg.LUID, family winipcfg.AddressFamily, maxRetries int) { - for i := 0; i < maxRetries; i++ { - _, err := luid.IPInterface(family) - if i != maxRetries-1 && err == windows.ERROR_NOT_FOUND { - time.Sleep(time.Millisecond * 50) - continue - } - break - } - } - luid := winipcfg.LUID(tun.LUID()) - f(luid, windows.AF_INET, 100) - f(luid, windows.AF_INET6, 3) -} -- cgit v1.2.3-59-g8ed1b