aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel/pitfalls.go
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/pitfalls.go')
-rw-r--r--tunnel/pitfalls.go177
1 files changed, 177 insertions, 0 deletions
diff --git a/tunnel/pitfalls.go b/tunnel/pitfalls.go
new file mode 100644
index 00000000..fdef6eb2
--- /dev/null
+++ b/tunnel/pitfalls.go
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved.
+ */
+
+package tunnel
+
+import (
+ "log"
+ "net/netip"
+ "strings"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/svc/mgr"
+ "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)
+ }
+}