Internettcpdumpwiresharkethernetiptcp

Packet Inspector: Read a Frame Field by Field

A single captured frame carrying an HTTP request, decoded layer by layer — Ethernet (src MAC, dst MAC, EtherType), IP (src, dst, TTL, protocol), TCP (ports, seq, ack, flags), HTTP — with, for every field, who wrote it, who reads it, and who is allowed to change it.

Educational modelLinux
▶ InteractiveInterview question
Progress

The problem

tcpdump prints one dense line per packet and Wireshark shows a tree of a hundred fields. Which dozen of them matter, what does each one mean, and which device on the path put it there?

One line of tcpdump, decoded

Educational model

This is the frame that carries a small plaintext HTTP request from a laptop to a server. The capture is simplified — real HTTPS traffic is encrypted after the TLS handshake, so the HTTP layer would be opaque bytes; use port 80 in a lab if you want to see it — and the addresses are documentation ranges. Everything the inspector shows is an educational model: the fields are real, the values are invented.

Read the line left to right: timestamp; link-layer addresses and EtherType; then IP addresses with ports appended after a dot; then TCP flags, sequence range, acknowledgment, window; then the decoded payload. -e adds the Ethernet header, -n disables name resolution so you see numbers, -v adds TTL and the protocol.

`tcpdump -e -n -v -i eth0 port 80` (one frame, simplified)
12:04:31.118 aa:bb:cc:00:00:01 > aa:bb:cc:00:00:0a, ethertype IPv4 (0x0800), length 154:
    (tos 0x0, ttl 64, id 41213, offset 0, flags [DF], proto TCP (6), length 140)
    10.0.0.4.54001 > 203.0.113.10.80: Flags [P.], seq 1:101, ack 1, win 502, length 100: HTTP: GET / HTTP/1.1

  aa:bb:cc:00:00:01 > aa:bb:cc:00:00:0a   src MAC (laptop) > dst MAC (the GATEWAY, not the server)
  ethertype IPv4 (0x0800)                 what the frame carries
  ttl 64                                  hops remaining; Linux default 64
  flags [DF]                              don't fragment — PMTUD in use
  proto TCP (6)                           what the packet carries
  10.0.0.4.54001 > 203.0.113.10.80        src IP.port > dst IP.port — the 4-tuple
  Flags [P.]                              PSH + ACK ("." is ACK in tcpdump notation)
  seq 1:101                               relative sequence numbers: bytes 1..100 of this stream
  ack 1                                   next byte expected from the server
  win 502                                 receive window in units of the scale factor
  length 100                              TCP payload bytes

Ethernet: the current hop

Destination MAC (6 bytes) is the first field on the wire for a reason: a switch or NIC can decide what to do with the frame before the rest has arrived. Here it is the gateway’s MAC, not the server’s — the laptop knows 203.0.113.10 is off-link and sent the frame to the router. Source MAC is the laptop’s interface. EtherType (2 bytes) tells the receiver’s driver which protocol handler gets the payload: 0x0800 IPv4, 0x86DD IPv6, 0x0806 ARP, 0x8100 a VLAN tag that shifts the real EtherType by four bytes.

Who reads it: every switch on the segment (destination only) and the receiving NIC (destination, to accept or ignore; EtherType, to dispatch). Who rewrites it: every router, entirely, because the next link has different endpoints. The frame check sequence at the end is computed by the sending NIC and verified by the receiving one; a mismatch is a silent drop counted in the interface’s rx_crc_errors.

IP: the whole trip

Source IP and destination IP (4 bytes each in IPv4, 16 in IPv6) are the end-to-end addresses. Every router reads the destination to choose a next hop; nothing on the path should change either — except a NAT, which rewrites the source outbound and the destination on the reply, and an L4 load balancer, which may rewrite the destination to a backend. TTL (IPv4) / hop limit (IPv6) starts at a sender default — 64 on Linux and macOS, 128 on Windows, 255 on many routers — and every router subtracts one; at zero the packet is discarded and an ICMP *Time Exceeded* goes back to the source. It is the only field routinely modified in transit, and with it the IPv4 header checksum.

Protocol (IPv4) / next header (IPv6) says what is inside: 6 TCP, 17 UDP, 1 ICMP (58 ICMPv6), 47 GRE, 50 ESP. Firewalls match on it. Total length lets the receiver find the end of the packet inside a frame that may be padded to Ethernet’s 64-byte minimum. Identification, flags, fragment offset support IPv4 fragmentation; DF set means "drop and tell me rather than fragment". DSCP/TOS requests a service class that most of the internet ignores and some enterprise networks honour.

  • Written by: the sending host’s IP layer. Read by: every router (destination, TTL). Changed by: every router (TTL, checksum); NAT and L4 balancers (addresses).
  • The IPv4 header checksum covers only the header, so it must be recomputed at each hop after the TTL change; IPv6 has no header checksum at all.

TCP: the conversation

Source port (54001, ephemeral, chosen by the client kernel) and destination port (80, chosen by the application) complete the 4-tuple that identifies this connection at both ends. Sequence number is the position of the first payload byte in the client → server byte stream; tcpdump shows it relative to the initial number from the handshake, so seq 1:101 means the first 100 bytes after the SYN. Acknowledgment number is the next byte the sender expects from the peer — ack 1 says "I have received your SYN and nothing else yet". Flags describe the segment’s role: SYN (open), ACK (the ack field is valid — set on every segment after the first), PSH (deliver to the application now), FIN (I am done sending), RST (abort; nothing listens or state is gone). Window is how many more bytes the sender is willing to receive, multiplied by a scale factor negotiated in the SYN; a window of 0 means "stop".

