interfaces_windows.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package netmon
  4. import (
  5. "log"
  6. "net/netip"
  7. "net/url"
  8. "strings"
  9. "syscall"
  10. "unsafe"
  11. "golang.org/x/sys/windows"
  12. "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
  13. "tailscale.com/feature/buildfeatures"
  14. "tailscale.com/tsconst"
  15. )
  16. const (
  17. fallbackInterfaceMetric = uint32(0) // Used if we cannot get the actual interface metric
  18. )
  19. func init() {
  20. likelyHomeRouterIP = likelyHomeRouterIPWindows
  21. if buildfeatures.HasUseProxy {
  22. getPAC = getPACWindows
  23. }
  24. }
  25. func likelyHomeRouterIPWindows() (ret netip.Addr, _ netip.Addr, ok bool) {
  26. rs, err := winipcfg.GetIPForwardTable2(windows.AF_INET)
  27. if err != nil {
  28. log.Printf("routerIP/GetIPForwardTable2 error: %v", err)
  29. return
  30. }
  31. var ifaceMetricCache map[winipcfg.LUID]uint32
  32. getIfaceMetric := func(luid winipcfg.LUID) (metric uint32) {
  33. if ifaceMetricCache == nil {
  34. ifaceMetricCache = make(map[winipcfg.LUID]uint32)
  35. } else if m, ok := ifaceMetricCache[luid]; ok {
  36. return m
  37. }
  38. if iface, err := luid.IPInterface(windows.AF_INET); err == nil {
  39. metric = iface.Metric
  40. } else {
  41. log.Printf("routerIP/luid.IPInterface error: %v", err)
  42. metric = fallbackInterfaceMetric
  43. }
  44. ifaceMetricCache[luid] = metric
  45. return
  46. }
  47. v4unspec := netip.IPv4Unspecified()
  48. var best *winipcfg.MibIPforwardRow2 // best (lowest metric) found so far, or nil
  49. for i := range rs {
  50. r := &rs[i]
  51. if r.Loopback || r.DestinationPrefix.PrefixLength != 0 || r.DestinationPrefix.Prefix().Addr().Unmap() != v4unspec {
  52. // Not a default route, so skip
  53. continue
  54. }
  55. ip := r.NextHop.Addr().Unmap()
  56. if !ip.IsValid() {
  57. // Not a valid gateway, so skip (won't happen though)
  58. continue
  59. }
  60. if best == nil {
  61. best = r
  62. ret = ip
  63. continue
  64. }
  65. // We can get here only if there are multiple default gateways defined (rare case),
  66. // in which case we need to calculate the effective metric.
  67. // Effective metric is sum of interface metric and route metric offset
  68. if ifaceMetricCache == nil {
  69. // If we're here it means that previous route still isn't updated, so update it
  70. best.Metric += getIfaceMetric(best.InterfaceLUID)
  71. }
  72. r.Metric += getIfaceMetric(r.InterfaceLUID)
  73. if best.Metric > r.Metric || best.Metric == r.Metric && ret.Compare(ip) > 0 {
  74. // Pick the route with lower metric, or lower IP if metrics are equal
  75. best = r
  76. ret = ip
  77. }
  78. }
  79. if ret.IsValid() && !ret.IsPrivate() {
  80. // Default route has a non-private gateway
  81. return netip.Addr{}, netip.Addr{}, false
  82. }
  83. return ret, netip.Addr{}, ret.IsValid()
  84. }
  85. // NonTailscaleMTUs returns a map of interface LUID to interface MTU,
  86. // for all interfaces except Tailscale tunnels.
  87. func NonTailscaleMTUs() (map[winipcfg.LUID]uint32, error) {
  88. mtus := map[winipcfg.LUID]uint32{}
  89. ifs, err := NonTailscaleInterfaces()
  90. for luid, iface := range ifs {
  91. mtus[luid] = iface.MTU
  92. }
  93. return mtus, err
  94. }
  95. func notTailscaleInterface(iface *winipcfg.IPAdapterAddresses) bool {
  96. // TODO(bradfitz): do this without the Description method's
  97. // utf16-to-string allocation. But at least we only do it for
  98. // the virtual interfaces, for which there won't be many.
  99. if iface.IfType != winipcfg.IfTypePropVirtual {
  100. return true
  101. }
  102. desc := iface.Description()
  103. return !(strings.Contains(desc, tsconst.WintunInterfaceDesc) ||
  104. strings.Contains(desc, tsconst.WintunInterfaceDesc0_14))
  105. }
  106. // NonTailscaleInterfaces returns a map of interface LUID to interface
  107. // for all interfaces except Tailscale tunnels.
  108. func NonTailscaleInterfaces() (map[winipcfg.LUID]*winipcfg.IPAdapterAddresses, error) {
  109. return getInterfaces(windows.AF_UNSPEC, winipcfg.GAAFlagIncludeAllInterfaces, notTailscaleInterface)
  110. }
  111. // getInterfaces returns a map of interfaces keyed by their LUID for
  112. // all interfaces matching the provided match predicate.
  113. //
  114. // The family (AF_UNSPEC, AF_INET, or AF_INET6) and flags are passed
  115. // to winipcfg.GetAdaptersAddresses.
  116. func getInterfaces(family winipcfg.AddressFamily, flags winipcfg.GAAFlags, match func(*winipcfg.IPAdapterAddresses) bool) (map[winipcfg.LUID]*winipcfg.IPAdapterAddresses, error) {
  117. ifs, err := winipcfg.GetAdaptersAddresses(family, flags)
  118. if err != nil {
  119. return nil, err
  120. }
  121. ret := map[winipcfg.LUID]*winipcfg.IPAdapterAddresses{}
  122. for _, iface := range ifs {
  123. if match(iface) {
  124. ret[iface.LUID] = iface
  125. }
  126. }
  127. return ret, nil
  128. }
  129. // GetWindowsDefault returns the interface that has the non-Tailscale
  130. // default route for the given address family.
  131. //
  132. // It returns (nil, nil) if no interface is found.
  133. //
  134. // The family must be one of AF_INET or AF_INET6.
  135. func GetWindowsDefault(family winipcfg.AddressFamily) (*winipcfg.IPAdapterAddresses, error) {
  136. ifs, err := getInterfaces(family, winipcfg.GAAFlagIncludeAllInterfaces, func(iface *winipcfg.IPAdapterAddresses) bool {
  137. switch iface.IfType {
  138. case winipcfg.IfTypeSoftwareLoopback:
  139. return false
  140. }
  141. switch family {
  142. case windows.AF_INET:
  143. if iface.Flags&winipcfg.IPAAFlagIpv4Enabled == 0 {
  144. return false
  145. }
  146. case windows.AF_INET6:
  147. if iface.Flags&winipcfg.IPAAFlagIpv6Enabled == 0 {
  148. return false
  149. }
  150. }
  151. return iface.OperStatus == winipcfg.IfOperStatusUp && notTailscaleInterface(iface)
  152. })
  153. if err != nil {
  154. return nil, err
  155. }
  156. routes, err := winipcfg.GetIPForwardTable2(family)
  157. if err != nil {
  158. return nil, err
  159. }
  160. bestMetric := ^uint32(0)
  161. var bestIface *winipcfg.IPAdapterAddresses
  162. for _, route := range routes {
  163. if route.DestinationPrefix.PrefixLength != 0 {
  164. // Not a default route.
  165. continue
  166. }
  167. iface := ifs[route.InterfaceLUID]
  168. if iface == nil {
  169. continue
  170. }
  171. // Microsoft docs say:
  172. //
  173. // "The actual route metric used to compute the route
  174. // preferences for IPv4 is the summation of the route
  175. // metric offset specified in the Metric member of the
  176. // MIB_IPFORWARD_ROW2 structure and the interface
  177. // metric specified in this member for IPv4"
  178. metric := route.Metric
  179. switch family {
  180. case windows.AF_INET:
  181. metric += iface.Ipv4Metric
  182. case windows.AF_INET6:
  183. metric += iface.Ipv6Metric
  184. }
  185. if metric < bestMetric {
  186. bestMetric = metric
  187. bestIface = iface
  188. }
  189. }
  190. return bestIface, nil
  191. }
  192. func defaultRoute() (d DefaultRouteDetails, err error) {
  193. // We always return the IPv4 default route.
  194. // TODO(bradfitz): adjust API if/when anything cares. They could in theory differ, though,
  195. // in which case we might send traffic to the wrong interface.
  196. iface, err := GetWindowsDefault(windows.AF_INET)
  197. if err != nil {
  198. return d, err
  199. }
  200. if iface != nil {
  201. d.InterfaceName = iface.FriendlyName()
  202. d.InterfaceDesc = iface.Description()
  203. d.InterfaceIndex = int(iface.IfIndex)
  204. }
  205. return d, nil
  206. }
  207. var (
  208. winHTTP = windows.NewLazySystemDLL("winhttp.dll")
  209. detectAutoProxyConfigURL = winHTTP.NewProc("WinHttpDetectAutoProxyConfigUrl")
  210. kernel32 = windows.NewLazySystemDLL("kernel32.dll")
  211. globalFree = kernel32.NewProc("GlobalFree")
  212. )
  213. const (
  214. winHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001
  215. winHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002
  216. )
  217. func getPACWindows() string {
  218. if !buildfeatures.HasUseProxy {
  219. return ""
  220. }
  221. var res *uint16
  222. r, _, e := detectAutoProxyConfigURL.Call(
  223. winHTTP_AUTO_DETECT_TYPE_DHCP|winHTTP_AUTO_DETECT_TYPE_DNS_A,
  224. uintptr(unsafe.Pointer(&res)),
  225. )
  226. if r == 1 {
  227. if res == nil {
  228. log.Printf("getPACWindows: unexpected success with nil result")
  229. return ""
  230. }
  231. defer globalFree.Call(uintptr(unsafe.Pointer(res)))
  232. s := windows.UTF16PtrToString(res)
  233. s = strings.TrimSpace(s)
  234. if s == "" {
  235. return "" // Issue 2357: invalid URL "\n" from winhttp; ignoring
  236. }
  237. if _, err := url.Parse(s); err != nil {
  238. log.Printf("getPACWindows: invalid URL %q from winhttp; ignoring", s)
  239. return ""
  240. }
  241. return s
  242. }
  243. const (
  244. ERROR_WINHTTP_AUTODETECTION_FAILED = 12180
  245. )
  246. if e == syscall.Errno(ERROR_WINHTTP_AUTODETECTION_FAILED) {
  247. // Common case on networks without advertised PAC.
  248. return ""
  249. }
  250. log.Printf("getPACWindows: %T=%v", e, e) // syscall.Errno=0x....
  251. return ""
  252. }