skip to content

How do you write a tcpdump filter for TCP traffic with 192.0.2.10 on port 5432, and what do its type, dir and proto qualifiers do?

level: juniorimportance: must knowfreq 44%

answer

  1. an id plus up to three qualifiers
  2. proto first, type last
  3. missing type means host
  4. missing dir means src or dst

basics

~20 s

Use tcp port 5432 and host 192.0.2.10. Each primitive is an id with qualifiers: proto (tcp, ip, ip6), dir (src, dst) and type (host, net, port, portrange). Missing ones default to host, src or dst, and every consistent protocol.

solid answer

~40 s

The filter is `tcp port 5432 and host 192.0.2.10`. A pcap-filter primitive is an id (an address, a number, a name) with up to three qualifiers: **proto** limits the protocol (`ether`, `ip`, `ip6`, `arp`, `tcp`, `udp`, `sctp`), **dir** picks a direction (`src`, `dst`, `src and dst`), and **type** says what the id is (`host`, `net`, `port`, `portrange`). When more than one is present, proto comes first and type last, as in `tcp dst port 5432`. Omitted qualifiers fall back to defaults: no type means `host`, no dir means `src or dst`, and no proto means every protocol consistent with the type, so a bare `port 5432` matches TCP, UDP and SCTP. To see only the client's requests, write `tcp dst port 5432 and src host 192.0.2.10`.

go deeper

for a junior

Recall the three qualifier kinds and their defaults, and be able to type tcp port 5432 and host 192.0.2.10 without looking anything up.

for a middle

Explain the ordering rule, proto first and type last, and what each default expands to, such as port 53 covering TCP, UDP and SCTP.

for a senior

Show judgement on incident filters: literal addresses instead of names, direction qualifiers to cut volume, and knowing that port filters cannot see later fragments.

for a principal

Frame filters as a team standard: shared, reviewed expressions for common incidents reduce both missed evidence and oversized captures on production hosts.

## What a primitive is tcpdump hands its filter expression to libpcap, which compiles it into a BPF program; the language is documented in the **pcap-filter** manual page. The expression is built from **primitives**. A primitive is usually an **id** (a host address, a network, a port number or a name) preceded by one or more **qualifiers** that say how to interpret it. For the question as asked, the answer is: ``` tcp port 5432 and host 192.0.2.10 ``` That is two primitives joined by `and`: `tcp port 5432` and `host 192.0.2.10`. ## The three qualifier kinds | Kind | Values (common ones) | What it does | Default when omitted | |---|---|---|---| | **proto** | `ether`, `ip`, `ip6`, `arp`, `tcp`, `udp`, `sctp` | restricts the match to one protocol | every protocol consistent with the type | | **dir** | `src`, `dst`, `src or dst`, `src and dst` | picks the direction relative to the id | `src or dst` | | **type** | `host`, `net`, `port`, `portrange`, `proto` | says what kind of thing the id is | `host` | The defaults are what make short filters work, and they are also where surprises come from: - `port 53` means `(tcp or udp or sctp) port 53`, so it matches DNS over both TCP and UDP. - `src 192.0.2.10` means a source **host** match for IPv4, ARP and RARP; with a name that resolves to both families, IPv6 is covered too. - `net 198.51.100.0/24` without a proto qualifier matches IPv4, ARP and RARP addresses in that prefix. - `dst port 5432` without `tcp` also matches UDP or SCTP packets to 5432, which rarely matters but shows up in noisy captures. ## Ordering and combinations that are rejected In a primitive that follows this pattern, each qualifier kind may appear at most once, the **proto qualifier must be first** and the **type qualifier must be last**: `tcp dst port 80` is valid, while `dst tcp port 80` and `tcp port dst 80` are not. Some combinations make no sense and are rejected even in the right order: 1. `ether port` - the Ethernet header has no ports. 2. `tcp net` - the TCP header has no layer-3 addresses. 3. `dst proto` - the protocol field applies to both ends of a packet. Some special primitives do not follow the pattern at all: `vlan`, `mpls`, `less`, `greater`, `broadcast`, `multicast` and the byte-offset relations. ## How ids are read - **host** takes an IPv4 or IPv6 address or a name. A name is resolved with `getaddrinfo` when the filter is compiled, and the primitive is true if any resolved address matches. During an incident, prefer the literal address: the name may resolve differently on the capture host, or not at all. - **net** takes a prefix. `net 198.51.100.0/24` and `net 2001:db8::/32` use CIDR; `net 192.168.1` (a dotted triple) means `/24` and `net 172.16` means `/16`, while a dotted quad without a length is a `/32` host match. `net 192.168 mask 255.255` also works for IPv4. - **port** takes a number or a name from `/etc/services`. With a name, both the port number and the protocol are checked; with a number only the port is. `portrange 6000-6008` is inclusive and the two ends may be given in either order. - Protocol names that are also keywords need a backslash in `proto` primitives, as in `ip proto \tcp` or `ether proto \arp`. One subtlety of `port`: for IPv4 it also requires the packet to be unfragmented or the first fragment, and for IPv6 it requires that no extension header sits between the IPv6 header and the transport header. Later fragments carry no ports, so no port filter can catch them. ## Writing it under pressure The usual incident request is "show me only the database traffic for this client". Build it from the narrowest primitive outwards: 1. Pick the protocol and port: `tcp port 5432`. 2. Add the peer: `and host 192.0.2.10`. 3. If only one direction matters, add dir qualifiers: `tcp dst port 5432 and src host 192.0.2.10` shows only what the client sends. 4. Exclude your own session when you are logged in over SSH to the same host: `and not port 22`. Quote the whole expression in single quotes on the command line so the shell leaves parentheses and other metacharacters alone. How to group several primitives with `and`, `or` and `not` is a separate skill with its own trap, because those operators do not follow the precedence you expect from most programming languages.

  • How would you narrow `tcp port 5432 and host 192.0.2.10` to only the packets the client sends?
    Add direction qualifiers: `tcp dst port 5432 and src host 192.0.2.10`. The dir qualifier sits between any proto and type qualifiers, and it applies to the id in that one primitive only. Without a dir qualifier each primitive means `src or dst`, which is why the original filter shows both directions of the conversation.
  • Why might `host db.example.internal` behave differently from `host 192.0.2.10` in a tcpdump filter?
    A host name is resolved with `getaddrinfo` once, when the filter is compiled. If the name resolves to several addresses the primitive matches any of them, and if it resolves to both IPv4 and IPv6 it covers both families. If the capture host's resolver answers differently, or the address changes during the capture, the filter keeps matching the old answer.

saying these in an interview costs you the question

  • Says a bare port 53 filter only matches UDP DNS
  • Writes dst tcp port 80 and expects it to compile
  • Thinks net 192.168.1 is a single host address
  • Believes a host name in the filter is re-resolved for every packet
  • Expects a port filter to catch later IPv4 fragments