How to Filter TCP Keep-Alive Packets Using tcpdump on Linux: A Practical Guide for Network Analysis


2 views

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:

  1. Verify TCP Keep-Alive is enabled on your systems (sysctl net.ipv4.tcp_keepalive_time)
  2. Check your interface name (eth0 might be enp0s3 or similar on modern systems)
  3. Try running tcpdump as root or with sudo