ipconfig.co.uk

Packet capture: tcpdump, tshark, Wireshark and pktmon

ipconfig tells you what your computer thinks its configuration is. A packet capture shows what is actually on the wire: whether the DHCP server answered, whether DNS queries leave, whether the TCP handshake completes. It is the final word in most arguments about "whose fault is it".

tcpdump (Linux, macOS, BSD)

Installed by default on macOS and most Linux servers (apt install tcpdump otherwise). Needs root or the cap_net_raw capability. On macOS 14 and later you are asked to allow Terminal to capture the first time.

Linux · Terminal
sudo tcpdump -i eth0                          # everything on eth0, names resolved, until Ctrl+C
sudo tcpdump -i eth0 -n                       # -n: no name resolution (always use it; much faster and clearer)
sudo tcpdump -i eth0 -nn                      # -nn: also show port numbers instead of service names
sudo tcpdump -i any -n                        # all interfaces (Linux)
sudo tcpdump -D                               # list interfaces
sudo tcpdump -i eth0 -n -c 20                 # stop after 20 packets
sudo tcpdump -i eth0 -n -v                    # more detail (-vv, -vvv for more)
sudo tcpdump -i eth0 -n -e                    # include MAC addresses
sudo tcpdump -i eth0 -n -X                    # hex and ASCII dump of each packet
sudo tcpdump -i eth0 -n -A                    # ASCII payload (readable for HTTP, DNS names)
sudo tcpdump -i eth0 -n -tttt                 # full timestamps
sudo tcpdump -i eth0 -n -s 0 -w capture.pcap  # save everything to a file for Wireshark
sudo tcpdump -i eth0 -n -w capture.pcap -c 1000
sudo tcpdump -i eth0 -n -w cap.pcap -G 60 -W 10        # rotate files every 60 s, keep 10
tcpdump -n -r capture.pcap                    # read a file back
tcpdump -n -r capture.pcap 'port 53'          # read with a filter
sudo tcpdump -i eth0 -n -l | tee live.txt     # line-buffered for piping

Capture filters (BPF syntax)

The filter goes at the end, in quotes. It is applied in the kernel, so a narrow filter keeps the load down and the output readable.

Linux · Terminal
sudo tcpdump -i eth0 -n host 192.168.1.10               # to or from one host
sudo tcpdump -i eth0 -n src 192.168.1.10
sudo tcpdump -i eth0 -n dst 1.1.1.1
sudo tcpdump -i eth0 -n net 192.168.1.0/24
sudo tcpdump -i eth0 -n port 53                           # DNS
sudo tcpdump -i eth0 -n 'port 67 or port 68'              # DHCP
sudo tcpdump -i eth0 -n arp                               # ARP only
sudo tcpdump -i eth0 -n icmp                              # ping and errors
sudo tcpdump -i eth0 -n icmp6                             # IPv6 neighbour discovery, RAs
sudo tcpdump -i eth0 -n tcp port 443
sudo tcpdump -i eth0 -n 'tcp port 80 and host 192.168.1.10'
sudo tcpdump -i eth0 -n 'not port 22'                     # everything except your SSH session
sudo tcpdump -i eth0 -n 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'   # new connection attempts (SYN only)
sudo tcpdump -i eth0 -n 'tcp[tcpflags] & (tcp-rst) != 0' # resets
sudo tcpdump -i eth0 -n 'udp and not port 53'
sudo tcpdump -i eth0 -n 'ether host d4:6e:0e:11:22:33'    # by MAC
sudo tcpdump -i eth0 -n 'vlan 10'
sudo tcpdump -i eth0 -n 'ip6'
sudo tcpdump -i eth0 -n 'greater 1400'                    # packets over 1400 bytes (MTU hunting)

Reading the output

Output
12:04:31.118342 IP 192.168.1.42.52114 > 142.250.187.196.443: Flags [S], seq 3919253911, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0
12:04:31.127905 IP 142.250.187.196.443 > 192.168.1.42.52114: Flags [S.], seq 1122334455, ack 3919253912, win 65535, options [mss 1412,sackOK,TS val 2 ecr 1,nop,wscale 8], length 0
12:04:31.127950 IP 192.168.1.42.52114 > 142.250.187.196.443: Flags [.], ack 1, win 502, length 0
12:04:31.130001 IP 192.168.1.42.52114 > 142.250.187.196.443: Flags [P.], seq 1:518, ack 1, win 502, length 517
Flags [S], [S.], [.], [P.], [F.], [R]
SYN, SYN-ACK (the dot is ACK), ACK, PSH-ACK (data), FIN-ACK, RST. The first three lines above are a complete handshake; a SYN with no [S.] reply repeated every second is a blocked or unreachable port; a [R] straight back is a closed port.
192.168.1.42.52114 > 142.250.187.196.443
Source address.port > destination address.port. With -nn. Without it, 443 shows as https.
mss 1460 / mss 1412
Each side's maximum segment size. The far side's 1412 reveals a 1452-byte MTU somewhere (PPPoE plus something). Useful for MTU problems.
length
Payload bytes.

Recipes

Is the DHCP server answering?

Linux · Terminal
sudo tcpdump -i eth0 -n -v 'port 67 or port 68'
# then in another terminal: sudo dhclient -r eth0; sudo dhclient eth0   (or ipconfig /renew on the client you are testing)
# healthy: Discover, Offer, Request, ACK within a second. Only Discover/Request repeating: no server is hearing you.

Where do my DNS queries go, and do they come back?

Linux · Terminal
sudo tcpdump -i eth0 -n 'port 53'
# then: dig example.com
# you should see A? example.com. to your resolver and a reply with the address. Query with no reply: resolver dead or blocked. No query at all: the cache answered, or the browser is using DoH (port 443).

