From 1dd38bc6582854dac59de5e81ee07ed166f0e650 Mon Sep 17 00:00:00 2001 From: Lars Haalck Date: Sun, 21 May 2023 15:11:07 +0200 Subject: [PATCH] feat(wireguard): `WIREGUARD_MTU` enviromnent variable (#1571) --- Dockerfile | 1 + internal/configuration/settings/wireguard.go | 12 ++++++++++- .../configuration/sources/env/wireguard.go | 6 ++++++ internal/provider/utils/wireguard.go | 1 + internal/wireguard/constructor_test.go | 2 ++ internal/wireguard/run.go | 10 +++++----- internal/wireguard/settings.go | 17 ++++++++++++++++ internal/wireguard/settings_test.go | 20 +++++++++++++++++++ 8 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index f324cf55..4c2b6b5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -97,6 +97,7 @@ ENV VPN_SERVICE_PROVIDER=pia \ WIREGUARD_PRESHARED_KEY= \ WIREGUARD_PUBLIC_KEY= \ WIREGUARD_ADDRESSES= \ + WIREGUARD_MTU= \ WIREGUARD_IMPLEMENTATION=auto \ # VPN server filtering SERVER_REGIONS= \ diff --git a/internal/configuration/settings/wireguard.go b/internal/configuration/settings/wireguard.go index f0aa0c53..480fb054 100644 --- a/internal/configuration/settings/wireguard.go +++ b/internal/configuration/settings/wireguard.go @@ -8,6 +8,7 @@ import ( "github.com/qdm12/gluetun/internal/configuration/settings/helpers" "github.com/qdm12/gluetun/internal/constants/providers" "github.com/qdm12/gotree" + wireguarddevice "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -27,6 +28,10 @@ type Wireguard struct { // to create. It cannot be the empty string in the // internal state. Interface string + // Maximum Transmission Unit (MTU) of the Wireguard interface. + // It cannot be zero in the internal state, and defaults to + // the wireguard-go MTU default of 1420. + MTU uint16 // Implementation is the Wireguard implementation to use. // It can be "auto", "userspace" or "kernelspace". // It defaults to "auto" and cannot be the empty string @@ -110,6 +115,7 @@ func (w *Wireguard) copy() (copied Wireguard) { PreSharedKey: helpers.CopyPointer(w.PreSharedKey), Addresses: helpers.CopySlice(w.Addresses), Interface: w.Interface, + MTU: w.MTU, Implementation: w.Implementation, } } @@ -119,6 +125,7 @@ func (w *Wireguard) mergeWith(other Wireguard) { w.PreSharedKey = helpers.MergeWithPointer(w.PreSharedKey, other.PreSharedKey) w.Addresses = helpers.MergeSlices(w.Addresses, other.Addresses) w.Interface = helpers.MergeWithString(w.Interface, other.Interface) + w.MTU = helpers.MergeWithNumber(w.MTU, other.MTU) w.Implementation = helpers.MergeWithString(w.Implementation, other.Implementation) } @@ -127,6 +134,7 @@ func (w *Wireguard) overrideWith(other Wireguard) { w.PreSharedKey = helpers.OverrideWithPointer(w.PreSharedKey, other.PreSharedKey) w.Addresses = helpers.OverrideWithSlice(w.Addresses, other.Addresses) w.Interface = helpers.OverrideWithString(w.Interface, other.Interface) + w.MTU = helpers.OverrideWithNumber(w.MTU, other.MTU) w.Implementation = helpers.OverrideWithString(w.Implementation, other.Implementation) } @@ -134,6 +142,7 @@ func (w *Wireguard) setDefaults() { w.PrivateKey = helpers.DefaultPointer(w.PrivateKey, "") w.PreSharedKey = helpers.DefaultPointer(w.PreSharedKey, "") w.Interface = helpers.DefaultString(w.Interface, "wg0") + w.MTU = helpers.DefaultNumber(w.MTU, wireguarddevice.DefaultMTU) w.Implementation = helpers.DefaultString(w.Implementation, "auto") } @@ -159,7 +168,8 @@ func (w Wireguard) toLinesNode() (node *gotree.Node) { addressesNode.Appendf(address.String()) } - node.Appendf("Network interface: %s", w.Interface) + interfaceNode := node.Appendf("Network interface: %s", w.Interface) + interfaceNode.Appendf("MTU: %d", w.MTU) if w.Implementation != "auto" { node.Appendf("Implementation: %s", w.Implementation) diff --git a/internal/configuration/sources/env/wireguard.go b/internal/configuration/sources/env/wireguard.go index 80ab34d3..acc561b9 100644 --- a/internal/configuration/sources/env/wireguard.go +++ b/internal/configuration/sources/env/wireguard.go @@ -21,6 +21,12 @@ func (s *Source) readWireguard() (wireguard settings.Wireguard, err error) { if err != nil { return wireguard, err // already wrapped } + mtuPtr, err := envToUint16Ptr("WIREGUARD_MTU") + if err != nil { + return wireguard, fmt.Errorf("environment variable WIREGUARD_MTU: %w", err) + } else if mtuPtr != nil { + wireguard.MTU = *mtuPtr + } return wireguard, nil } diff --git a/internal/provider/utils/wireguard.go b/internal/provider/utils/wireguard.go index 88b949ef..3a50ca76 100644 --- a/internal/provider/utils/wireguard.go +++ b/internal/provider/utils/wireguard.go @@ -15,6 +15,7 @@ func BuildWireguardSettings(connection models.Connection, settings.PreSharedKey = *userSettings.PreSharedKey settings.InterfaceName = userSettings.Interface settings.Implementation = userSettings.Implementation + settings.MTU = userSettings.MTU settings.IPv6 = &ipv6Supported const rulePriority = 101 // 100 is to receive external connections diff --git a/internal/wireguard/constructor_test.go b/internal/wireguard/constructor_test.go index ff87e4db..a56ab903 100644 --- a/internal/wireguard/constructor_test.go +++ b/internal/wireguard/constructor_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.zx2c4.com/wireguard/device" ) func Test_New(t *testing.T) { @@ -48,6 +49,7 @@ func Test_New(t *testing.T) { netip.PrefixFrom(netip.AddrFrom4([4]byte{5, 6, 7, 8}), 32), }, FirewallMark: 100, + MTU: device.DefaultMTU, IPv6: ptr(false), Implementation: "auto", }, diff --git a/internal/wireguard/run.go b/internal/wireguard/run.go index cfb771d5..d4106771 100644 --- a/internal/wireguard/run.go +++ b/internal/wireguard/run.go @@ -74,7 +74,7 @@ func (w *Wireguard) Run(ctx context.Context, waitError chan<- error, ready chan< defer closers.cleanup(w.logger) link, waitAndCleanup, err := setupFunction(ctx, - w.settings.InterfaceName, w.netlink, &closers, w.logger) + w.settings.InterfaceName, w.netlink, w.settings.MTU, &closers, w.logger) if err != nil { waitError <- err return @@ -158,12 +158,12 @@ func (w *Wireguard) setupIPv6(link netlink.Link, closers *closers) (err error) { type waitAndCleanupFunc func() error func setupKernelSpace(ctx context.Context, - interfaceName string, netLinker NetLinker, + interfaceName string, netLinker NetLinker, mtu uint16, closers *closers, logger Logger) ( link netlink.Link, waitAndCleanup waitAndCleanupFunc, err error) { linkAttrs := netlink.LinkAttrs{ Name: interfaceName, - MTU: device.DefaultMTU, // TODO + MTU: int(mtu), } link = &netlink.Wireguard{ LinkAttrs: linkAttrs, @@ -186,10 +186,10 @@ func setupKernelSpace(ctx context.Context, } func setupUserSpace(ctx context.Context, - interfaceName string, netLinker NetLinker, + interfaceName string, netLinker NetLinker, mtu uint16, closers *closers, logger Logger) ( link netlink.Link, waitAndCleanup waitAndCleanupFunc, err error) { - tun, err := tun.CreateTUN(interfaceName, device.DefaultMTU) + tun, err := tun.CreateTUN(interfaceName, int(mtu)) if err != nil { return nil, nil, fmt.Errorf("%w: %s", ErrCreateTun, err) } diff --git a/internal/wireguard/settings.go b/internal/wireguard/settings.go index 986e719d..a43f14e9 100644 --- a/internal/wireguard/settings.go +++ b/internal/wireguard/settings.go @@ -7,6 +7,7 @@ import ( "regexp" "strings" + "golang.zx2c4.com/wireguard/device" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -28,6 +29,9 @@ type Settings struct { // FirewallMark to be used in routing tables and IP rules. // It defaults to 51820 if left to 0. FirewallMark int + // Maximum Transmission Unit (MTU) setting for the network interface. + // It defaults to device.DefaultMTU from wireguard-go which is 1420 + MTU uint16 // RulePriority is the priority for the rule created with the // FirewallMark. RulePriority int @@ -55,6 +59,10 @@ func (s *Settings) SetDefaults() { s.FirewallMark = defaultFirewallMark } + if s.MTU == 0 { + s.MTU = device.DefaultMTU + } + if s.IPv6 == nil { ipv6 := false // this should be injected from host s.IPv6 = &ipv6 @@ -78,6 +86,7 @@ var ( ErrAddressMissing = errors.New("interface address is missing") ErrAddressNotValid = errors.New("interface address is not valid") ErrFirewallMarkMissing = errors.New("firewall mark is missing") + ErrMTUMissing = errors.New("MTU is missing") ErrImplementationInvalid = errors.New("invalid implementation") ) @@ -127,6 +136,10 @@ func (s *Settings) Check() (err error) { return fmt.Errorf("%w", ErrFirewallMarkMissing) } + if s.MTU == 0 { + return fmt.Errorf("%w", ErrMTUMissing) + } + switch s.Implementation { case "auto", "kernelspace", "userspace": default: @@ -209,6 +222,10 @@ func (s Settings) ToLines(settings ToLinesSettings) (lines []string) { lines = append(lines, fieldPrefix+"Firewall mark: "+fmt.Sprint(s.FirewallMark)) } + if s.MTU != 0 { + lines = append(lines, fieldPrefix+"MTU: "+fmt.Sprint(s.MTU)) + } + if s.RulePriority != 0 { lines = append(lines, fieldPrefix+"Rule priority: "+fmt.Sprint(s.RulePriority)) } diff --git a/internal/wireguard/settings_test.go b/internal/wireguard/settings_test.go index 61939ebd..cb29a5ce 100644 --- a/internal/wireguard/settings_test.go +++ b/internal/wireguard/settings_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.zx2c4.com/wireguard/device" ) func ptr[T any](v T) *T { return &v } @@ -22,6 +23,7 @@ func Test_Settings_SetDefaults(t *testing.T) { expected: Settings{ InterfaceName: "wg0", FirewallMark: 51820, + MTU: device.DefaultMTU, IPv6: ptr(false), Implementation: "auto", }, @@ -34,6 +36,7 @@ func Test_Settings_SetDefaults(t *testing.T) { InterfaceName: "wg0", FirewallMark: 51820, Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 51820), + MTU: device.DefaultMTU, IPv6: ptr(false), Implementation: "auto", }, @@ -43,6 +46,7 @@ func Test_Settings_SetDefaults(t *testing.T) { InterfaceName: "wg1", FirewallMark: 999, Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 9999), + MTU: device.DefaultMTU, IPv6: ptr(true), Implementation: "userspace", }, @@ -50,6 +54,7 @@ func Test_Settings_SetDefaults(t *testing.T) { InterfaceName: "wg1", FirewallMark: 999, Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 9999), + MTU: device.DefaultMTU, IPv6: ptr(true), Implementation: "userspace", }, @@ -174,6 +179,19 @@ func Test_Settings_Check(t *testing.T) { }, err: ErrFirewallMarkMissing, }, + "missing_MTU": { + settings: Settings{ + InterfaceName: "wg0", + PrivateKey: validKey1, + PublicKey: validKey2, + Endpoint: netip.AddrPortFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 51820), + Addresses: []netip.Prefix{ + netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 24), + }, + FirewallMark: 999, + }, + err: ErrMTUMissing, + }, "invalid implementation": { settings: Settings{ InterfaceName: "wg0", @@ -184,6 +202,7 @@ func Test_Settings_Check(t *testing.T) { netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 24), }, FirewallMark: 999, + MTU: 1420, Implementation: "x", }, err: errors.New("invalid implementation: x"), @@ -198,6 +217,7 @@ func Test_Settings_Check(t *testing.T) { netip.PrefixFrom(netip.AddrFrom4([4]byte{1, 2, 3, 4}), 24), }, FirewallMark: 999, + MTU: 1420, Implementation: "userspace", }, },