Measure what encapsulation costs you
Task
Work out how many bytes of a 1500-byte Ethernet frame are actually your data, then prove your arithmetic against a real capture. Overhead is the reason a gigabit link never delivers a gigabit of payload, and it is examinable as the header sizes you are about to count.
Steps
- Predict first, on paper. Write down the header sizes you expect for an Ethernet frame carrying IPv4 carrying TCP with no options: Ethernet header and trailer, IP header, TCP header. Total them.
- Start a listener on Host B:
nc -l -p 9000 > /dev/null. Ifncis absent,python3 -m http.server 9000and fetch from it instead. - Capture on Host B while sending:
sudo tcpdump -i eth0 -n -s 0 -w /tmp/x.pcap port 9000. From Host A, send a megabyte:head -c 1048576 /dev/urandom | nc 192.168.20.10 9000. - Stop the capture and read the frame sizes:
tcpdump -r /tmp/x.pcap -n -c 20 | head. Note the largest segment length tcpdump reports, which is the TCP payload, not the frame. - Compare your prediction with what you measured, and account for any gap. The common surprises are the 14-byte Ethernet header tcpdump does not print in the length field, and TCP timestamp options adding 12 bytes.
Verify
python3 -c "eth=14; ip=20; tcp=20; mtu=1500; print('payload', mtu-ip-tcp, 'efficiency', round((mtu-ip-tcp)/(mtu+eth+4)*100,2), '%')"
tcpdump -r /tmp/x.pcap -n | awk '{print $NF}' | sort -n | tail -1
The Python line gives 1460 bytes of payload and roughly 96% efficiency counting the frame's own overhead. The largest length in the capture should be 1460 — or 1448 if timestamps are on, which is the answer to "why is mine 12 short".
Notes
Extend it by adding a VLAN tag (4 more bytes) or a VPN. A GRE or IPsec tunnel wraps the whole packet in another set of headers, which is why tunnels so often cause the fragmentation problems domain 5 asks about: the payload shrinks and nobody told the application.