Who is sending ARP for the gateway, and who answers?

Linux · Terminal
sudo tcpdump -i eth0 -n -e arp
# two different MACs answering 'is-at' for 192.168.1.1 = duplicate IP or ARP spoofing

Is my port actually reachable from outside?

Linux · Terminal
sudo tcpdump -i eth0 -n 'tcp port 8080'
# then connect from another machine. SYN arriving but no SYN-ACK: nothing is listening or the host firewall drops it. Nothing arriving: the router/NAT or upstream firewall is blocking.

What is this device talking to?

Linux · Terminal
sudo tcpdump -i eth0 -n 'host 192.168.1.88 and not arp' -c 200 | awk '{print $5}' | cut -d. -f1-4 | sort | uniq -c | sort -rn | head
# needs the traffic to pass your capture point: run it on the router (OpenWrt has tcpdump), or use a switch mirror port

IPv6 router advertisements

Linux · Terminal
sudo tcpdump -i eth0 -n -v 'icmp6 and ip6[40] == 134'    # RA packets: prefix, lifetime, M/O flags, RDNSS

Wireshark and tshark

Wireshark is the graphical analyser; tshark is the same engine in the terminal. Both read pcap files from tcpdump and pktmon. Wireshark's display filters use a different syntax from tcpdump's capture filters:

Wanttcpdump capture filterWireshark display filter
One hosthost 192.168.1.10ip.addr == 192.168.1.10
DNSport 53dns
DHCPport 67 or port 68dhcp (older: bootp)
HTTP requeststcp port 80http.request
TLS handshakes with server nametcp port 443tls.handshake.extensions_server_name
SYN without ACKtcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syntcp.flags.syn == 1 && tcp.flags.ack == 0
Retransmissionsn/atcp.analysis.retransmission
Errors of all kindsn/atcp.analysis.flags || icmp.type == 3
Slow responsesn/ahttp.time > 1, dns.time > 0.5
Exclude your SSH sessionnot port 22!(tcp.port == 22)
Linux · Terminal
tshark -i eth0 -n -f 'port 53' -Y 'dns.flags.response == 1' -T fields -e dns.qry.name -e dns.a   # names and answers only
tshark -r capture.pcap -q -z conv,ip                       # conversation summary per IP pair
tshark -r capture.pcap -q -z io,phs                        # protocol hierarchy
tshark -r capture.pcap -Y 'tcp.analysis.retransmission' | wc -l
tshark -r capture.pcap -T fields -e frame.time -e ip.src -e ip.dst -e _ws.col.Info | head
tshark -i eth0 -a duration:60 -w one-minute.pcap

In Wireshark, Statistics › Conversations, Statistics › Protocol Hierarchy, Analyze › Expert Information and right-click › Follow › TCP Stream answer most questions without writing a filter. The Time column can be switched to "seconds since previous displayed packet" to spot delays.

Windows: pktmon and netsh trace

Windows 10 1809 and later include pktmon, which captures at every layer of the stack (so you can see a packet arrive at the NIC and vanish in a filter driver). Wireshark for Windows uses Npcap and is the friendlier option when you can install software.

Windows · Command Prompt
:: capture DNS and DHCP to an ETL file, then convert for Wireshark (admin)
pktmon filter add -p 53
pktmon filter add -p 67
pktmon filter add -p 68
pktmon start --capture --pkt-size 0
:: ...reproduce the problem...
pktmon stop
pktmon etl2pcapng PktMon.etl -o capture.pcapng
pktmon filter remove

:: live text output instead of a file (1809-21H1 syntax differs slightly)
pktmon start --capture --log-mode real-time

:: list interfaces and components
pktmon list
pktmon list --all

:: filter examples
pktmon filter add -i 192.168.1.10
pktmon filter add -t tcp -p 443
pktmon filter add -m 8C-16-45-3A-9B-2D
pktmon filter add -t arp

:: the older way: netsh trace, also ETL, convert with etl2pcapng from GitHub
netsh trace start capture=yes tracefile=C:\temp\net.etl maxsize=200
netsh trace stop

PowerShell 5.1+ also has New-NetEventSession/Add-NetEventPacketCaptureProvider for scripted captures. In Wireshark on Windows, capturing on Wi-Fi shows only your own traffic (no monitor mode with most drivers); Ethernet shows the same broadcast and multicast traffic as any other host.

Capturing where the traffic actually is

  • On a switched network you only see your own traffic plus broadcasts and multicasts (ARP, DHCP, mDNS, SSDP). To see another device's traffic you need a mirror/SPAN port on a managed switch, a hub or a tap, or to capture on the router.
  • Routers: OpenWrt (opkg install tcpdump), pfSense/OPNsense (Diagnostics › Packet Capture), UniFi (SSH then tcpdump), MikroTik (/tool sniffer), Fritz!Box (fritz.box/html/capture.html). Stream a router capture to Wireshark: ssh root@router tcpdump -i br-lan -n -w - | wireshark -k -i -.
  • Wi-Fi monitor mode captures every frame in the air on one channel, including other devices' (encrypted) traffic and all management frames. Linux: iw dev wlan0 set type monitor or airmon-ng; macOS: Wireshark's monitor-mode checkbox or tcpdump -I. Windows: generally not supported.
  • Inside a container or namespace: nsenter -t PID -n tcpdump -i eth0, or capture on the host's veth/bridge.

Related pages

Last reviewed . Command syntax verified against Windows 11, Ubuntu 24.04, macOS 15 and FreeBSD 14 unless noted otherwise.

Spotted a mistake or a switch we have missed? Every page on this site is written to be checked against real output, so please test on your own machine and compare.