tun_windows.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //go:build windows && !e2e_testing
  2. // +build windows,!e2e_testing
  3. package overlay
  4. import (
  5. "crypto"
  6. "encoding/binary"
  7. "fmt"
  8. "io"
  9. "net/netip"
  10. "github.com/sirupsen/logrus"
  11. "github.com/slackhq/nebula/config"
  12. "github.com/slackhq/nebula/util"
  13. "golang.org/x/sys/windows"
  14. wgtun "golang.zx2c4.com/wireguard/tun"
  15. "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
  16. )
  17. const tunGUIDLabel = "Fixed Nebula Windows GUID v1"
  18. type tun struct {
  19. luid winipcfg.LUID
  20. }
  21. func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ []netip.Prefix) (*wgTun, error) {
  22. return nil, fmt.Errorf("newTunFromFd not supported in Windows")
  23. }
  24. func newTun(c *config.C, l *logrus.Logger, vpnNetworks []netip.Prefix, _ bool) (*wgTun, error) {
  25. deviceName := c.GetString("tun.dev", "Nebula")
  26. mtu := c.GetInt("tun.mtu", DefaultMTU)
  27. // Create WireGuard TUN device
  28. tunDevice, err := wgtun.CreateTUN(deviceName, mtu)
  29. if err != nil {
  30. return nil, fmt.Errorf("failed to create TUN device: %w", err)
  31. }
  32. // Get the actual device name
  33. actualName, err := tunDevice.Name()
  34. if err != nil {
  35. tunDevice.Close()
  36. return nil, fmt.Errorf("failed to get TUN device name: %w", err)
  37. }
  38. t := &wgTun{
  39. tunDevice: tunDevice,
  40. vpnNetworks: vpnNetworks,
  41. MaxMTU: mtu,
  42. DefaultMTU: mtu,
  43. l: l,
  44. }
  45. // Create Windows-specific route manager
  46. rm := &tun{}
  47. // Get LUID from the TUN device
  48. // The WireGuard TUN device on Windows should provide a LUID() method
  49. if nativeTun, ok := tunDevice.(interface{ LUID() uint64 }); ok {
  50. rm.luid = winipcfg.LUID(nativeTun.LUID())
  51. } else {
  52. tunDevice.Close()
  53. return nil, fmt.Errorf("failed to get LUID from TUN device")
  54. }
  55. t.routeManager = rm
  56. err = t.reload(c, true)
  57. if err != nil {
  58. tunDevice.Close()
  59. return nil, err
  60. }
  61. c.RegisterReloadCallback(func(c *config.C) {
  62. err := t.reload(c, false)
  63. if err != nil {
  64. util.LogWithContextIfNeeded("failed to reload tun device", err, t.l)
  65. }
  66. })
  67. l.WithField("name", actualName).Info("Created WireGuard TUN device")
  68. return t, nil
  69. }
  70. func (rm *tun) Activate(t *wgTun) error {
  71. // Set MTU
  72. err := rm.setMTU(t, t.MaxMTU)
  73. if err != nil {
  74. return fmt.Errorf("failed to set MTU: %w", err)
  75. }
  76. // Add IP addresses
  77. for _, network := range t.vpnNetworks {
  78. if err := rm.addIP(t, network); err != nil {
  79. return err
  80. }
  81. }
  82. // Add routes
  83. if err := rm.AddRoutes(t, false); err != nil {
  84. return err
  85. }
  86. return nil
  87. }
  88. func (rm *tun) SetMTU(t *wgTun, mtu int) {
  89. if err := rm.setMTU(t, mtu); err != nil {
  90. t.l.WithError(err).Error("Failed to set MTU")
  91. }
  92. }
  93. func (rm *tun) setMTU(t *wgTun, mtu int) error {
  94. // Set MTU using winipcfg
  95. // Note: MTU setting on Windows TUN devices may be handled by the driver
  96. // For now, we'll skip explicit MTU setting as the WireGuard TUN handles it
  97. return nil
  98. }
  99. func (rm *tun) SetDefaultRoute(t *wgTun, cidr netip.Prefix) error {
  100. // On Windows, routes are managed differently
  101. return nil
  102. }
  103. func (rm *tun) AddRoutes(t *wgTun, logErrors bool) error {
  104. routes := *t.Routes.Load()
  105. for _, r := range routes {
  106. if !r.Install {
  107. continue
  108. }
  109. if r.MTU > 0 {
  110. // Windows route MTU is not directly supported
  111. t.l.WithField("route", r).Debug("Route MTU is not supported on Windows")
  112. }
  113. // Use winipcfg to add the route
  114. // The rm.luid should have the AddRoute method from winipcfg
  115. if len(r.Via) == 0 {
  116. t.l.WithField("route", r).Warn("Route has no via address, skipping")
  117. continue
  118. }
  119. err := rm.luid.AddRoute(r.Cidr, r.Via[0].Addr(), uint32(r.Metric))
  120. if err != nil {
  121. retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
  122. if logErrors {
  123. retErr.Log(t.l)
  124. } else {
  125. return retErr
  126. }
  127. } else {
  128. t.l.WithField("route", r).Info("Added route")
  129. }
  130. }
  131. return nil
  132. }
  133. func (rm *tun) RemoveRoutes(t *wgTun, routes []Route) {
  134. for _, r := range routes {
  135. if !r.Install {
  136. continue
  137. }
  138. if len(r.Via) == 0 {
  139. continue
  140. }
  141. err := rm.luid.DeleteRoute(r.Cidr, r.Via[0].Addr())
  142. if err != nil {
  143. t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
  144. } else {
  145. t.l.WithField("route", r).Info("Removed route")
  146. }
  147. }
  148. }
  149. func (rm *tun) NewMultiQueueReader(t *wgTun) (io.ReadWriteCloser, error) {
  150. // Windows doesn't support multi-queue TUN devices
  151. // Return a reader that wraps the same device
  152. return &wgTunReader{
  153. parent: t,
  154. tunDevice: t.tunDevice,
  155. offset: 0,
  156. l: t.l,
  157. }, nil
  158. }
  159. func (rm *tun) addIP(t *wgTun, network netip.Prefix) error {
  160. // Add IP address using winipcfg
  161. // SetIPAddresses expects a slice of prefixes
  162. err := rm.luid.SetIPAddresses([]netip.Prefix{network})
  163. if err != nil {
  164. return fmt.Errorf("failed to add IP address %s: %w", network, err)
  165. }
  166. return nil
  167. }
  168. // generateGUIDByDeviceName generates a GUID based on the device name
  169. func generateGUIDByDeviceName(deviceName string) (*windows.GUID, error) {
  170. // Hash the device name to create a deterministic GUID
  171. h := crypto.SHA256.New()
  172. h.Write([]byte(tunGUIDLabel))
  173. h.Write([]byte(deviceName))
  174. sum := h.Sum(nil)
  175. guid := &windows.GUID{
  176. Data1: binary.LittleEndian.Uint32(sum[0:4]),
  177. Data2: binary.LittleEndian.Uint16(sum[4:6]),
  178. Data3: binary.LittleEndian.Uint16(sum[6:8]),
  179. }
  180. copy(guid.Data4[:], sum[8:16])
  181. return guid, nil
  182. }