Hands-on Test: Observe Packets
Hands-on test: Observe packets
Let's see the traffic you just generated. We'll capture packets inside the containers and identify the layers by eye.
5.1 Watch ARP and ICMP on the wire
Start a capture on the stack container:
lab git:(main) docker compose exec stack bash -lc 'tcpdump -ni eth0 -vvv -l arp or icmp'
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytesIn another terminal, send a ping from the client:
docker compose exec client ping -c 3 10.10.0.4Back in the capture window, you should see something like:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:28:58.137481 IP (tos 0x0, ttl 64, id 3157, offset 0, flags [DF], proto ICMP (1), length 84)
10.10.0.3 > 10.10.0.4: ICMP echo request, id 3, seq 1, length 64
16:28:58.137511 IP (tos 0x0, ttl 64, id 45375, offset 0, flags [none], proto ICMP (1), length 84)
10.10.0.4 > 10.10.0.3: ICMP echo reply, id 3, seq 1, length 64
16:29:03.193121 ARP, Ethernet (len 6), IPv4 (len 4), Request who-has 10.10.0.4 tell 10.10.0.3, length 28
16:29:03.193153 ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.10.0.4 is-at 02:42:0a:0a:00:04, length 28
Understanding the Output
That output is exactly what we want to see — it's the first glimpse of your "network" coming alive. Let's unpack it a bit.
ICMP Messages
The first two lines are ICMP (Internet Control Message Protocol) packets — the famous ping.
- The
10.10.0.3 > 10.10.0.4: ICMP echo requestmeans the client sent a "Hello, are you there?" message. - The immediate reply
10.10.0.4 > 10.10.0.3: ICMP echo replyis the stack container saying "Yep, I'm here."
These are standard Layer 3 (network layer) messages carried inside IP packets.
ARP Messages
Then you see two ARP (Address Resolution Protocol) frames. ARP lives one layer lower, at Layer 2.
Before the client can send that ICMP request, it needs the MAC address of 10.10.0.4 to build the Ethernet frame.
So it broadcasts a request:
"Who has 10.10.0.4? Tell 10.10.0.3."
The stack container answers:
"I do! My MAC is 02:42:0a:0a:00:04."
Once that mapping is cached, the client can keep sending packets directly to that MAC address without asking again.
Layer Summary
So, in those four lines, you just watched all the layers in motion:
- ARP — translating IP to MAC.
- Ethernet — carrying the frames.
- IP — wrapping the ICMP message.
- ICMP — giving you the visible "ping" response.
Congratulations — that's a full network exchange, end to end, happening entirely in your virtual lab.
5.2 Peek at the Ethernet frame
Capture one packet in hex on the stack side:
docker compose exec stack bash -lc \
'tcpdump -XX -s 0 -c 1 -ni eth0 icmp'You'll see a hex dump like:
16:33:56.909265 IP 10.10.0.3 > 10.10.0.4: ICMP echo request, id 4, seq 1, length 64
0x0000: 0242 0a0a 0004 0242 0a0a 0003 0800 4500 .B.....B......E.
0x0010: 0054 ef70 4000 4001 371e 0a0a 0003 0a0a .T.p@.@.7.......
0x0020: 0004 0800 c1fb 0004 0001 f4e3 e768 0000 .............h..
0x0030: 0000 8ddf 0d00 0000 0000 1011 1213 1415 ................
0x0040: 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 ...........!"#$%
0x0050: 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 &'()*+,-./012345
0x0060: 3637 67
1 packet captured
Beautiful — that's a single ICMP Echo Request captured in raw bytes.
Try to spot:
- EtherType
0x0800for IPv4 (in the Ethernet header). - IPv4 first byte
0x45(version 4, header length 5×4=20 bytes). - ICMP type (8 request or 0 reply).
Layer-by-Layer Analysis
Let's walk through it layer by layer, so you can start to see the structure behind all those hex digits.
Layer 2 — Ethernet
The first 14 bytes form the Ethernet header:
0x0000: 0242 0a0a 0004 0242 0a0a 0003 0800
Break it down:
- Destination MAC:
02:42:0a:0a:00:04→ the stack container - Source MAC:
02:42:0a:0a:00:03→ the client container - EtherType:
0800→ IPv4 payload follows
That 0800 is a little flag saying, "the next protocol inside me is IP."
Layer 3 — IPv4
Next comes the IP header:
4500 0054 ef70 4000 4001 371e 0a0a 0003 0a0a 0004
Let's read the first few fields:
45→ Version 4 (4), Header Length 5×4 = 20 bytes (5)00→ Type of Service (not used here)0054→ Total length = 84 bytesef70→ Identification (used for fragmentation)4000→ Flags + Fragment offset (no fragmentation)40→ TTL = 64 hops01→ Protocol = ICMP371e→ Header checksum0a0a0003→ Source IP = 10.10.0.30a0a0004→ Destination IP = 10.10.0.4
Already, this tells us it's an ICMP packet from the client to the stack, total size 84 bytes.
Layer 4 — ICMP
The ICMP message starts right after the 20-byte IP header:
0800 c1fb 0004 0001 ...
08→ Type 8 (Echo Request)00→ Code 0 (standard ping)c1fb→ ICMP checksum0004→ Identifier0001→ Sequence number
That's the "hello" message your ping sent.
The stack's kernel will reply with the same ID and sequence number, but with Type 0 (Echo Reply) instead of 8.
Payload
Everything after that (f4e3 e768 … 3637) is just filler data — 56 bytes by default.
It doesn't carry meaning for ICMP; it's there so you can measure round-trip time with real packet sizes.
The Big Picture
So in this one line of tcpdump, you're literally seeing three layers stacked together:
Ethernet header → IP header → ICMP payload
That's the same nesting you'll recreate in code later — constructing and parsing these exact bytes by hand.
Exercise
Draw the packet as a raw byte array and then separate each section and try to understand the concept of encapsulation.
If you take a closer look you'll see that the IP header contains a total length value set to 84 bytes. This includes the IP header (20 bytes) and the ICMP header (8 bytes) plus the payload of 56 bytes, resulting in an IP packet of 84 bytes that is encapsulated in an Ethernet frame of 98 bytes total (84 plus 14 Ethernet Header).
Encapsulation is a big concept in the layer stack representation of networking.
5.3 Quick sanity checks
- If ARP never appears: are both containers on
10.10.0.0/24? - If ping times out: check
docker compose psand tryip -brief addrin both containers. - If tcpdump shows nothing: make sure you're capturing on the right interface (
eth0inside containers).
You've just confirmed three things: containers can talk, ARP resolves neighbors, and ICMP packets are flowing. This is the exact viewpoint you'll keep as you start replacing the kernel's behavior with your own stack.