mirror of
https://github.com/qdm12/gluetun.git
synced 2026-05-10 04:30:20 +02:00
Path MTU discovery fixes and improvements (#3109)
- 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
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/pmtud/constants"
|
||||
"github.com/qdm12/gluetun/internal/pmtud/ip"
|
||||
)
|
||||
|
||||
// createSYNPacket creates a TCP SYN packet for initiating a handshake.
|
||||
// SYN packets have normally no data payload, so you SHOULD set mtu to 0.
|
||||
// However, in some cases where the server closes the connection with RST immediately,
|
||||
// it can be useful to add some data payload to a SYN packet and check if the server still
|
||||
// replies. Only set mtu to a non zero value if you know what you are doing.
|
||||
func createSYNPacket(src, dst netip.AddrPort, mtu uint32) (packet []byte, seq uint32) {
|
||||
seq = rand.Uint32() //nolint:gosec
|
||||
const ack = 0 // SYN has no ACK number
|
||||
payloadLength := constants.BaseTCPHeaderLength // no data payload
|
||||
if mtu > 0 {
|
||||
payloadLength = getPayloadLength(mtu, dst)
|
||||
}
|
||||
return createPacket(src, dst, seq, ack, payloadLength, synFlag), seq
|
||||
}
|
||||
|
||||
// createACKPacket creates a TCP ACK packet.
|
||||
// If the mtu is set to 0, no payload is sent.
|
||||
// Otherwise, the payload is calculated to test the MTU given.
|
||||
func createACKPacket(src, dst netip.AddrPort, seq, ack uint32, mtu uint32) []byte {
|
||||
payloadLength := constants.BaseTCPHeaderLength // no data payload
|
||||
if mtu > 0 {
|
||||
payloadLength = getPayloadLength(mtu, dst)
|
||||
}
|
||||
const flags = ackFlag | pshFlag
|
||||
return createPacket(src, dst, seq, ack, payloadLength, flags)
|
||||
}
|
||||
|
||||
func createRSTPacket(src, dst netip.AddrPort, seq, ack uint32) []byte {
|
||||
const payloadLength = constants.BaseTCPHeaderLength // no data payload
|
||||
return createPacket(src, dst, seq, ack, payloadLength, rstFlag)
|
||||
}
|
||||
|
||||
func getPayloadLength(mtu uint32, dst netip.AddrPort) uint32 {
|
||||
var ipHeaderLength uint32
|
||||
if dst.Addr().Is4() {
|
||||
ipHeaderLength = constants.IPv4HeaderLength
|
||||
} else {
|
||||
ipHeaderLength = constants.IPv6HeaderLength
|
||||
}
|
||||
if mtu < ipHeaderLength+constants.BaseTCPHeaderLength {
|
||||
panic("MTU too small to hold IP and TCP headers")
|
||||
}
|
||||
return mtu - ipHeaderLength
|
||||
}
|
||||
|
||||
func createPacket(src, dst netip.AddrPort,
|
||||
seq, ack, payloadLength uint32, flags byte,
|
||||
) []byte {
|
||||
if payloadLength < constants.BaseTCPHeaderLength {
|
||||
panic("payload length is too small to hold TCP header")
|
||||
}
|
||||
|
||||
var ipHeader []byte
|
||||
if dst.Addr().Is4() {
|
||||
ipHeader = ip.HeaderV4(src.Addr(), dst.Addr(), payloadLength)
|
||||
} else {
|
||||
ipHeader = ip.HeaderV6(src.Addr(), dst.Addr(),
|
||||
uint16(payloadLength), byte(syscall.IPPROTO_TCP)) //nolint:gosec
|
||||
}
|
||||
|
||||
tcpHeader := makeTCPHeader(src.Port(), dst.Port(), seq, ack, flags)
|
||||
|
||||
// data is just zeroes
|
||||
dataLength := int(payloadLength) - int(constants.BaseTCPHeaderLength)
|
||||
var data []byte
|
||||
if dataLength > 0 {
|
||||
data = make([]byte, dataLength)
|
||||
}
|
||||
checksum := tcpChecksum(ipHeader, tcpHeader, data)
|
||||
tcpHeader[16] = byte(checksum >> 8) //nolint:mnd
|
||||
tcpHeader[17] = byte(checksum & 0xff) //nolint:mnd
|
||||
|
||||
packet := make([]byte, len(ipHeader)+int(constants.BaseTCPHeaderLength)+dataLength)
|
||||
copy(packet, ipHeader)
|
||||
copy(packet[len(ipHeader):], tcpHeader)
|
||||
copy(packet[len(ipHeader)+int(constants.BaseTCPHeaderLength):], data)
|
||||
return packet
|
||||
}
|
||||
Reference in New Issue
Block a user