WIP: rebase fork onto upstream/main (v1.103.0) #15

Closed
codinget wants to merge 670 commits from webnet into save/webnet-2026-07-29
2 changed files with 90 additions and 61 deletions
Showing only changes of commit 6a275c01db - Show all commits
+10 -1
View File
@@ -322,7 +322,16 @@ func (i *iptablesRunner) DNATWithLoadBalancer(origDst netip.Addr, dsts []netip.A
func (i *iptablesRunner) ClampMSSToPMTU(tun string, addr netip.Addr) error {
table := i.getIPTByAddr(addr)
return table.Append("mangle", "FORWARD", "-o", tun, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu")
// Clamp MSS on forwarded TCP handshakes in both directions: the SYN
// leaving via tun towards the tailnet peer, and the SYN-ACK arriving on
// tun and being forwarded back out towards the originating endpoint. A
// single -o tun rule only clamps one side of the handshake, leaving the
// endpoint on the other side advertising an MSS that is too large for the
// tun MTU, which black-holes large segments when PMTU discovery is broken.
if err := table.Append("mangle", "FORWARD", "-o", tun, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"); err != nil {
return err
}
return table.Append("mangle", "FORWARD", "-i", tun, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu")
}
// addBase6 adds some basic IPv6 processing rules to be
+80 -60
View File
@@ -248,11 +248,16 @@ func (n *nftablesRunner) EnsureSNATForDst(src, dst netip.Addr) error {
// ClampMSSToPMTU ensures that all packets with TCP flags (SYN, ACK, RST) set
// being forwarded via the given interface (tun) have MSS set to <MTU of the
// interface> - 40 (IP and TCP headers). This can be useful if this tailscale
// instance is expected to run as a forwarding proxy, forwarding packets from an
// endpoint with higher MTU in an environment where path MTU discovery is
// expected to not work (such as the proxies created by the Tailscale Kubernetes
// operator). ClamMSSToPMTU creates a new base-chain ts-clamp in the filter
// interface> - 40 (IP and TCP headers). It clamps both directions of a
// forwarded TCP handshake: the SYN leaving via tun and the SYN-ACK arriving on
// tun. Clamping only the output direction would leave the endpoint on the other
// side of the proxy advertising an MSS that is too large for the tun MTU,
// black-holing large segments when path MTU discovery is broken. This can be
// useful if this tailscale instance is expected to run as a forwarding proxy,
// forwarding packets from an endpoint with higher MTU in an environment where
// path MTU discovery is expected to not work (such as the proxies created by
// the Tailscale Kubernetes operator). ClampMSSToPMTU creates a new base-chain
// ts-clamp in the filter
// table with accept policy and priority -150. In practice, this means that for
// SYN packets the clamp rule in this chain will likely run first and accept the
// packet. This is fine because 1) nftables run ALL chains with the same hook
@@ -289,61 +294,75 @@ func (n *nftablesRunner) ClampMSSToPMTU(tun string, addr netip.Addr) error {
return fmt.Errorf("error ensuring forward chain: %w", err)
}
clampRule := &nftables.Rule{
Table: filterTable,
Chain: fwChain,
Exprs: []expr.Any{
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(tun),
// clampRuleForIface builds a rule that clamps the MSS of forwarded TCP
// handshake packets matching tun on the given interface-name meta key.
// ifaceKey is either expr.MetaKeyOIFNAME (packets leaving via tun) or
// expr.MetaKeyIIFNAME (packets arriving on tun).
clampRuleForIface := func(ifaceKey expr.MetaKey) *nftables.Rule {
return &nftables.Rule{
Table: filterTable,
Chain: fwChain,
Exprs: []expr.Any{
&expr.Meta{Key: ifaceKey, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte(tun),
},
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{unix.IPPROTO_TCP},
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 13,
Len: 1,
},
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 1,
Mask: []byte{0x02},
Xor: []byte{0x00},
},
&expr.Cmp{
Op: expr.CmpOpNeq, // match any packet with a TCP flag set (SYN, ACK, RST)
Register: 1,
Data: []byte{0x00},
},
&expr.Rt{
Register: 1,
Key: expr.RtTCPMSS,
},
&expr.Byteorder{
DestRegister: 1,
SourceRegister: 1,
Op: expr.ByteorderHton,
Len: 2,
Size: 2,
},
&expr.Exthdr{
SourceRegister: 1,
Type: 2,
Offset: 2,
Len: 2,
Op: expr.ExthdrOpTcpopt,
},
},
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{unix.IPPROTO_TCP},
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 13,
Len: 1,
},
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 1,
Mask: []byte{0x02},
Xor: []byte{0x00},
},
&expr.Cmp{
Op: expr.CmpOpNeq, // match any packet with a TCP flag set (SYN, ACK, RST)
Register: 1,
Data: []byte{0x00},
},
&expr.Rt{
Register: 1,
Key: expr.RtTCPMSS,
},
&expr.Byteorder{
DestRegister: 1,
SourceRegister: 1,
Op: expr.ByteorderHton,
Len: 2,
Size: 2,
},
&expr.Exthdr{
SourceRegister: 1,
Type: 2,
Offset: 2,
Len: 2,
Op: expr.ExthdrOpTcpopt,
},
},
}
}
n.conn.AddRule(clampRule)
// Clamp both directions of the forwarded handshake: the SYN leaving via
// tun towards the tailnet peer, and the SYN-ACK arriving on tun and being
// forwarded back out towards the originating endpoint. Matching only the
// output interface leaves the endpoint on the other side advertising an MSS
// that is too large for the tun MTU, which black-holes large segments when
// PMTU discovery is broken.
n.conn.AddRule(clampRuleForIface(expr.MetaKeyOIFNAME))
n.conn.AddRule(clampRuleForIface(expr.MetaKeyIIFNAME))
return n.conn.Flush()
}
@@ -584,8 +603,9 @@ type NetfilterRunner interface {
DeleteSvc(svc, tun string, targetIPs []netip.Addr, pm []PortMap) error
// ClampMSSToPMTU adds a rule to the mangle/FORWARD chain to clamp MSS for
// traffic destined for the provided tun interface.
// ClampMSSToPMTU adds rules to clamp the MSS of forwarded TCP handshake
// packets in both directions (entering and leaving the provided tun
// interface), so both endpoints negotiate an MSS that fits the tun MTU.
ClampMSSToPMTU(tun string, addr netip.Addr) error
// AddMagicsockPortRule adds a rule to the ts-input chain to accept