mirror of
https://github.com/qdm12/gluetun.git
synced 2026-05-07 04:20:12 +02:00
be92aa2ac4
- Existing option `WIREGUARD_MTU` , if set, disables PMTUD and is used - New option `PMTUD_ICMP_ADDRESSES=1.1.1.1,8.8.8.8` and `PMTUD_TCP_ADDRESSES=1.1.1.1:443,8.8.8.8:443` - ICMP PMTUD now targets external-by-default IP addresses - New TCP PMTUD (binary search only) as a second MTU confirmation and fallback mechanism. - Force set TCP MSS to MTU - IP header - TCP base header - "magic 20 bytes" 🎆 - Fix #3108
77 lines
2.5 KiB
Go
77 lines
2.5 KiB
Go
package pmtud
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"github.com/qdm12/gluetun/internal/pmtud/constants"
|
|
"github.com/qdm12/gluetun/internal/pmtud/icmp"
|
|
"github.com/qdm12/gluetun/internal/pmtud/tcp"
|
|
)
|
|
|
|
// PathMTUDiscover discovers the maximum MTU using both ICMP and TCP.
|
|
// Multiple ICMP addresses and TCP addresses can be specified for redundancy.
|
|
// ICMP PMTUD is run first. If successful, the range of possible MTU values to
|
|
// check for TCP PMTUD is reduced to [maxMTU-150, maxMTU] where maxMTU is the
|
|
// maximum MTU found with ICMP PMTUD. Otherwise, TCP PMTUD is run with the
|
|
// whole range of possible MTU values up to the physical link MTU to check.
|
|
// If the physicalLinkMTU is zero, it defaults to 1500 which is the ethernet standard MTU.
|
|
// If the pingTimeout is zero, it defaults to 1 second.
|
|
// If the logger is nil, a no-op logger is used.
|
|
// It returns [ErrMTUNotFound] if the MTU could not be determined.
|
|
func PathMTUDiscover(ctx context.Context, icmpAddrs []netip.Addr, tcpAddrs []netip.AddrPort,
|
|
physicalLinkMTU uint32, tryTimeout time.Duration, logger Logger) (
|
|
mtu uint32, err error,
|
|
) {
|
|
if physicalLinkMTU == 0 {
|
|
const ethernetStandardMTU = 1500
|
|
physicalLinkMTU = ethernetStandardMTU
|
|
}
|
|
if tryTimeout == 0 {
|
|
tryTimeout = time.Second
|
|
}
|
|
if logger == nil {
|
|
logger = &noopLogger{}
|
|
}
|
|
|
|
// Try finding the MTU using ICMP
|
|
maxPossibleMTU := physicalLinkMTU
|
|
icmpSuccess := false
|
|
for _, icmpIP := range icmpAddrs {
|
|
mtu, err := icmp.PathMTUDiscover(ctx, icmpIP, physicalLinkMTU,
|
|
tryTimeout, logger)
|
|
switch {
|
|
case err == nil:
|
|
logger.Debugf("ICMP path MTU discovery against %s found maximum valid MTU %d", icmpIP, mtu)
|
|
icmpSuccess = true
|
|
maxPossibleMTU = mtu
|
|
case errors.Is(err, icmp.ErrNotPermitted), errors.Is(err, icmp.ErrMTUNotFound):
|
|
logger.Debugf("ICMP path MTU discovery failed: %s", err)
|
|
default:
|
|
return 0, fmt.Errorf("ICMP path MTU discovery: %w", err)
|
|
}
|
|
}
|
|
|
|
for _, addrPort := range tcpAddrs {
|
|
minMTU := constants.MinIPv4MTU
|
|
if addrPort.Addr().Is6() {
|
|
minMTU = constants.MinIPv6MTU
|
|
}
|
|
if icmpSuccess {
|
|
const mtuMargin = 150
|
|
minMTU = max(maxPossibleMTU-mtuMargin, minMTU)
|
|
}
|
|
mtu, err = tcp.PathMTUDiscover(ctx, addrPort, minMTU, maxPossibleMTU, logger)
|
|
if err != nil {
|
|
logger.Debugf("TCP path MTU discovery to %s failed: %s", addrPort, err)
|
|
continue
|
|
}
|
|
logger.Debugf("TCP path MTU discovery to %s found maximum valid MTU %d", addrPort, mtu)
|
|
return mtu, nil
|
|
}
|
|
return 0, fmt.Errorf("TCP path MTU discovery: last error: %w", err)
|
|
}
|