diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-11-08 20:23:42 +0100 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2021-11-08 20:32:38 +0100 |
commit | b1fc806ad0a845a4f4c8404cace4678ca8b7b702 (patch) | |
tree | d965c2cb5985c10302810f1c7807920e7d2f0fb9 /tunnel | |
parent | build: bump to go 1.17.3 (diff) | |
download | wireguard-windows-b1fc806ad0a845a4f4c8404cace4678ca8b7b702.tar.xz wireguard-windows-b1fc806ad0a845a4f4c8404cace4678ca8b7b702.zip |
tunnel: check for WeakHostSend/Forwarding in pitfalls
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'tunnel')
-rw-r--r-- | tunnel/interfacewatcher.go | 1 | ||||
-rw-r--r-- | tunnel/pitfalls.go | 177 | ||||
-rw-r--r-- | tunnel/service.go | 2 |
3 files changed, 180 insertions, 0 deletions
diff --git a/tunnel/interfacewatcher.go b/tunnel/interfacewatcher.go index f3dbc46a..08e8936a 100644 --- a/tunnel/interfacewatcher.go +++ b/tunnel/interfacewatcher.go @@ -80,6 +80,7 @@ func (iw *interfaceWatcher) setup(family winipcfg.AddressFamily) { iw.errors <- interfaceWatcherError{services.ErrorSetNetConfig, err} return } + evaluateDynamicPitfalls(family, iw.conf, iw.luid) iw.started <- family } diff --git a/tunnel/pitfalls.go b/tunnel/pitfalls.go new file mode 100644 index 00000000..6ac0f725 --- /dev/null +++ b/tunnel/pitfalls.go @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved. + */ + +package tunnel + +import ( + "log" + "strings" + "unsafe" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc/mgr" + "golang.zx2c4.com/go118/netip" + "golang.zx2c4.com/wireguard/windows/conf" + "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" +) + +func evaluateStaticPitfalls() { + go func() { + pitfallDnsCacheDisabled() + pitfallVirtioNetworkDriver() + }() +} + +func evaluateDynamicPitfalls(family winipcfg.AddressFamily, conf *conf.Config, luid winipcfg.LUID) { + go func() { + pitfallWeakHostSend(family, conf, luid) + }() +} + +func pitfallDnsCacheDisabled() { + scm, err := mgr.Connect() + if err != nil { + return + } + defer scm.Disconnect() + svc := mgr.Service{Name: "dnscache"} + svc.Handle, err = windows.OpenService(scm.Handle, windows.StringToUTF16Ptr(svc.Name), windows.SERVICE_QUERY_CONFIG) + if err != nil { + return + } + defer svc.Close() + cfg, err := svc.Config() + if err != nil { + return + } + if cfg.StartType != mgr.StartDisabled { + return + } + + log.Printf("Warning: the %q (dnscache) service is disabled; please re-enable it", cfg.DisplayName) +} + +func pitfallVirtioNetworkDriver() { + var modules []windows.RTL_PROCESS_MODULE_INFORMATION + for bufferSize := uint32(128 * 1024); ; { + moduleBuffer := make([]byte, bufferSize) + err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize) + switch err { + case windows.STATUS_INFO_LENGTH_MISMATCH: + continue + case nil: + break + default: + return + } + mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0])) + modules = unsafe.Slice(&mods.Modules[0], mods.NumberOfModules) + break + } + for i := range modules { + if !strings.EqualFold(windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:]), "netkvm.sys") { + continue + } + driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:]) + var zero windows.Handle + infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero) + if err != nil { + return + } + versionInfo := make([]byte, infoSize) + err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0])) + if err != nil { + return + } + var fixedInfo *windows.VS_FIXEDFILEINFO + fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo)) + err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen) + if err != nil { + return + } + const minimumPlausibleVersion = 40 << 48 + const minimumGoodVersion = (100 << 48) | (85 << 32) | (104 << 16) | (20800 << 0) + version := (uint64(fixedInfo.FileVersionMS) << 32) | uint64(fixedInfo.FileVersionLS) + if version >= minimumGoodVersion || version < minimumPlausibleVersion { + return + } + log.Println("Warning: the VirtIO network driver (NetKVM) is out of date and may cause known problems; please update to v100.85.104.20800 or later") + return + } +} + +func pitfallWeakHostSend(family winipcfg.AddressFamily, conf *conf.Config, ourLUID winipcfg.LUID) { + routingTable, err := winipcfg.GetIPForwardTable2(family) + if err != nil { + return + } + type endpointRoute struct { + addr netip.Addr + name string + lowestMetric uint32 + highestCIDR uint8 + weakHostSend bool + finalIsOurs bool + } + endpoints := make([]endpointRoute, 0, len(conf.Peers)) + for _, peer := range conf.Peers { + addr, err := netip.ParseAddr(peer.Endpoint.Host) + if err != nil || (addr.Is4() && family != windows.AF_INET) || (addr.Is6() && family != windows.AF_INET6) { + continue + } + endpoints = append(endpoints, endpointRoute{addr: addr, lowestMetric: ^uint32(0)}) + } + for i := range routingTable { + var ( + ifrow *winipcfg.MibIfRow2 + ifacerow *winipcfg.MibIPInterfaceRow + metric uint32 + ) + for j := range endpoints { + r, e := &routingTable[i], &endpoints[j] + if r.DestinationPrefix.PrefixLength < e.highestCIDR { + continue + } + if !r.DestinationPrefix.Prefix().Contains(e.addr) { + continue + } + if ifrow == nil { + ifrow, err = r.InterfaceLUID.Interface() + if err != nil { + continue + } + } + if ifrow.OperStatus != winipcfg.IfOperStatusUp { + continue + } + if ifacerow == nil { + ifacerow, err = r.InterfaceLUID.IPInterface(family) + if err != nil { + continue + } + metric = r.Metric + ifacerow.Metric + } + if r.DestinationPrefix.PrefixLength == e.highestCIDR && metric > e.lowestMetric { + continue + } + e.lowestMetric = metric + e.highestCIDR = r.DestinationPrefix.PrefixLength + e.finalIsOurs = r.InterfaceLUID == ourLUID + if !e.finalIsOurs { + e.name = ifrow.Alias() + e.weakHostSend = ifacerow.ForwardingEnabled || ifacerow.WeakHostSend + } + } + } + problematicInterfaces := make(map[string]bool, len(endpoints)) + for _, e := range endpoints { + if e.weakHostSend && e.finalIsOurs { + problematicInterfaces[e.name] = true + } + } + for iface := range problematicInterfaces { + log.Printf("Warning: the %q interface has Forwarding/WeakHostSend enabled, which will cause routing loops", iface) + } +} diff --git a/tunnel/service.go b/tunnel/service.go index 4a4f5e40..374d71d3 100644 --- a/tunnel/service.go +++ b/tunnel/service.go @@ -135,6 +135,8 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest, } } + evaluateStaticPitfalls() + log.Println("Watching network interfaces") watcher, err = watchInterface() if err != nil { |