interfaces_windows.go 7.7 KB

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