Who reads it: only the two end hosts’ kernels — and any middlebox that keeps connection state (NAT, stateful firewall, L4 balancer), which is why those devices can break things routers cannot. Who changes it: nobody, except a NAT rewriting the source port and recomputing the checksum, and an MSS-clamping router editing the MSS option in a SYN. The checksum covers the header, the payload and a pseudo-header containing both IP addresses, so a segment that arrives at the wrong host fails it even if the bytes are intact.

HTTP: the meaning

The first 100 bytes of payload here are GET / HTTP/1.1\r\nHost: engineer-atlas.dev\r\n… — the request line, then headers, then a blank line. This is the only layer whose content the *application* wrote and the only one it will ever see; in real traffic it is inside TLS, so the capture would show Application Data records of random-looking bytes and the inspector could not decode them without the session keys. The Host header is what lets one IP address serve many sites, and the reason a server behind a load balancer needs X-Forwarded-For to know who really sent the request — the IP header’s source, by the time it arrives, is the balancer’s.

Who reads it: the server process (and an L7 proxy, which terminates TLS and re-sends). Who changes it: an L7 proxy may add or rewrite headers; nothing else on the path can. The transport does not know where one request ends and the next begins — the HTTP parser has to, using Content-Length or chunked encoding.

The dozen fields that matter
FieldWritten byRead byChanged by
dst MACsender on this link (via ARP/ND)switches, receiving NICevery router
src MACsending interfaceswitches (learning), receiverevery router
EtherTypesending link layerreceiving drivernobody (VLAN tag inserts)
src / dst IPsending hostevery router (dst)NAT, L4 balancer
TTLsending hostevery routerevery router (−1)
protocolsending hostreceiving IP layer, firewallsnobody
src / dst portclient kernel / applicationboth kernels, stateful middleboxesNAT (src)
seq / ackboth kernelsboth kernelsnobody
flagsboth kernelsboth kernels, stateful firewallsnobody
windowreceiving kernelsending kernelnobody
HTTP requestapplicationserver process, L7 proxyL7 proxy

Key points

  • Ethernet fields name the current hop; IP fields name the whole trip; TCP fields name the conversation; HTTP is the only part the application wrote.
  • Destination MAC on an off-link packet is the gateway’s, never the server’s.
  • TTL is the one field routers modify by design; MACs are rewritten wholesale; IP addresses and ports change only at NATs and balancers.
  • Sequence and acknowledgment numbers are byte positions in each direction of the stream; tcpdump shows them relative to the handshake.
  • The TCP checksum includes the IP addresses via a pseudo-header, so a NAT that rewrites an address must also fix the TCP checksum.
  • Real HTTPS captures show TLS records, not HTTP; decoding needs the session keys.

Why does this exist?

Mechanisms are answers to constraints. Open each question before reading the answer.

Why is the destination MAC the first bytes on the wire?

So hardware can begin a forwarding decision before the frame has finished arriving (cut-through switching) and so a NIC can discard frames not meant for it at line rate without involving software.

Why does TCP’s checksum include IP addresses it does not own?

To catch misdelivery: if a packet reaches the wrong host with intact bytes, the pseudo-header mismatch fails the checksum. It couples TCP to IP slightly — which is exactly why NATs must recompute it.

Why show relative sequence numbers?

Initial sequence numbers are random 32-bit values for security; humans want "byte 1 to 100". The tool subtracts the ISN from the handshake it saw; if the capture missed the handshake, numbers appear absolute.

Packet inspector

Packet inspector
Click a header, then a field, to learn who on the path is allowed to read or rewrite it.
Plain HTTP on port 80 so the payload is visible; with TLS the HTTP box would be ciphertext.Educational model
Who reads / rewrites src port? Endpoint-only, except NAT rewrites it to keep mappings unique; firewalls and L4 load balancers read it.
tcpdump -nn (simplified)
12:00:01.026 IP 192.168.1.10.51234 > 93.184.216.34.80: Flags [P.], seq 1001:1078, ack 5001, win 64240, length 77: HTTP: GET / HTTP/1.1
Switches stop at Ethernet. Routers stop at IP (and rewrite Ethernet). NAT reaches into the ports. Only the two endpoints ever parse TCP sequence numbers or HTTP.

How it fails

What the failure looks like from inside real software.

  • Capturing on the sender and seeing "bad checksum" on every packet: checksum offload means the NIC fills it in after the capture point. Not a bug.
  • Capturing HTTPS and seeing no HTTP: the payload is TLS. Either capture at the proxy after termination or export SSLKEYLOGFILE from the client.
  • Segments larger than the MTU in a sender-side capture: TSO/GSO hands the NIC super-segments; the wire sees MSS-sized frames.
  • A RST immediately after a SYN: nothing is listening on that port — the packet reached the host, so routing and firewalling are fine.
  • Repeated retransmissions of the same seq with no ack: loss on the path, or the peer is gone; the sender backs off 1 s, 2 s, 4 s … then gives up after ~15 tries.