netbirdio/netbird · error
add outbound MSS clamp rule: %w
Error message
add outbound MSS clamp rule: %w
What it means
addMSSClampingRules() appends the TCPMSS rule to the NETBIRD-RT-MSSCLAMP mangle chain: outgoing TCP SYN packets on the NetBird interface get --set-mss = mtu - (40 for IPv4 / 60 for IPv6). Failure is logged, not propagated, so the clamp chain exists but stays empty. Note mss is computed with uint16 arithmetic, so an MTU below the header size would underflow to a huge value.
Source
Thrown at client/firewall/iptables/router_linux.go:587
// Add jump rule from FORWARD chain in mangle table to our custom chain
jumpRule := []string{
"-j", chainRTMSSCLAMP,
}
if err := r.iptablesClient.Insert(tableMangle, chainFORWARD, 1, jumpRule...); err != nil {
return fmt.Errorf("add jump to MSS clamp chain: %w", err)
}
r.rules[jumpMSSClamp] = jumpRule
ruleOut := []string{
"-o", r.wgIface.Name(),
"-p", "tcp",
"--tcp-flags", "SYN,RST", "SYN",
"-j", "TCPMSS",
"--set-mss", fmt.Sprintf("%d", mss),
}
if err := r.iptablesClient.Append(tableMangle, chainRTMSSCLAMP, ruleOut...); err != nil {
return fmt.Errorf("add outbound MSS clamp rule: %w", err)
}
r.rules["mss-clamp-out"] = ruleOut
return nil
}
func (r *router) insertEstablishedRule(chain string) error {
establishedRule := getConntrackEstablished()
err := r.iptablesClient.Insert(tableFilter, chain, 1, establishedRule...)
if err != nil {
return fmt.Errorf("failed to insert established rule: %v", err)
}
ruleKey := "established-" + chain
r.rules[ruleKey] = establishedRule
return nilView on GitHub (pinned to 93e97f4bf1)
Solutions
- Reproduce manually with the MSS value from the log to confirm whether xt_tcpmss is the problem
- `modprobe xt_tcpmss`
- Check the configured MTU (management network config / interface): it must exceed 40 (IPv4) or 60 (IPv6) by a sane margin
- Verify the clamp chain received the rule: `sudo iptables -t mangle -S NETBIRD-RT-MSSCLAMP`
- Restart the agent after fixing modules/MTU so addMSSClampingRules runs again
Example fix
// before: uint16 underflow possible when mtu < header size
mss := r.mtu - overhead
// after: guard the subtraction before building the rule
if int(r.mtu) <= int(overhead) {
return fmt.Errorf("mtu %d too small for MSS clamp (needs > %d)", r.mtu, overhead)
}
mss := r.mtu - overhead Defensive patterns
Strategy: validation
Validate before calling
func validMSS(mtu uint16, v6 bool) (uint16, error) {
overhead := uint16(40)
if v6 {
overhead = 60
}
if mtu <= overhead {
return 0, fmt.Errorf("mtu %d must exceed header size %d", mtu, overhead)
}
return mtu - overhead, nil
} Try / catch
Log-only at the call site; on catch, check whether xt_tcpmss exists and whether the MSS value was sane, then retry once after loading the module.
Prevention
- Keep overlay MTU at or above 1280 (IPv6 minimum) in management config
- modprobe xt_tcpmss on hosts forwarding TCP over the overlay
- Re-run MSS clamp setup after interface MTU changes
When it happens
Trigger: `iptables -t mangle -A NETBIRD-RT-MSSCLAMP -o wt0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss <mss>` failing when xt_tcpmss is missing, the NetBird interface name is stale, or iptables rejects the computed MSS value; also triggered by misconfigured tiny MTUs making the subtraction underflow.
Common situations: Kernels without xt_tcpmss (or it built as an unloaded module); overlay MTU set unusually low (bad management-side MTU config); IPv6 peers (v6=true, 60-byte header) on a host with an MTU below 60 (extreme misconfig); silent failure noticed only as PMTU blackholes for routed TCP.
Related errors
- add jump to MSS clamp chain: %w
- add static nat rules: %w
- add jump rules: %w
- add mangle prerouting rule: %w
- add mangle postrouting rule: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/038d62579d7e6fa2.
Report an issue: GitHub.