From b8ff94b7fde1c2d21f03642acb183d2234fc50f3 Mon Sep 17 00:00:00 2001 From: etm Date: Sun, 20 Sep 2026 14:51:07 +0330 Subject: [PATCH 1/2] feat(wireguard): support configurable server interface MTU --- backend/wireguard/config.go | 11 ++++++ backend/wireguard/manager.go | 63 +++++++++++++++++++++++++++++++--- backend/wireguard/wireguard.go | 7 ++-- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/backend/wireguard/config.go b/backend/wireguard/config.go index 1b8665b9..a068ff70 100644 --- a/backend/wireguard/config.go +++ b/backend/wireguard/config.go @@ -17,6 +17,7 @@ type Config struct { PrivateKey string `json:"private_key"` PreSharedKey string `json:"pre_shared_key,omitempty"` ListenPort int `json:"listen_port"` + MTU *int `json:"mtu,omitempty"` Address []string `json:"address"` Latency *LatencyConfig `json:"latency,omitempty"` @@ -61,6 +62,9 @@ func NewConfig(config string) (*Config, error) { } // Validate configuration + if err := validateMTU(wgConfig.MTU); err != nil { + return nil, err + } if wgConfig.InterfaceName == "" { wgConfig.InterfaceName = "wg0" } @@ -81,6 +85,13 @@ func NewConfig(config string) (*Config, error) { return &wgConfig, nil } +func validateMTU(mtu *int) error { + if mtu != nil && (*mtu < 576 || *mtu > 9000) { + return errors.New("mtu must be an integer between 576 and 9000") + } + return nil +} + // InterfaceNetworks returns CIDR prefixes parsed from the node's core `address` list. // Used to restrict peer AllowedIPs to subnets this interface actually serves. func (c *Config) InterfaceNetworks() []*net.IPNet { diff --git a/backend/wireguard/manager.go b/backend/wireguard/manager.go index 10537438..eb897b6a 100644 --- a/backend/wireguard/manager.go +++ b/backend/wireguard/manager.go @@ -74,11 +74,14 @@ func wrapPermissionDeniedError(action string, err error) error { // Manager handles WireGuard interface management using wgctrl type Manager struct { - client wgClient - iFaceName string - nl netlinkOps - configure configureDeviceFunc - mu sync.RWMutex + client wgClient + iFaceName string + nl netlinkOps + configure configureDeviceFunc + setLinkMTU func(netlink.Link, int) error + defaultMTU int + mtuManaged bool + mu sync.RWMutex } // NewManager creates a new WireGuard manager @@ -126,9 +129,17 @@ func buildInitialWGConfig(privateKey wgtypes.Key, listenPort int, peers []wgtype // InitializeWithPeers sets up the WireGuard interface with initial configuration and optional full peer snapshot. func (m *Manager) InitializeWithPeers(privateKey wgtypes.Key, listenPort int, serverIPs []string, peers []wgtypes.PeerConfig) error { + return m.initializeWithPeers(privateKey, listenPort, serverIPs, peers, nil) +} + +func (m *Manager) initializeWithPeers(privateKey wgtypes.Key, listenPort int, serverIPs []string, peers []wgtypes.PeerConfig, mtu *int) error { m.mu.Lock() defer m.mu.Unlock() + if err := validateMTU(mtu); err != nil { + return err + } + if m.client == nil { return fmt.Errorf("wgctrl client is not initialized") } @@ -173,6 +184,11 @@ func (m *Manager) InitializeWithPeers(privateKey wgtypes.Key, listenPort int, se if err != nil { return fmt.Errorf("failed to get link: %w", err) } + m.defaultMTU = link2.Attrs().MTU + m.mtuManaged = false + if err := m.applyMTULocked(mtu); err != nil { + return err + } for _, addr := range parsedAddrs { if err := nl.AddrAdd(link2, addr); err != nil { @@ -226,6 +242,10 @@ func (m *Manager) ApplyPeersReplaceAll(peers []wgtypes.PeerConfig) error { // ApplyConfig safely configures the device with the given configuration under lock. func (m *Manager) ApplyConfig(config wgtypes.Config) error { + return m.applyConfig(config, nil) +} + +func (m *Manager) applyConfig(config wgtypes.Config, mtu *int) error { m.mu.Lock() defer m.mu.Unlock() @@ -233,10 +253,43 @@ func (m *Manager) ApplyConfig(config wgtypes.Config) error { return fmt.Errorf("wgctrl client is not initialized") } + if err := m.applyMTULocked(mtu); err != nil { + return err + } configure := m.getConfigureDevice() return configure(m.client, m.iFaceName, config) } +// applyMTULocked preserves the kernel default until an explicit MTU is configured. +// Clearing that setting restores the MTU captured when the interface was created. +func (m *Manager) applyMTULocked(mtu *int) error { + if err := validateMTU(mtu); err != nil { + return err + } + if mtu == nil && !m.mtuManaged { + return nil + } + target := m.defaultMTU + if mtu != nil { + target = *mtu + } + link, err := m.getNetlinkOps().LinkByName(m.iFaceName) + if err != nil { + return fmt.Errorf("failed to get link for MTU update: %w", err) + } + if link.Attrs().MTU != target { + setMTU := m.setLinkMTU + if setMTU == nil { + setMTU = netlink.LinkSetMTU + } + if err := setMTU(link, target); err != nil { + return fmt.Errorf("failed to set interface MTU: %w", wrapPermissionDeniedError("setting wireguard interface MTU", err)) + } + } + m.mtuManaged = mtu != nil + return nil +} + // GetDevice returns the current WireGuard device statistics func (m *Manager) GetDevice() (*wgtypes.Device, error) { m.mu.RLock() diff --git a/backend/wireguard/wireguard.go b/backend/wireguard/wireguard.go index c41225ec..403cf9c8 100644 --- a/backend/wireguard/wireguard.go +++ b/backend/wireguard/wireguard.go @@ -147,6 +147,9 @@ func newWithManagerFactory(cfg *config.Config, wgConfig *Config, users []*common } wg.config = wgConfig + if err := validateMTU(wgConfig.MTU); err != nil { + return nil, err + } log.Println("config loaded in", time.Since(start).Seconds(), "second.") @@ -177,7 +180,7 @@ func newWithManagerFactory(cfg *config.Config, wgConfig *Config, users []*common } // Initialize the WireGuard interface with peers in the same kernel configure call. - if err = manager.InitializeWithPeers(privateKey, wgConfig.ListenPort, wgConfig.Address, startupPeerConfigs); err != nil { + if err = manager.initializeWithPeers(privateKey, wgConfig.ListenPort, wgConfig.Address, startupPeerConfigs, wgConfig.MTU); err != nil { manager.Close() return nil, fmt.Errorf("failed to initialize interface: %w", err) } @@ -278,7 +281,7 @@ func (wg *WireGuard) restartLocked() error { ReplacePeers: true, } - if err := manager.ApplyConfig(config); err != nil { + if err := manager.applyConfig(config, cfg.MTU); err != nil { return fmt.Errorf("failed to reconfigure interface during restart: %w", err) } From a10b715117063f60fabd6349ef5bc03fadff2c1c Mon Sep 17 00:00:00 2001 From: etm Date: Sun, 20 Sep 2026 14:56:51 +0330 Subject: [PATCH 2/2] fix(wireguard): capture default MTU only when overriding it --- backend/wireguard/manager.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/wireguard/manager.go b/backend/wireguard/manager.go index eb897b6a..19426f64 100644 --- a/backend/wireguard/manager.go +++ b/backend/wireguard/manager.go @@ -184,7 +184,7 @@ func (m *Manager) initializeWithPeers(privateKey wgtypes.Key, listenPort int, se if err != nil { return fmt.Errorf("failed to get link: %w", err) } - m.defaultMTU = link2.Attrs().MTU + m.defaultMTU = 0 m.mtuManaged = false if err := m.applyMTULocked(mtu); err != nil { return err @@ -261,7 +261,7 @@ func (m *Manager) applyConfig(config wgtypes.Config, mtu *int) error { } // applyMTULocked preserves the kernel default until an explicit MTU is configured. -// Clearing that setting restores the MTU captured when the interface was created. +// Clearing that setting restores the MTU captured before the first override. func (m *Manager) applyMTULocked(mtu *int) error { if err := validateMTU(mtu); err != nil { return err @@ -277,6 +277,9 @@ func (m *Manager) applyMTULocked(mtu *int) error { if err != nil { return fmt.Errorf("failed to get link for MTU update: %w", err) } + if m.defaultMTU == 0 { + m.defaultMTU = link.Attrs().MTU + } if link.Attrs().MTU != target { setMTU := m.setLinkMTU if setMTU == nil {