mirror of
https://github.com/qdm12/gluetun.git
synced 2026-05-10 04:30:20 +02:00
hotfix(pmtud): detect IPv6 usage in VPN connection
This commit is contained in:
@@ -61,6 +61,7 @@ type Storage interface {
|
||||
}
|
||||
|
||||
type NetLinker interface {
|
||||
AddrList(linkIndex uint32, family uint8) (addresses []netip.Prefix, err error)
|
||||
AddrReplace(linkIndex uint32, addr netip.Prefix) error
|
||||
Router
|
||||
Ruler
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package vpn
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/netlink"
|
||||
)
|
||||
|
||||
func (l *Loop) isIPv6Used(settings settings.VPN) bool {
|
||||
if !l.ipv6SupportLevel.IsSupported() {
|
||||
return false
|
||||
}
|
||||
switch settings.Type {
|
||||
case vpn.AmneziaWg:
|
||||
for _, prefix := range settings.AmneziaWg.Wireguard.Addresses {
|
||||
if prefix.Addr().Is6() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case vpn.OpenVPN:
|
||||
link, err := l.netLinker.LinkByName(settings.OpenVPN.Interface)
|
||||
if err != nil {
|
||||
l.logger.Warnf("assuming IPv6 is not supported, cannot get OpenVPN link by name: %v", err)
|
||||
return false
|
||||
}
|
||||
ipv6Prefixes, err := l.netLinker.AddrList(link.Index, netlink.FamilyV6)
|
||||
if err != nil {
|
||||
l.logger.Warnf("assuming IPv6 is not supported, cannot list OpenVPN link addresses: %v", err)
|
||||
return false
|
||||
}
|
||||
for _, prefix := range ipv6Prefixes {
|
||||
if prefix.Addr().IsGlobalUnicast() && !prefix.Addr().IsPrivate() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
case vpn.Wireguard:
|
||||
for _, prefix := range settings.Wireguard.Addresses {
|
||||
if prefix.Addr().Is6() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
panic("vpn type not implemented: " + settings.Type)
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
|
||||
enabled: settings.Type != vpn.Wireguard || *settings.Wireguard.MTU == 0,
|
||||
vpnType: settings.Type,
|
||||
network: connection.Protocol,
|
||||
ipv6: l.isIPv6Used(settings),
|
||||
icmpAddrs: settings.PMTUD.ICMPAddresses,
|
||||
tcpAddrs: settings.PMTUD.TCPAddresses,
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -40,6 +41,8 @@ type tunnelUpPMTUDData struct {
|
||||
// network is used to find the network level header overhead.
|
||||
// It can be [constants.UDP] or [constants.TCP].
|
||||
network string
|
||||
// ipv6 is true if the VPN connection supports IPv6.
|
||||
ipv6 bool
|
||||
// icmpAddrs is the list of addresses to use for ICMP path MTU discovery.
|
||||
// Each address should handle ICMP packets for PMTUD to work.
|
||||
icmpAddrs []netip.Addr
|
||||
@@ -69,7 +72,7 @@ func (l *Loop) onTunnelUp(ctx, loopCtx context.Context, data tunnelUpData) {
|
||||
if data.pmtud.enabled {
|
||||
mtuLogger := l.logger.New(log.SetComponent("MTU discovery"))
|
||||
err := updateToMaxMTU(ctx, data.vpnIntf, data.pmtud.vpnType,
|
||||
data.pmtud.network, data.pmtud.icmpAddrs, data.pmtud.tcpAddrs,
|
||||
data.pmtud.network, data.pmtud.ipv6, data.pmtud.icmpAddrs, data.pmtud.tcpAddrs,
|
||||
l.netLinker, l.routing, l.fw, mtuLogger)
|
||||
if err != nil {
|
||||
mtuLogger.Error(err.Error())
|
||||
@@ -173,16 +176,11 @@ func (l *Loop) restartVPN(ctx context.Context, healthErr error) {
|
||||
}
|
||||
|
||||
func updateToMaxMTU(ctx context.Context, vpnInterface string,
|
||||
vpnType, network string, icmpAddrs []netip.Addr, tcpAddrs []netip.AddrPort,
|
||||
vpnType, network string, ipv6 bool, icmpAddrs []netip.Addr, tcpAddrs []netip.AddrPort,
|
||||
netlinker NetLinker, routing Routing, firewall tcp.Firewall, logger *log.Logger,
|
||||
) error {
|
||||
logger.Info("finding maximum MTU, this can take up to 6 seconds")
|
||||
|
||||
vpnGatewayIP, err := routing.VPNLocalGatewayIP(vpnInterface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting VPN gateway IP address: %w", err)
|
||||
}
|
||||
|
||||
vpnRoutes, err := routing.VPNRoutes(vpnInterface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting VPN routes: %w", err)
|
||||
@@ -195,7 +193,7 @@ func updateToMaxMTU(ctx context.Context, vpnInterface string,
|
||||
|
||||
originalMTU := link.MTU
|
||||
|
||||
vpnLinkMTU := pmtud.MaxTheoreticalVPNMTU(vpnType, network, vpnGatewayIP)
|
||||
vpnLinkMTU := pmtud.MaxTheoreticalVPNMTU(vpnType, network, ipv6)
|
||||
|
||||
// Setting the VPN link MTU to 1500 might interrupt the connection until
|
||||
// the new MTU is set again, but this is necessary to find the highest valid MTU.
|
||||
@@ -206,6 +204,15 @@ func updateToMaxMTU(ctx context.Context, vpnInterface string,
|
||||
return fmt.Errorf("setting VPN interface %s MTU to %d: %w", vpnInterface, vpnLinkMTU, err)
|
||||
}
|
||||
|
||||
if !ipv6 {
|
||||
icmpAddrs = slices.DeleteFunc(icmpAddrs, func(addr netip.Addr) bool {
|
||||
return addr.Is6()
|
||||
})
|
||||
tcpAddrs = slices.DeleteFunc(tcpAddrs, func(addr netip.AddrPort) bool {
|
||||
return addr.Addr().Is6()
|
||||
})
|
||||
}
|
||||
|
||||
const pingTimeout = time.Second
|
||||
vpnLinkMTU, err = pmtud.PathMTUDiscover(ctx, icmpAddrs, tcpAddrs,
|
||||
vpnLinkMTU, pingTimeout, firewall, logger)
|
||||
|
||||
Reference in New Issue
Block a user