Wireshark Cheatsheet
Capture Filters (BPF)
Use this Wireshark reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Two Different Languages
Wireshark has two filter syntaxes and they share nothing. Mixing them up is the single most common Wireshark mistake.
| Capture filter | Display filter | |
|---|---|---|
| Language | BPF, same as tcpdump | Wireshark display filter |
| Where | Capture options (Ctrl+K), -f | Filter bar, -Y / -R |
| Applied | In the kernel, before writing | After dissection, on the saved packets |
| Undoable | No, excluded packets are gone forever | Yes, change it freely |
| Host syntax | host 10.0.0.1 | ip.addr == 10.0.0.1 |
| Port syntax | port 443 | tcp.port == 443 |
| Knows protocols | Only what the byte offsets say | Every dissector, 3000+ protocols |
tshark -f "tcp port 443" # capture filter, BPF tshark -Y "tcp.port == 443" # display filter
BPF has no idea what HTTP or DNS is beyond a port number, so port 53 is as
close to "capture DNS" as a capture filter gets. Anything protocol-aware belongs
in a display filter.
Primitives
A BPF expression is built from primitives, each of which is a direction, a type, and a value.
| Primitive | Matches |
|---|---|
host 10.0.0.1 | Either direction, IPv4 or IPv6 |
src host 10.0.0.1 / dst host … | One direction only |
net 10.0.0.0/8, net 10.0.0.0 mask 255.0.0.0 | Subnet |
port 443 | TCP or UDP, either direction |
src port 53 / dst port 53 | Directional port |
portrange 8000-8100 | Port range |
ether host 00:1a:2b:3c:4d:5e | MAC address |
ether broadcast, ether multicast | Layer 2 destination class |
ip, ip6, arp, rarp | Network-layer protocol |
tcp, udp, icmp, icmp6, sctp | Transport |
vlan, vlan 100 | 802.1Q, shifts all later offsets by 4 bytes |
mpls, pppoed, pppoes | Encapsulations, also shift offsets |
less 128, greater 1400 | Frame length |
proto \ip, ip proto 47 | Explicit protocol number, 47 is GRE |
Combine with and / &&, or / ||, not / !. Parentheses need escaping or
quoting in a shell.
tshark -f "host 10.0.0.5 and not port 22" tshark -f "tcp and (port 80 or port 443) and not net 10.0.0.0/8"
not host x and not host yis the correct way to exclude two hosts.not (host x or host y)is equivalent.not host x or not host yis always true and excludes nothing.
Byte Offsets and TCP Flags
proto[offset:size] reads raw bytes. Size defaults to 1 and may be 1, 2, or 4.
This is how BPF reaches things it has no keyword for. A byte read is only a
filter once it is compared, so ip[9] is a value and ip[9] == 6 is the filter.
| Expression | Reads |
|---|---|
ip[0] & 0x0f | IHL, header length in 32-bit words |
ip[1] >> 2 | DSCP, the upper 6 bits of the TOS byte |
ip[8] | TTL |
ip[9] | Protocol number, 6 TCP, 17 UDP, 1 ICMP |
tcp[13] | The flags byte |
tcp[tcpflags] | Same byte, by name |
tcp[0:2] | Source port |
icmp[0] | ICMP type |
udp[8:4] | First four payload bytes of UDP |
The flags byte, bit by bit:
| Flag | Bit value | tcp[13] mask | Named constant |
|---|---|---|---|
| FIN | 1 | 0x01 | tcp-fin |
| SYN | 2 | 0x02 | tcp-syn |
| RST | 4 | 0x04 | tcp-rst |
| PSH | 8 | 0x08 | tcp-push |
| ACK | 16 | 0x10 | tcp-ack |
| URG | 32 | 0x20 | tcp-urg |
| ECE | 64 | 0x40 | tcp-ece |
| CWR | 128 | 0x80 | tcp-cwr |
# SYN set, regardless of other flags tshark -f "tcp[tcpflags] & tcp-syn != 0" tshark -f "tcp[13] & 2 != 0" # SYN only, so connection attempts and not the SYN/ACK reply tshark -f "tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn" # Resets tshark -f "tcp[tcpflags] & tcp-rst != 0" # Any packet carrying TCP payload, the standard "data only" idiom tshark -f "tcp[((tcp[12:1] & 0xf0) >> 2)] != 0"
The last one deserves the explanation. tcp[12] holds the data offset in its
high nibble, in 32-bit words. & 0xf0 isolates it, >> 2 divides by 4 to shift
the nibble down and multiplies by 4 to convert words to bytes, which cancel into
a single shift. The result is the TCP header length in bytes, so
tcp[that_offset] is the first payload byte. Comparing it to zero is a proxy for
"there is at least one payload byte".
Recipes
# DNS only -f "port 53" -f "udp port 53 or tcp port 53" # TLS handshakes only, content type 22 at the start of the record -f "tcp port 443 and (tcp[((tcp[12:1] & 0xf0) >> 2)] = 0x16)" # HTTP requests with a payload (GET, POST, HEAD by their first bytes) -f "tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)" -f "tcp[20:4] = 0x47455420" # "GET " right after a 20-byte TCP header # One host pair, both directions -f "host 10.0.0.5 and host 93.184.216.34" # Exclude your own SSH session so the capture does not feed itself -f "not (tcp port 22 and host 203.0.113.9)" -f "not port 22" # Broadcast and multicast noise only, or everything but -f "ether broadcast or ether multicast" -f "not ether broadcast and not ether multicast" # DHCP, ARP, ICMP -f "udp port 67 or udp port 68" -f "arp" -f "icmp or icmp6" # VLAN-tagged traffic on VLAN 100 -f "vlan 100" # Small packets only, useful for pure ACK storms -f "less 100"
vlan is positional. Everything after it is offset by the 4-byte tag, so
vlan and tcp port 80 works while tcp port 80 and vlan does not match tagged
HTTP. For a link that carries both tagged and untagged frames, write the filter
twice: -f "(tcp port 80) or (vlan and tcp port 80)".
Compile and Debug a Filter
BPF errors are terse. Compile the expression and read the bytecode when a filter silently matches nothing.
dumpcap -d -f "tcp[tcpflags] & tcp-syn != 0" -i en0 tcpdump -d "tcp port 443" # same, without an interface capture tcpdump -dd "tcp port 443" # C array form
Checklist when a capture filter looks broken:
- Kernel-level filter, so a typo is a hard error and the capture never starts.
Read the status bar, not the empty packet list.
- Names resolve at compile time. host www.example.com expands to every A and
AAAA record at that instant and will not follow DNS changes.
- Snaplen interacts with byte offsets. -s 96 plus a filter reading deep into
the payload works on capture but truncates what you can inspect later.
- IPv6 has no fragment-friendly byte-offset support in BPF, so ip6[…] idioms
break on extension headers. Filter on ip6 and tcp port 443 and finish the job
with a display filter.
- Offsets assume Ethernet. On any (Linux cooked capture) or a loopback link
type, the link-layer header size differs and hand-written offsets shift.