Networking Fundamentals · intermediate · ~12 min
- Open a saved packet capture (pcap) in Wireshark or read it with tcpdump, and understand what the columns mean. - Read the layered headers of a single packet (Ethernet -> IP -> TCP/UDP -> application) and see how encapsulation looks on the wire. - Write display filters (`tcp.port == 80`, `ip.addr == ...`, `http`, `dns`) to isolate the packets you care about out of thousands. - Recognise the TCP three-way handshake and decode the TCP flag bits (SYN, ACK, FIN, RST, PSH, URG). - Use *Follow TCP Stream* to reassemble a scattered conversation into one readable transcript. - Understand the legal and ethical limits of packet capture, and why this course parses fixtures instead of sniffing live traffic.
A packet capture (often shortened to pcap) is a saved recording of network traffic: every frame that crossed a network interface, byte for byte, timestamped in the order it arrived. Two tools dominate this space. Wireshark is the graphical analyser that decodes each packet into a readable tree of headers. tcpdump is its command-line cousin, used to capture and to do quick filtering on servers where no GUI exists.
Think of a capture as a flight recorder for a network. When something goes wrong, or when you want to understand exactly how a protocol behaves, you stop guessing and look at what actually crossed the wire.
This lesson builds directly on two things you already know. From The TCP/IP and OSI models you learned that data is wrapped in layers: an application message sits inside a transport segment, inside an internet packet, inside a link-layer frame. A packet capture is where you see those layers stacked up, one inside the next. From TCP vs UDP, ports, and the ones to know you learned what ports identify and how TCP differs from UDP. In a capture you watch TCP set up a connection with a handshake, carry data, and tear it down, all visible as individual packets with flags.
In plain terms: you open a file, you filter down to the traffic that matters, you click a packet to expand its layers, and you follow a conversation to read it as text. The terminology (display filter, follow stream, flags, encapsulation) simply names those actions.
Captures are ground truth. Configuration files, logs, and documentation tell you what should happen. A capture tells you what did happen, at the level of individual bytes. When those two disagree, the capture wins.
Concretely, reading captures lets you:
For anyone working in networking or security, whether building systems or defending them, reading a capture is a daily, foundational skill. It is the difference between describing a problem and proving it.
Definition. Each captured packet is decoded into a stack of headers, one per protocol layer, in the exact order they are nested on the wire.
Plain explanation. When your browser sends a request, the HTTP message is placed inside a TCP segment, which is placed inside an IP packet, which is placed inside an Ethernet frame. Capturing catches the whole frame, and Wireshark unwraps it layer by layer so you can read each header separately.
How it works internally. The bytes are contiguous in memory. Wireshark starts at byte 0, reads the Ethernet header (it knows Ethernet is 14 bytes and the type field says what comes next), advances to the IP header, reads the IP header length to find where TCP starts, reads TCP to find where the payload starts, and so on. This is called dissection.
One Ethernet frame on the wire (bytes left to right):
+-----------+------------------+------------------+-------------------+
| Ethernet | IPv4 | TCP | HTTP payload |
| header | header | header | "GET / HTTP/1.1" |
| (14 bytes)| (20+ bytes) | (20+ bytes) | (variable) |
+-----------+------------------+------------------+-------------------+
link internet transport application
Click a layer in Wireshark -> it highlights that slice of bytes.
When to use it. Always, when you need to understand a single packet in detail. Expanding layers is how you read a field's exact value.
When NOT to. When you have thousands of packets and only need the shape of a conversation, expanding every layer is slow. Filter and follow-stream first, then drill in.
Common pitfall. Confusing the link layer address (MAC) with the internet layer address (IP). The MAC only identifies the next hop on the local network and changes at every router; the IP identifies the end host.
Knowledge check: In the frame above, which layer would you expand to find the destination port number, and which to find the destination IP address?
Definition. A display filter is an expression that hides every packet except the ones matching it. It changes the view, not the capture file.
Plain explanation. A busy capture has thousands of packets. A filter like tcp.port == 80 shows only web traffic on port 80; ip.addr == 10.0.0.5 shows only packets to or from that host.
How it works internally. Wireshark has already dissected every packet, so each field (like tcp.port or http.request.method) is a named value it can test. The filter is evaluated against each packet's dissected fields.
| Filter | Shows |
|---|---|
tcp.port == 443 |
Traffic on port 443 (HTTPS) in either direction |
ip.addr == 10.0.0.5 |
Any packet to or from 10.0.0.5 |
http |
Only packets the HTTP dissector recognised |
dns |
Only DNS queries and responses |
tcp.flags.syn == 1 && tcp.flags.ack == 0 |
Connection-opening SYN packets |
When to use it. First thing, on almost every capture, to cut noise down to signal.
When NOT to. Do not confuse a display filter (applied after capture, easy to change) with a capture filter (applied while capturing, uses a different syntax and permanently drops packets). Beginners mix the two up constantly.
Common pitfall. Writing ip.addr != 10.0.0.5 and expecting it to hide all traffic involving that host. Because a packet has two addresses, this expression is true whenever either address is not 10.0.0.5, which is almost always. The correct form is !(ip.addr == 10.0.0.5).
Knowledge check: Predict the result: you type
tcp.port = 80(one equals sign) into the filter bar. What happens, and what did you mean to type?
Definition. TCP flags are single control bits in the TCP header. The three-way handshake is the fixed SYN, SYN-ACK, ACK exchange that opens every TCP connection.
Plain explanation. Before TCP carries any data, both sides agree to talk. The client sends SYN (synchronise), the server replies SYN-ACK, the client replies ACK. After that, data flows. To close, FIN packets are exchanged; RST is an abrupt "go away".
Three-way handshake:
Client Server
|------------- SYN ----------->| "let's talk"
|<--------- SYN, ACK ----------| "ok, and let's talk back"
|------------- ACK ----------->| "agreed"
|======== data flows ==========|
...
|------------- FIN ----------->| "I'm done sending"
|<------------ ACK ------------|
How it works internally. The flags live in a single byte of the TCP header. Each bit is one flag. A SYN packet has the SYN bit set to 1; a SYN-ACK has both SYN and ACK set. Wireshark decodes this byte and shows [SYN], [SYN, ACK], and so on in the Info column.
| Flag | Meaning |
|---|---|
| SYN | Start a connection, synchronise sequence numbers |
| ACK | This packet acknowledges received data |
| FIN | Sender has finished sending (graceful close) |
| RST | Reset: abort the connection immediately |
| PSH | Push buffered data to the application now |
| URG | Urgent pointer field is meaningful (rare) |
When to use it. Reading the first few packets of any TCP flow tells you whether a connection even got established. No SYN-ACK means the server never accepted.
When NOT to. UDP has no handshake and no flags; do not look for SYN in a DNS-over-UDP capture.
Common pitfall. Assuming an RST means an attack. RSTs are normal: they occur when you connect to a closed port, or when an application closes abruptly. Context matters.
Knowledge check: In your own words, what does it tell you if you see a SYN from the client but the only reply is an RST from the server?
Definition. Follow TCP Stream gathers every packet belonging to one connection, sorts them into the correct order, strips the headers, and shows the application data as one continuous transcript.
Plain explanation. A single web request might be split across many packets, interleaved with other conversations. Following the stream reassembles just that one conversation so you can read it top to bottom, with each side's data shown in a different colour.
How it works internally. Wireshark identifies the connection by its four-tuple (source IP, source port, destination IP, destination port), orders packets by TCP sequence number, and concatenates the payloads. This is the single most useful reading tool, and it is exactly what the quiz question refers to.
When to use it. Whenever you want to read content rather than inspect individual packets: an HTTP exchange, an SMTP session, a plaintext login.
When NOT to. For encrypted traffic (TLS), the stream shows ciphertext, not readable content. Following a TLS stream shows you the handshake and then noise.
Common pitfall. Forgetting that following a stream also sets a display filter (tcp.stream == N). After you close the follow window, remember to clear that filter or you will think the rest of the capture vanished.
Definition. Packet capture records other people's data. In most jurisdictions, capturing traffic you are not authorised to capture is illegal (wiretapping / interception).
Plain explanation. Only capture traffic on a host you own, in a lab you control, or on a network where you have written permission. Sniffing a coffee-shop WiFi or a corporate network without authorisation is a crime, not a learning exercise.
Why this course uses fixtures. Live sniffing needs elevated privileges and a real network card, and it tempts learners into grey-area captures. So the matching exercises hand you the same header bytes in a fixed buffer and ask you to parse them, which teaches the exact skill Wireshark uses internally, safely and reproducibly.
Display filters use field operator value, combined with logical operators. This is Wireshark syntax, not code you compile.
tcp.port == 443 # equality: note the DOUBLE equals
ip.addr == 10.0.0.5 # matches source OR destination
http.request.method == "GET" # string values in quotes
tcp.flags.syn == 1 && tcp.flags.ack == 0 # AND (&&), a bare SYN
dns || http # OR (||): show either protocol
!(ip.addr == 10.0.0.5) # NOT: exclude a host correctly
tcp.stream == 3 # everything in connection #3
Key rules:
== not =. A single = is not a valid comparison and the bar turns red.&&, ||, ! (or the words and, or, not) combine expressions.tcp, then tcp.flags, then tcp.flags.syn. Click any field in a decoded packet and Wireshark shows its filter name in the status bar.The common tcpdump equivalents (a different, older syntax) for quick command-line work:
tcpdump -r capture.pcap 'tcp port 80' # read a file, keep port-80 TCP
tcpdump -r capture.pcap 'host 10.0.0.5' # keep traffic to/from a host
tcpdump -r capture.pcap -c 20 -n # first 20 packets, no name lookups
A packet capture (pcap) is a recording of network traffic. Wireshark (a graphical tool) and tcpdump (a command-line tool) can read and write these files.
Reading captures is how you verify what is actually on the wire, rather than what you assume is happening.
Wireshark shows the protocol layers from top to bottom, the same layers you have already learned:
Frame 12: 74 bytes
Ethernet II src/dst MAC ← link
Internet Protocol src/dst IP ← internet
Transmission Control Protocol src/dst port, flags, seq ← transport
Hypertext Transfer Protocol GET / ... ← application
Click any layer and Wireshark highlights its bytes. This makes encapsulation (one protocol wrapped inside another) visible.
tcp.port == 80, ip.addr == 10.0.0.5, http, dns.Only capture traffic you are authorised to capture. That means your own host, a lab, or a network you have written permission to test.
Capturing other people's traffic is wiretapping. Every lab in this course uses loopback or static fixtures.
You will not run a live sniffer here. That requires elevated privileges and a real network card.
Instead, the matching C exercises have you parse the same headers (IPv4, TCP flags, ARP frames) from fixed byte buffers. This is exactly what Wireshark does internally.
Because this is a concept lesson (no compiler), the "code" here is a realistic worked reading session: a small text-mode capture summary plus the filters and steps to make sense of it. This mirrors what you would do in Wireshark or with tcpdump on a file called web.pcap.
# 1) List the packets (like Wireshark's packet list, or `tcpdump -r web.pcap -n`)
No. Time Source Dest Proto Length Info
1 0.000000 10.0.0.20:51000 93.184.216.34:80 TCP 74 [SYN] Seq=0
2 0.014200 93.184.216.34:80 10.0.0.20:51000 TCP 74 [SYN, ACK] Seq=0 Ack=1
3 0.014300 10.0.0.20:51000 93.184.216.34:80 TCP 66 [ACK] Seq=1 Ack=1
4 0.014900 10.0.0.20:51000 93.184.216.34:80 HTTP 145 GET / HTTP/1.1
5 0.030100 93.184.216.34:80 10.0.0.20:51000 TCP 66 [ACK] Seq=1 Ack=80
6 0.061500 93.184.216.34:80 10.0.0.20:51000 HTTP 512 HTTP/1.1 200 OK (text/html)
7 0.061700 10.0.0.20:51000 93.184.216.34:80 TCP 66 [ACK] Seq=80 Ack=447
8 0.100000 10.0.0.20:51000 93.184.216.34:80 TCP 66 [FIN, ACK]
9 0.114000 93.184.216.34:80 10.0.0.20:51000 TCP 66 [FIN, ACK]
10 0.114200 10.0.0.20:51000 93.184.216.34:80 TCP 66 [ACK]
# 2) Narrow to just this web conversation
Display filter: tcp.port == 80
# 3) Expand packet 4 to read its layers (the encapsulated view)
Frame 4: 145 bytes on wire
Ethernet II Src: aa:bb:cc:00:11:22 Dst: 00:11:22:33:44:55
Internet Protocol Version 4 Src: 10.0.0.20 Dst: 93.184.216.34
Transmission Control Protocol Src Port: 51000 Dst Port: 80 Flags: [PSH, ACK]
Hypertext Transfer Protocol
GET / HTTP/1.1\r\n
Host: example.com\r\n
# 4) Right-click packet 4 -> Follow -> TCP Stream
(reassembled, client in one colour, server in another)
GET / HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Content-Type: text/html
...<html>...
What this session does. Packets 1-3 are the three-way handshake (SYN, SYN-ACK, ACK), so we know the connection opened. Packet 4 is the HTTP request; packet 6 is the response. Packets 8-10 close the connection with FIN/ACK. The filter tcp.port == 80 removed unrelated traffic, and Follow TCP Stream reassembled the request and response into readable text.
Expected result. After following the stream you can read the full GET / HTTP/1.1 request and the HTTP/1.1 200 OK response as plain text, because HTTP on port 80 is unencrypted.
Edge cases to note. If the response were HTTPS (port 443), Follow Stream would show a TLS handshake and then ciphertext, unreadable without keys. If packet 2 (SYN-ACK) were missing and you instead saw an RST, the connection never opened. If packets arrived out of order, Wireshark would still reassemble them correctly using sequence numbers.
Walking the packet list above, packet by packet:
| Pkt | Flags / Info | What it means |
|---|---|---|
| 1 | [SYN] Seq=0 |
Client 10.0.0.20 opens a connection from ephemeral port 51000 to the server on port 80. Sequence starts at 0 (Wireshark's relative view). |
| 2 | [SYN, ACK] Ack=1 |
Server accepts. Ack=1 acknowledges the client's SYN (SYN counts as 1 byte). |
| 3 | [ACK] Ack=1 |
Client confirms. Handshake complete; the connection is now established. |
| 4 | GET / HTTP/1.1 |
First data packet. Length 145 is bigger because it carries the HTTP request bytes. |
| 5 | [ACK] Ack=80 |
Server acknowledges 79 bytes of request data (relative ack jumps from 1 to 80). |
| 6 | HTTP/1.1 200 OK |
The response body. Now the client will acknowledge these bytes. |
| 7 | [ACK] Ack=447 |
Client confirms it received the 446 bytes of response. |
| 8-10 | FIN, ACK x2 then ACK |
Graceful shutdown: each side sends FIN, each is acknowledged. Connection closed. |
How the values change over time: the Seq and Ack numbers are counters. Each side's Seq counts bytes it has sent; the other side's Ack says "I have received everything up to here." You can watch the client's Ack climb from 1 to 447 as it receives the response, and the server's Ack climb from 1 to 80 as it receives the request. This is TCP's reliability in action: every byte is accounted for.
When you expand packet 4 (step 3 of the session), memory-wise you are looking at one contiguous byte buffer. Wireshark reads the Ethernet header first (14 bytes), sees the type field says IPv4, jumps to the IP header, reads that the payload is TCP, jumps to the TCP header, reads that the destination port is 80 and hands the rest to the HTTP dissector, which finds the GET line. Each "jump" is just an offset added to a pointer into the buffer, which is precisely what the companion C exercises make you do by hand.
Mistake 1: Using = instead of == in a display filter.
WRONG: tcp.port = 80
RIGHT: tcp.port == 80
Why it is wrong: Wireshark's filter language uses == for equality. A single = is not valid; the filter bar turns red and nothing is applied. Recognise it by the red bar and the fact that your packet list did not change.
Mistake 2: Excluding a host with != on an address field.
WRONG: ip.addr != 10.0.0.5 # still shows almost everything
RIGHT: !(ip.addr == 10.0.0.5) # truly hides that host
Why it is wrong: every packet has two addresses. ip.addr != 10.0.0.5 is true whenever either the source or the destination is not that host, which is nearly always. Negate the whole equality instead. This exact trap applies to any field that appears twice in a packet (also tcp.port, eth.addr).
Mistake 3: Confusing a capture filter with a display filter.
Capture filters (BPF syntax, e.g. tcp port 80) are applied while capturing and permanently drop packets. Display filters (e.g. tcp.port == 80) are applied afterward and are reversible. Typing BPF syntax into the display bar, or vice versa, silently fails. Prevent it by remembering: the green bar at the top of the main window is the display filter.
Mistake 4: Trying to read encrypted traffic as text.
Following a TLS stream (port 443) and expecting to see a password. Why it is wrong: TLS encrypts everything after the handshake, so the stream is ciphertext. The fix is to look for cleartext protocols (HTTP, FTP, Telnet) in the lab, and to understand that encryption is exactly what prevents this reading, which is the point of TLS.
Mistake 5: Interpreting every RST as malicious.
An RST simply means "this connection is being aborted." It happens when you hit a closed port, when a firewall rejects you, or when an app crashes. Read the surrounding packets before concluding anything. Prevent misreadings by checking whether a handshake ever completed.
This is an analysis skill, so "debugging" means your reading of the capture is not making sense. Common situations and how to work through them:
"My filter shows nothing."
= instead of ==), so fix the expression.tcp.stream == N filter set from a previous Follow Stream, clear the bar.tcp or ip to confirm packets are present at all."I do not see the handshake."
"Follow Stream shows garbage."
"The bytes I parse by hand (in the C exercise) do not match Wireshark."
0x00 0x1C is 28, not 7168.Questions to ask when a capture confuses you:
This is a concept lesson with no C to compile, but the reading skills here connect directly to memory-safe parsing in the companion C exercises, and that connection is worth making explicit.
When you parse header bytes by hand (as in Parse the fixed fields of a mock IPv4 header or Decode TCP flag byte into individual flags), you are indexing into a byte buffer using offsets you compute from the data itself. That is a classic setting for memory-safety bugs:
length before indexing, or you read out of bounds.The defensive habit: parsers of untrusted network data must be fail-closed. Validate every length and offset against the real buffer size before dereferencing. Historically, packet-parsing code that skipped these checks has been the source of serious vulnerabilities (buffer over-reads that leak memory). Reading captures in Wireshark is safe; writing the parser is where the discipline lives.
Concrete real-world use. A backend service intermittently times out talking to a database. Logs are inconclusive. An engineer captures traffic on the app server with tcpdump -w db.pcap 'host 10.0.0.9 and port 5432', reproduces the failure, opens the file in Wireshark, filters to the failing connection, and sees the SYN going out with no SYN-ACK coming back, then a retransmit, then a timeout. That single view proves the problem is network reachability (a firewall or routing issue), not the application. The capture turned a day of guessing into a five-minute diagnosis.
Other everyday uses: verifying that a client really upgraded to TLS, confirming a DNS query returned the expected address, spotting a plaintext credential that should never have been sent unencrypted, and measuring how long a server took to respond.
Professional best-practice habits:
host X and port Y) so the file is small and contains only relevant, authorised traffic. Huge captures are hard to read and may sweep up data you should not have.| Level | What it looks like |
|---|---|
| Beginner | Opens a saved pcap, applies one filter, follows a TCP stream, identifies the handshake and reads a cleartext HTTP exchange. |
| Advanced | Writes precise multi-clause filters, reads sequence/ack numbers to spot retransmissions and window issues, uses tcpdump on headless servers, correlates captures with logs, and writes bounds-checked parsers for custom protocols. |
Beginner 1 - Label a handshake. Objective: Given the 10-packet listing in the Code section, identify the three packets that form the TCP three-way handshake and state the flags on each. Requirements: Name the packet numbers and the flag(s) each carries; state at which packet the connection becomes established. Constraints: Use only the listing provided. Hint: The handshake is always SYN, then SYN-ACK, then ACK, at the very start. Concepts: TCP flags, the handshake.
Beginner 2 - Write three filters.
Objective: Write Wireshark display filters for three goals.
Requirements: (a) show only DNS traffic; (b) show only traffic to or from 192.168.1.50; (c) show only bare SYN packets (SYN set, ACK not set).
Example expected answers use the fields dns, ip.addr, tcp.flags.syn, tcp.flags.ack.
Constraints: Use display-filter syntax with ==, &&, and correct field names.
Hint: A bare SYN is tcp.flags.syn == 1 && tcp.flags.ack == 0.
Concepts: Display filters, flags.
Intermediate 1 - Follow the money (bytes).
Objective: Using the sequence and acknowledgement numbers in the listing, explain how you can tell the client received the entire HTTP response.
Requirements: Reference the specific Seq/Ack values; explain what the final client Ack value proves.
Input/output: Point to packet 7 (Ack=447) in your explanation.
Constraints: Explain in prose; no tools needed.
Hint: An Ack value means "I have received everything up to this byte."
Concepts: TCP reliability, sequence/ack numbers.
Intermediate 2 - Spot the failure. Objective: Describe, packet by packet, what a failed connection to a closed port looks like, and contrast it with a filtered (firewalled) port. Requirements: State the flags you would see in each case (closed port vs. silently dropped), and how a capture distinguishes "connection refused" from "no route / dropped". Constraints: Concept answer; reference SYN, RST, and the absence of a reply. Hint: A closed port typically replies with RST; a firewall that drops packets replies with nothing (you see retransmitted SYNs). Concepts: Handshake, flags, RST, diagnosing reachability.
Challenge - Manual offset walk (bridge to the C exercise).
Objective: On paper, given a 20-byte IPv4 header where the first byte is 0x45, determine the IP version, the header length in bytes, and the byte offset at which the TCP header begins in the frame (assume a 14-byte Ethernet header before it). Then explain the one bounds check you must perform before reading the TCP source port.
Requirements: Show how 0x45 splits into version (high nibble) and IHL (low nibble); convert IHL (in 32-bit words) to bytes; compute the TCP start offset; state the length check.
Constraints: No code required, but your reasoning should map directly onto the Parse the fixed fields of a mock IPv4 header exercise.
Hint: IHL is measured in 4-byte words, so IHL 5 means a 20-byte IP header. TCP starts at 14 + 20. Before reading a 2-byte port at that offset, confirm the buffer holds at least offset + 2 bytes.
Concepts: Encapsulation, header offsets, big-endian fields, bounds checking untrusted input.
A packet capture (pcap) is a byte-for-byte recording of network traffic; Wireshark reads it graphically and tcpdump on the command line. It is ground truth: what actually crossed the wire, not what you assumed.
The core reading workflow: (1) apply a display filter to cut noise (tcp.port == 80, ip.addr == ..., http, dns), remembering == not = and !(ip.addr == x) to exclude a host; (2) expand a packet's layers to see encapsulation (Ethernet -> IP -> TCP/UDP -> application) and read exact field values; (3) recognise the three-way handshake (SYN, SYN-ACK, ACK) and decode the flags (SYN, ACK, FIN, RST, PSH, URG); (4) Follow TCP Stream to reassemble a scattered conversation into readable text, which only works for cleartext, not TLS.
Common mistakes: = vs ==, misusing != on address fields, confusing capture and display filters, expecting to read encrypted traffic, and treating every RST as an attack.
Remember: only capture traffic you are authorised to capture. And when you write a parser for these headers (the companion C exercises), treat every length and offset from the wire as untrusted, validate against the real buffer size, and respect big-endian byte order. Reading is safe; parsing is where the safety discipline lives.