mirror of https://github.com/tailscale/tailscale/
wgengine,net,ipn,disco: split up and define different types of MTU
Prepare for path MTU discovery by splitting up the concept of DefaultMTU() into the concepts of the Tailscale TUN MTU, MTUs of underlying network interfaces, minimum "safe" TUN MTU, user configured TUN MTU, probed path MTU to a peer, and maximum probed MTU. Add a set of likely MTUs to probe. Updates #311 Signed-off-by: Val <valerie@tailscale.com>pull/9517/head
parent
fb2f3e4741
commit
059051c58a
@ -1,33 +1,154 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tstun
|
||||
|
||||
import "tailscale.com/envknob"
|
||||
import (
|
||||
"tailscale.com/envknob"
|
||||
)
|
||||
|
||||
// The MTU (Maximum Transmission Unit) of a network interface is the largest
|
||||
// packet that can be sent or received through that interface, including all
|
||||
// headers above the link layer (e.g. IP headers, UDP headers, Wireguard
|
||||
// headers, etc.). We have to think about several different values of MTU:
|
||||
//
|
||||
// Wire MTU: The MTU of an interface underneath the tailscale TUN, e.g. an
|
||||
// Ethernet network card will default to a 1500 byte MTU. The user may change
|
||||
// this MTU at any time.
|
||||
//
|
||||
// TUN MTU: The current MTU of the tailscale TUN. This MTU is adjusted downward
|
||||
// to make room for the wireguard/tailscale headers. For example, if the
|
||||
// underlying network interface's MTU is 1500 bytes, the maximum size of a
|
||||
// packet entering the tailscale TUN is 1420 bytes. The user may change this MTU
|
||||
// at any time via the OS's tools (ifconfig, ip, etc.).
|
||||
//
|
||||
// User configured initial MTU: The MTU the tailscale TUN should be created
|
||||
// with, set by the user via TS_DEBUG_MTU. It should be adjusted down from the
|
||||
// underlying interface MTU by 80 bytes to make room for the wireguard
|
||||
// headers. This envknob is mostly for debugging. This value is used once at TUN
|
||||
// creation and ignored thereafter.
|
||||
//
|
||||
// User configured current MTU: The MTU set via the OS's tools (ifconfig, ip,
|
||||
// etc.). This MTU can change at any time. Setting the MTU this way goes through
|
||||
// the MTU() method of tailscale's TUN wrapper.
|
||||
//
|
||||
// Maximum probed MTU: This is the largest MTU size that we send probe packets
|
||||
// for.
|
||||
//
|
||||
// Safe MTU: If the tailscale TUN MTU is set to this value, almost all packets
|
||||
// will get to their destination. Tailscale defaults to this MTU in the absence
|
||||
// of path MTU probe information or user MTU configuration. We may occasionally
|
||||
// find a path that needs a smaller MTU but it is very rare.
|
||||
//
|
||||
// Peer MTU: This is the path MTU to a peer's current best endpoint. It defaults
|
||||
// to the Safe MTU unless we have path MTU probe results that tell us otherwise.
|
||||
//
|
||||
// Initial MTU: This is the MTU tailscaled creates the TUN with. In order of
|
||||
// priority, it is:
|
||||
//
|
||||
// 1. If set, the value of TS_DEBUG_MTU clamped to a maximum of 65536
|
||||
// 2. If TS_DEBUG_ENABLE_PMTUD is set, the maximum size MTU we probe, minus wg
|
||||
// overhead
|
||||
// 3. If TS_DEBUG_ENABLE_PMTUD is not set, the Safe MTU
|
||||
//
|
||||
// Current MTU: This the MTU of the tailscale TUN at any given moment
|
||||
// after TUN creation. In order of priority, it is:
|
||||
//
|
||||
// 1. The MTU set by the user via the OS, if it has ever been set
|
||||
// 2. If TS_DEBUG_ENABLE_PMTUD is set, the maximum size MTU we probe, minus wg
|
||||
// overhead
|
||||
// 4. If TS_DEBUG_ENABLE_PMTUD is not set, the Safe MTU
|
||||
|
||||
// TUNMTU is the MTU for the tailscale TUN.
|
||||
type TUNMTU uint32
|
||||
|
||||
// WireMTU is the MTU for the underlying network devices.
|
||||
type WireMTU uint32
|
||||
|
||||
const (
|
||||
maxMTU uint32 = 65536
|
||||
defaultMTU uint32 = 1280
|
||||
// maxTUNMTU is the largest MTU we will consider for the Tailscale
|
||||
// TUN. This is inherited from wireguard-go and can be surprisingly
|
||||
// small; on Windows it is currently 2048 - 32 bytes and iOS it is 1700
|
||||
// - 32 bytes.
|
||||
// TODO(val,raggi): On Windows this seems to derive from RIO driver
|
||||
// constraints in Wireguard but we don't use RIO so could probably make
|
||||
// this bigger.
|
||||
maxTUNMTU TUNMTU = TUNMTU(MaxPacketSize)
|
||||
// safeTUNMTU is the default "safe" MTU for the Tailscale TUN that we
|
||||
// use in the absence of other information such as path MTU probes.
|
||||
safeTUNMTU TUNMTU = 1280
|
||||
)
|
||||
|
||||
// DefaultMTU returns either the constant default MTU of 1280, or the value set
|
||||
// in TS_DEBUG_MTU clamped to a maximum of 65536.
|
||||
func DefaultMTU() uint32 {
|
||||
// DefaultMTU is the Tailscale default MTU for now.
|
||||
//
|
||||
// wireguard-go defaults to 1420 bytes, which only works if the
|
||||
// "outer" MTU is 1500 bytes. This breaks on DSL connections
|
||||
// (typically 1492 MTU) and on GCE (1460 MTU?!).
|
||||
//
|
||||
// 1280 is the smallest MTU allowed for IPv6, which is a sensible
|
||||
// "probably works everywhere" setting until we develop proper PMTU
|
||||
// discovery.
|
||||
tunMTU := defaultMTU
|
||||
if mtu, ok := envknob.LookupUintSized("TS_DEBUG_MTU", 10, 32); ok {
|
||||
mtu := uint32(mtu)
|
||||
if mtu > maxMTU {
|
||||
mtu = maxMTU
|
||||
// MaxProbedWireMTU is the largest MTU we will test for path MTU
|
||||
// discovery.
|
||||
var MaxProbedWireMTU WireMTU = 9000
|
||||
|
||||
func init() {
|
||||
if MaxProbedWireMTU > WireMTU(maxTUNMTU) {
|
||||
MaxProbedWireMTU = WireMTU(maxTUNMTU)
|
||||
}
|
||||
tunMTU = mtu
|
||||
}
|
||||
|
||||
// wgHeaderLen is the length of all the headers Wireguard adds to a packet
|
||||
// in the worst case (IPv6). This constant is for use when we can't or
|
||||
// shouldn't use information about the IP version of a specific packet
|
||||
// (e.g., calculating the MTU for the Tailscale interface.
|
||||
//
|
||||
// A Wireguard header includes:
|
||||
//
|
||||
// - 20-byte IPv4 header or 40-byte IPv6 header
|
||||
// - 8-byte UDP header
|
||||
// - 4-byte type
|
||||
// - 4-byte key index
|
||||
// - 8-byte nonce
|
||||
// - 16-byte authentication tag
|
||||
const wgHeaderLen = 40 + 8 + 4 + 4 + 8 + 16
|
||||
|
||||
// TUNToWireMTU takes the MTU that the Tailscale TUN presents to the user and
|
||||
// returns the on-the-wire MTU necessary to transmit the largest packet that
|
||||
// will fit through the TUN, given that we have to add wireguard headers.
|
||||
func TUNToWireMTU(t TUNMTU) WireMTU {
|
||||
return WireMTU(t + wgHeaderLen)
|
||||
}
|
||||
|
||||
// WireToTUNMTU takes the MTU of an underlying network device and returns the
|
||||
// largest possible MTU for a Tailscale TUN operating on top of that device,
|
||||
// given that we have to add wireguard headers.
|
||||
func WireToTUNMTU(w WireMTU) TUNMTU {
|
||||
if w < wgHeaderLen {
|
||||
return 0
|
||||
}
|
||||
return tunMTU
|
||||
return TUNMTU(w - wgHeaderLen)
|
||||
}
|
||||
|
||||
// DefaultTUNMTU returns the MTU we use to set the Tailscale TUN
|
||||
// MTU. It is also the path MTU that we default to if we have no
|
||||
// information about the path to a peer.
|
||||
//
|
||||
// 1. If set, the value of TS_DEBUG_MTU clamped to a maximum of MaxTunMTU
|
||||
// 2. If TS_DEBUG_ENABLE_PMTUD is set, the maximum size MTU we probe, minus wg overhead
|
||||
// 3. If TS_DEBUG_ENABLE_PMTUD is not set, the Safe MTU
|
||||
func DefaultTUNMTU() TUNMTU {
|
||||
if m, ok := envknob.LookupUintSized("TS_DEBUG_MTU", 10, 32); ok {
|
||||
return min(TUNMTU(m), maxTUNMTU)
|
||||
}
|
||||
|
||||
debugPMTUD, _ := envknob.LookupBool("TS_DEBUG_ENABLE_PMTUD")
|
||||
if debugPMTUD {
|
||||
return WireToTUNMTU(MaxProbedWireMTU)
|
||||
}
|
||||
|
||||
return safeTUNMTU
|
||||
}
|
||||
|
||||
// Temporary workaround for code on corp that uses this function name.
|
||||
// TODO(val): Remove as soon as corp OSS is updated.
|
||||
func DefaultMTU() uint32 {
|
||||
return uint32(DefaultTUNMTU())
|
||||
}
|
||||
|
||||
// DefaultWireMTU returns the default TUN MTU, adjusted for wireguard
|
||||
// overhead.
|
||||
func DefaultWireMTU() WireMTU {
|
||||
return TUNToWireMTU(DefaultTUNMTU())
|
||||
}
|
||||
|
Loading…
Reference in New Issue