Build a TCP/IP Stack from Scratch · Module 02

Mini-quiz & Hands-on Exercises

Mini-quiz & hands-on exercises

You've now got your own ARP implementation running in user space — that's a big leap.

Before wrapping up this module, let's make sure the ideas and code really stick.

Quick concept check

Q1. What information does ARP provide that IP alone cannot?

(Hint: think "who is physically reachable at this IP?")

Answer

ARP provides MAC addresses. IP only gives you the logical network address, but to actually send an Ethernet frame, you need the physical hardware address (MAC) of the destination on the local network segment.

Q2. When your stack sends an ARP reply, which two addresses must be swapped compared to the request?

Answer

The source and destination addresses in both the Ethernet header and ARP payload:

  • Ethernet: requester's MAC becomes destination, your MAC becomes source
  • ARP: requester's IP/MAC become target, your IP/MAC become sender

Q3. What is the EtherType value for ARP frames?

a) 0x0800 b) 0x0806 c) 0x86DD

Answer

b) 0x0806 — This identifies ARP frames in the Ethernet header.

  • 0x0800 = IPv4
  • 0x86DD = IPv6

Q4. Why did we set arp_ignore=8 for the kernel?

Answer

To prevent the Linux kernel from automatically responding to ARP requests. With arp_ignore=8, only your userspace program will handle ARP, allowing you to implement your own ARP responder.

Q5. True or False: Ethernet frames can only be broadcast.

Answer

False. Ethernet frames can be:

  • Unicast (to a specific MAC address)
  • Broadcast (to ff:ff:ff:ff:ff:ff)
  • Multicast (to special group addresses)

Most traffic is unicast after ARP resolution completes.

Practical exercises

Exercise 1 — Log everything

Add simple logging to your stack so that every received frame is timestamped and shows:

[time] from <src MAC> to <dst MAC> type=0x0806 len=<n>

This will help when you begin handling multiple protocols later.

Hint: Use time() or gettimeofday() to get timestamps.

Exercise 2 — Make your MAC configurable

Right now, your stack's MAC is whatever the kernel assigns to tap0.

Modify your setup so your program explicitly sets a custom MAC (for example 02:00:00:00:00:01).

Tip: you can use ioctl(SIOCSIFHWADDR) on the TAP interface after creation.

Exercise 3 — Cache known hosts

Implement a small in-memory table that remembers (IP → MAC) pairs for each ARP reply you see.

Print it whenever a new mapping is added.

Example output:

[ARP cache] 10.10.0.3 -> 02:42:0a:0a:00:03
[ARP cache] 10.10.0.4 -> 02:42:0a:0a:00:04

Exercise 4 — Detect unknown EtherTypes

Instead of silently ignoring frames with unrecognized EtherTypes, print a warning like:

[unknown] EtherType 0x1234 len=84

This will help you debug later when you add IPv4, ICMP, or custom protocols.

Exercise 5 — Try breaking things

Send an ARP request for an IP that isn't yours (e.g., 10.10.0.9) and confirm your stack stays silent.

Then change your program so it replies incorrectly — see how that confuses the client's ARP cache.

Understanding wrong behavior is often the fastest way to grasp protocol logic.

Challenge (optional)

Implement ARP request generation yourself instead of using arping.

Write a tiny function that broadcasts an ARP query and prints any replies your stack receives.

You'll reuse this logic when your stack becomes a full IP host.

Example function signature:

void send_arp_request(int fd, const uint8_t target_ip[4]);

Checkpoint summary

You can decode, respond to, and manipulate ARP frames. You understand the Ethernet header structure. You can trace your own packets through tcpdump. You're thinking like a network engineer and programming like a systems hacker.

Next up, we'll take those ARP-resolved addresses and move to Layer 3: IPv4 & ICMP — the stage where your custom stack finally starts speaking "ping."