The maximum segment size (MSS) determines the largest volume of payload data a Linux host can pack into a single TCP packet without causing IP fragmentation. The kernel struct tcp_info records tcpi_snd_mss, reflecting the current negotiated and clamped send MSS. Tracing this value with eBPF gives systems engineers instant observability into Path MTU Discovery (PMTUD) issues and network middlebox clamping.
Why Send MSS Fluctuates
While standard Ethernet interfaces establish an MSS of 1460 bytes (derived from a 1500-byte MTU minus 40 bytes of IPv4/TCP headers), overlay networks (such as VXLAN, Geneve, or WireGuard) and VPN tunnels reduce usable MTU. Furthermore, misconfigured firewalls often drop ICMP Fragmentation Needed packets, silently forcing the Linux kernel to fall back to blackhole MSS detection.
eBPF BCC Program for MSS Tracing
The following BCC Python script monitors tcp_sync_mss to record effective send MSS changes across active sockets:
from bcc import BPF
bpf_source = """
#include <net/sock.h>
#include <linux/tcp.h>
struct mss_event_t {
u32 pid;
u16 sport;
u16 dport;
u32 snd_mss;
};
BPF_PERF_OUTPUT(mss_events);
int trace_mss_sync(struct pt_regs *ctx, struct sock *sk) {
if (sk->sk_family != AF_INET)
return 0;
struct tcp_sock *tp = tcp_sk(sk);
struct mss_event_t evt = {};
evt.pid = bpf_get_current_pid_tgid() >> 32;
evt.sport = sk->sk_num;
evt.dport = bpf_ntohs(sk->sk_dport);
evt.snd_mss = tp->snd_mss;
mss_events.perf_submit(ctx, &evt, sizeof(evt));
return 0;
}
"""
b = BPF(text=bpf_source)
b.attach_kprobe(event="tcp_sync_mss", fn_name="trace_mss_sync")
def print_mss(cpu, data, size):
e = b["mss_events"].event(data)
print(f"PID {e.pid:<6} | Port {e.sport} -> {e.dport} | Effective snd_mss: {e.snd_mss} bytes")
b["mss_events"].open_perf_buffer(print_mss)
print("Listening for tcpi_snd_mss adjustments... Press Ctrl+C to terminate.")
while True:
b.perf_buffer_poll()
Operational Verification and PMTUD Tuning
Run the script under root privileges:
sudo python3 trace_snd_mss.py
If snd_mss collapses below 1200 bytes, check for tunnel encapsulation overhead and verify whether net.ipv4.tcp_mtu_probing is configured appropriately to bypass path blackholes.