Debugging network issues, analyzing protocols, and testing security all require the ability to capture and inspect the raw packets flowing through your network. This chapter covers the essential tools for packet analysis — from command-line tcpdump to the powerful Python library Scapy — and shows you how to build custom monitoring solutions.
tcpdump is the foundational packet capture tool available on virtually every Unix system.
# Capture all traffic on eth0
sudo tcpdump -i eth0
# Capture with verbose output
sudo tcpdump -i eth0 -v
# Capture and save to a file (pcap format)
sudo tcpdump -i eth0 -w capture.pcap
# Read a saved capture
tcpdump -r capture.pcap
BPF (Berkeley Packet Filter) expressions let you focus on specific traffic:
# Only TCP traffic on port 80
sudo tcpdump -i eth0 'tcp port 80'
# Only traffic to/from a specific host
sudo tcpdump -i eth0 'host 192.168.1.42'
# Only DNS queries (UDP port 53)
sudo tcpdump -i eth0 'udp port 53'
# SYN packets only (new TCP connections)
sudo tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'
# Traffic between two hosts
sudo tcpdump -i eth0 'host 10.0.0.1 and host 10.0.0.2'
# HTTP requests (look for GET/POST in payload)
sudo tcpdump -i eth0 -A 'tcp port 80' | grep -E '^(GET|POST|HTTP)'
| Option | Description |
|---|---|
-i <iface> |
Capture on specific interface (any for all) |
-c <count> |
Stop after capturing N packets |
-w <file> |
Write packets to pcap file |
-r <file> |
Read from pcap file |
-n |
Don’t resolve hostnames (faster) |
-nn |
Don’t resolve hostnames or ports |
-v, -vv, -vvv |
Increasing verbosity |
-A |
Print packet payload as ASCII |
-X |
Print payload as hex and ASCII |
-s <size> |
Capture size (0 = full packet) |
Wireshark is the graphical packet analyzer. Its CLI counterpart, tshark, is useful for scripting:
# Capture with tshark
sudo tshark -i eth0 -f 'tcp port 443' -c 100
# Read a pcap file and display HTTP requests
tshark -r capture.pcap -Y 'http.request' -T fields -e http.host -e http.request.uri
# Extract TLS handshake info
tshark -r capture.pcap -Y 'tls.handshake' -T fields -e tls.handshake.type -e tls.handshake.extensions_server_name
# Statistics: protocol hierarchy
tshark -r capture.pcap -q -z io,phs
Scapy is a powerful Python library that lets you craft, send, sniff, and analyze network packets at any layer.
pip install scapy
Most Scapy operations require root privileges (raw socket access).
Scapy uses a layered syntax that mirrors the protocol stack:
from scapy.all import IP, TCP, UDP, ICMP, Ether, Raw
# Create an ICMP echo request (ping)
pkt = IP(dst="8.8.8.8") / ICMP()
# Create a TCP SYN packet
syn = IP(dst="example.com") / TCP(dport=80, flags="S")
# Create a full HTTP request
http_req = (
IP(dst="example.com")
/ TCP(dport=80, flags="PA")
/ Raw(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
)
# Inspect a packet
syn.show()
print(syn.summary())
from scapy.all import IP, ICMP, sr1, send
# Send and receive one response (like ping)
response = sr1(IP(dst="8.8.8.8") / ICMP(), timeout=2)
if response:
print(f"Reply from {response.src}: TTL={response.ttl}")
# Send without waiting for a response
send(IP(dst="8.8.8.8") / ICMP(), count=3)
from scapy.all import sniff, IP, TCP
def packet_handler(pkt):
if IP in pkt and TCP in pkt:
print(f"{pkt[IP].src}:{pkt[TCP].sport} → {pkt[IP].dst}:{pkt[TCP].dport} "
f"flags={pkt[TCP].flags}")
# Sniff 20 TCP packets
sniff(filter="tcp", prn=packet_handler, count=20, iface="eth0")
Full example: code/scapy_sniffer.py
from scapy.all import IP, ICMP, sr1
def traceroute(target: str, max_hops: int = 30):
print(f"Traceroute to {target}")
for ttl in range(1, max_hops + 1):
pkt = IP(dst=target, ttl=ttl) / ICMP()
reply = sr1(pkt, timeout=2, verbose=0)
if reply is None:
print(f"{ttl:3d} * * *")
elif reply.type == 0: # Echo reply — destination reached
print(f"{ttl:3d} {reply.src} (reached)")
break
else: # TTL exceeded
print(f"{ttl:3d} {reply.src}")
traceroute("8.8.8.8")
from scapy.all import IP, TCP, sr1
def scan_port(target: str, port: int) -> str:
"""Scan a single TCP port using SYN scan."""
pkt = IP(dst=target) / TCP(dport=port, flags="S")
resp = sr1(pkt, timeout=1, verbose=0)
if resp is None:
return "filtered"
if resp.haslayer(TCP):
if resp[TCP].flags == "SA": # SYN-ACK
# Send RST to close
sr1(IP(dst=target) / TCP(dport=port, flags="R"), timeout=1, verbose=0)
return "open"
elif resp[TCP].flags == "RA": # RST-ACK
return "closed"
return "unknown"
# Scan common ports
for port in [22, 80, 443, 8080, 3306]:
status = scan_port("192.168.1.1", port)
print(f"Port {port:5d}: {status}")
Full example: code/port_scanner.py
from scapy.all import rdpcap, IP, TCP
packets = rdpcap("capture.pcap")
print(f"Total packets: {len(packets)}")
# Filter and analyze
tcp_packets = [p for p in packets if TCP in p]
for pkt in tcp_packets[:10]:
print(f"{pkt[IP].src}:{pkt[TCP].sport} → {pkt[IP].dst}:{pkt[TCP].dport}")
from scapy.all import rdpcap, IP, TCP, UDP, ICMP
from collections import Counter
packets = rdpcap("capture.pcap")
protocols = Counter()
for pkt in packets:
if TCP in pkt:
protocols["TCP"] += 1
elif UDP in pkt:
protocols["UDP"] += 1
elif ICMP in pkt:
protocols["ICMP"] += 1
else:
protocols["Other"] += 1
for proto, count in protocols.most_common():
print(f"{proto:10s}: {count}")
Let’s build a simple real-time bandwidth monitor:
import time
from collections import defaultdict
from scapy.all import sniff, IP
traffic = defaultdict(lambda: {"bytes_in": 0, "bytes_out": 0, "packets": 0})
MY_IP = "192.168.1.42"
def monitor(pkt):
if IP in pkt:
size = len(pkt)
if pkt[IP].dst == MY_IP:
traffic[pkt[IP].src]["bytes_in"] += size
elif pkt[IP].src == MY_IP:
traffic[pkt[IP].dst]["bytes_out"] += size
traffic[pkt[IP].src]["packets"] += 1
def print_stats():
print(f"\n{'Host':20s} {'In (KB)':>10s} {'Out (KB)':>10s} {'Packets':>10s}")
print("-" * 55)
for host, stats in sorted(traffic.items(), key=lambda x: x[1]["bytes_in"], reverse=True)[:10]:
print(f"{host:20s} {stats['bytes_in']/1024:10.1f} {stats['bytes_out']/1024:10.1f} {stats['packets']:10d}")
# Sniff for 30 seconds, then print stats
sniff(prn=monitor, timeout=30, iface="eth0")
print_stats()
Full example: code/bandwidth_monitor.py
For monitoring without raw packets (no root required):
import psutil
import time
def monitor_connections():
"""List all active network connections."""
for conn in psutil.net_connections(kind="inet"):
laddr = f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else ""
raddr = f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else ""
print(f"{conn.status:12s} {laddr:25s} → {raddr:25s} PID={conn.pid}")
def monitor_bandwidth(interval: float = 1.0):
"""Monitor network I/O rates."""
prev = psutil.net_io_counters()
while True:
time.sleep(interval)
curr = psutil.net_io_counters()
sent = (curr.bytes_sent - prev.bytes_sent) / 1024
recv = (curr.bytes_recv - prev.bytes_recv) / 1024
print(f"↑ {sent:.1f} KB/s ↓ {recv:.1f} KB/s")
prev = curr
tcpdump is the essential command-line tool for packet capture; master its BPF filter syntaxtshark (Wireshark CLI) provides protocol-aware analysis and statisticspsutil provides network monitoring without root privileges (connection lists, bandwidth stats)| ← Previous: HTTP, APIs, and WebSockets | Table of Contents | Next: Network Automation and Configuration → |