| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- //go:build windows && !e2e_testing
- // +build windows,!e2e_testing
- package overlay
- import (
- "crypto"
- "encoding/binary"
- "fmt"
- "io"
- "net/netip"
- "github.com/sirupsen/logrus"
- "github.com/slackhq/nebula/config"
- "github.com/slackhq/nebula/util"
- "golang.org/x/sys/windows"
- wgtun "golang.zx2c4.com/wireguard/tun"
- "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
- )
- const tunGUIDLabel = "Fixed Nebula Windows GUID v1"
- type tun struct {
- luid winipcfg.LUID
- }
- func newTunFromFd(_ *config.C, _ *logrus.Logger, _ int, _ []netip.Prefix) (*wgTun, error) {
- return nil, fmt.Errorf("newTunFromFd not supported in Windows")
- }
- func newTun(c *config.C, l *logrus.Logger, vpnNetworks []netip.Prefix, _ bool) (*wgTun, error) {
- deviceName := c.GetString("tun.dev", "Nebula")
- mtu := c.GetInt("tun.mtu", DefaultMTU)
- // Create WireGuard TUN device
- tunDevice, err := wgtun.CreateTUN(deviceName, mtu)
- if err != nil {
- return nil, fmt.Errorf("failed to create TUN device: %w", err)
- }
- // Get the actual device name
- actualName, err := tunDevice.Name()
- if err != nil {
- tunDevice.Close()
- return nil, fmt.Errorf("failed to get TUN device name: %w", err)
- }
- t := &wgTun{
- tunDevice: tunDevice,
- vpnNetworks: vpnNetworks,
- MaxMTU: mtu,
- DefaultMTU: mtu,
- l: l,
- }
- // Create Windows-specific route manager
- rm := &tun{}
- // Get LUID from the TUN device
- // The WireGuard TUN device on Windows should provide a LUID() method
- if nativeTun, ok := tunDevice.(interface{ LUID() uint64 }); ok {
- rm.luid = winipcfg.LUID(nativeTun.LUID())
- } else {
- tunDevice.Close()
- return nil, fmt.Errorf("failed to get LUID from TUN device")
- }
- t.routeManager = rm
- err = t.reload(c, true)
- if err != nil {
- tunDevice.Close()
- return nil, err
- }
- c.RegisterReloadCallback(func(c *config.C) {
- err := t.reload(c, false)
- if err != nil {
- util.LogWithContextIfNeeded("failed to reload tun device", err, t.l)
- }
- })
- l.WithField("name", actualName).Info("Created WireGuard TUN device")
- return t, nil
- }
- func (rm *tun) Activate(t *wgTun) error {
- // Set MTU
- err := rm.setMTU(t, t.MaxMTU)
- if err != nil {
- return fmt.Errorf("failed to set MTU: %w", err)
- }
- // Add IP addresses
- for _, network := range t.vpnNetworks {
- if err := rm.addIP(t, network); err != nil {
- return err
- }
- }
- // Add routes
- if err := rm.AddRoutes(t, false); err != nil {
- return err
- }
- return nil
- }
- func (rm *tun) SetMTU(t *wgTun, mtu int) {
- if err := rm.setMTU(t, mtu); err != nil {
- t.l.WithError(err).Error("Failed to set MTU")
- }
- }
- func (rm *tun) setMTU(t *wgTun, mtu int) error {
- // Set MTU using winipcfg
- // Note: MTU setting on Windows TUN devices may be handled by the driver
- // For now, we'll skip explicit MTU setting as the WireGuard TUN handles it
- return nil
- }
- func (rm *tun) SetDefaultRoute(t *wgTun, cidr netip.Prefix) error {
- // On Windows, routes are managed differently
- return nil
- }
- func (rm *tun) AddRoutes(t *wgTun, logErrors bool) error {
- routes := *t.Routes.Load()
- for _, r := range routes {
- if !r.Install {
- continue
- }
- if r.MTU > 0 {
- // Windows route MTU is not directly supported
- t.l.WithField("route", r).Debug("Route MTU is not supported on Windows")
- }
- // Use winipcfg to add the route
- // The rm.luid should have the AddRoute method from winipcfg
- if len(r.Via) == 0 {
- t.l.WithField("route", r).Warn("Route has no via address, skipping")
- continue
- }
- err := rm.luid.AddRoute(r.Cidr, r.Via[0].Addr(), uint32(r.Metric))
- if err != nil {
- retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
- if logErrors {
- retErr.Log(t.l)
- } else {
- return retErr
- }
- } else {
- t.l.WithField("route", r).Info("Added route")
- }
- }
- return nil
- }
- func (rm *tun) RemoveRoutes(t *wgTun, routes []Route) {
- for _, r := range routes {
- if !r.Install {
- continue
- }
- if len(r.Via) == 0 {
- continue
- }
- err := rm.luid.DeleteRoute(r.Cidr, r.Via[0].Addr())
- if err != nil {
- t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
- } else {
- t.l.WithField("route", r).Info("Removed route")
- }
- }
- }
- func (rm *tun) NewMultiQueueReader(t *wgTun) (io.ReadWriteCloser, error) {
- // Windows doesn't support multi-queue TUN devices
- // Return a reader that wraps the same device
- return &wgTunReader{
- parent: t,
- tunDevice: t.tunDevice,
- offset: 0,
- l: t.l,
- }, nil
- }
- func (rm *tun) addIP(t *wgTun, network netip.Prefix) error {
- // Add IP address using winipcfg
- // SetIPAddresses expects a slice of prefixes
- err := rm.luid.SetIPAddresses([]netip.Prefix{network})
- if err != nil {
- return fmt.Errorf("failed to add IP address %s: %w", network, err)
- }
- return nil
- }
- // generateGUIDByDeviceName generates a GUID based on the device name
- func generateGUIDByDeviceName(deviceName string) (*windows.GUID, error) {
- // Hash the device name to create a deterministic GUID
- h := crypto.SHA256.New()
- h.Write([]byte(tunGUIDLabel))
- h.Write([]byte(deviceName))
- sum := h.Sum(nil)
- guid := &windows.GUID{
- Data1: binary.LittleEndian.Uint32(sum[0:4]),
- Data2: binary.LittleEndian.Uint16(sum[4:6]),
- Data3: binary.LittleEndian.Uint16(sum[6:8]),
- }
- copy(guid.Data4[:], sum[8:16])
- return guid, nil
- }
|