When analyzing network traffic to verify TCP keep-alive configurations across devices, we often need to isolate these specific packets from the noise. While Wireshark provides GUI filters, Linux server environments typically rely on command-line tools like tcpdump.
TCP keep-alive packets have distinct characteristics:
1. ACK flag set with sequence number = previous sequence number - 1
2. Window size typically unchanged from previous packet
3. No payload data (length = 0)
4. Often sent at regular intervals (default 2 hours in Linux)
The most precise filter combines these characteristics:
tcpdump -i eth0 'tcp[tcpflags] == tcp-ack and tcp[14:4] = 0 and tcp[18:4] = 0 and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
The filter works by examining:
- tcp[tcpflags] == tcp-ack: Matches ACK-only packets
- tcp[14:4] = 0: Checks for zero payload size
- The ip[] and tcp[] offsets calculate header lengths to verify zero payload
For quick checks where some false positives are acceptable:
tcpdump -i eth0 'tcp[tcpflags] == tcp-ack and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
To analyze timing patterns (e.g., check if keep-alives are sent every 30 minutes):
tcpdump -i eth0 -ttt 'tcp[tcpflags] == tcp-ack and tcp[14:4] = 0'
The -ttt
flag shows inter-packet timing.
Here's a complete script to monitor keep-alives per host:
#!/bin/bash
INTERFACE=eth0
TIMEOUT=300
tcpdump -i $INTERFACE -G $TIMEOUT -W 1 -w keepalives.pcap \
'tcp[tcpflags] == tcp-ack and tcp[14:4] = 0' &> /dev/null &
echo "Monitoring for $TIMEOUT seconds..."
sleep $TIMEOUT
kill $!
tcpdump -r keepalives.pcap -nn | awk '{print $3,$5}' | \
sort | uniq -c | sort -n
When analyzing output:
- High frequency from single host: Possible application-level keep-alives
- Zero results: Keep-alives likely disabled
- Periodic patterns: Standard TCP keep-alive behavior
TCP Keep-Alive packets are special TCP segments sent to verify that a connection is still active. These packets have a specific pattern: they have a sequence number one less than the current sequence number (SEG.SEQ = SND.NXT-1) and carry no data (length = 0).
To filter Keep-Alive packets with tcpdump, we need to look for TCP packets with:
- ACK flag set
- Payload length of 0
- Window size unchanged from previous packets
Here's the most reliable filter for capturing Keep-Alive packets:
tcpdump -i eth0 'tcp[tcpflags] & (tcp-ack) != 0 and tcp[13] & 16 == 16 and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
Let's break this down:
tcp[tcpflags] & (tcp-ack) != 0 # Matches packets with ACK flag set
and tcp[13] & 16 == 16 # Ensures ACK flag is set (redundant but explicit)
and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0 # Matches zero-length payload
For newer versions of tcpdump, you can use a simpler expression:
tcpdump -i eth0 'tcp[13] = 16 and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
To capture Keep-Alive packets and save them to a file for later analysis:
tcpdump -i eth0 -w keepalives.pcap 'tcp[tcpflags] & (tcp-ack) != 0 and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
After capturing packets, verify them with:
tcpdump -r keepalives.pcap -nn -v
Look for packets showing:
ACK [sequence number] win [window size]
with length 0 in the output.
If you want to monitor systems with specific keep-alive intervals, combine with time-based filtering:
tcpdump -i eth0 -G 60 -W 1 -w keepalives_%H-%M.pcap \
'tcp[tcpflags] & (tcp-ack) != 0 and ((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) == 0'
If you're not seeing expected packets:
- Verify TCP Keep-Alive is enabled on your systems (sysctl net.ipv4.tcp_keepalive_time)
- Check your interface name (eth0 might be enp0s3 or similar on modern systems)
- Try running tcpdump as root or with sudo