Core Question
Core Question
While an IP address is still unresolved, where does the packet that wanted to go there actually go?
Outcome
Outcome
By the end of this session, the learner should be able to:
- explain why the IP layer doesn't block waiting for an ARP reply
- name the cap on how many packets can wait behind one unresolved IP
- explain exactly which packet gets dropped when that cap is exceeded
- describe what
flush()anddrop_all()each do to the queue, and when the responder loop calls which one
Read Order
Read Order
- Read the module comment at the top of the file
- Read
PendingQueue - Read
enqueue() - Read
flush() - Read
drop_all() - Run
examples/arp/session_02_walkthrough.py
Read It Like Code
Read It Like Code
PendingQueue(
waiting,
)Fields That Matter
Fields That Matter
| Field | Why it matters |
|---|---|
waiting | One tuple per destination IP — the line of packets parked behind an unresolved mapping. |
Decision Flow
Decision Flow
enqueue appends the new packet, then checks length: len(updated) > MAX_QUEUE_PER_IP -> slice to the LAST MAX_QUEUE_PER_IP entries, return QUEUED_DROPPED_OLDEST otherwise -> keep everything, return QUEUED flush(ip) -> pop and return the whole tuple for that IP (queue for that IP is now gone) drop_all(ip) -> pop and discard the whole tuple for that IP (nothing is returned)
Reading Lens
Reading Lens
The important move in this session is to stop thinking of ARP resolution as something the sender blocks on and start asking:
- where does a packet physically sit while its destination is unresolved?
- when the line for one IP is full, which packet is sacrificed — the newest arrival or the oldest one already waiting?
- does completing resolution give the packets back, or does failing resolution give them back?
Toy Model Boundary
Toy Model Boundary
Real IP stacks queue at the interface or neighbor-cache level with hardware and driver-specific limits, and some implementations queue only the single most recent packet per destination rather than a short FIFO. This toy fixes MAX_QUEUE_PER_IP = 3 for every destination and uses a plain tuple slice to drop the oldest, which keeps the eviction rule readable as one line of code instead of a configurable policy. There's no notion of packet priority, size, or per-queue timeout here — a packet parked in PendingQueue.waiting sits there until flush() or drop_all() is called explicitly by the responder loop; nothing here times it out on its own.
Code Landmarks
Code Landmarks
The module comment
Names the thesis directly: "the IP layer doesn't block on ARP." A packet to an unresolved destination parks in a line instead of stalling the sender.
enqueue()
The main reading target. It always appends first — current + (packet_label,) — and only afterward checks whether the result is too long. The drop is a slice, updated[-MAX_QUEUE_PER_IP:], which keeps the most recent MAX_QUEUE_PER_IP entries and discards everything before them — drop-oldest, not drop-newest.
flush()
queue.waiting.pop(ip, ()) — this is a genuine pop, not a copy-and-clear. After flush(), the IP has no entry in waiting at all, which matters if you check ip in queue.waiting afterward.
drop_all()
Nearly identical to flush() — queue.waiting.pop(ip, None) — but the return value is thrown away by the caller's intent, not by the function signature. drop_all() still returns via pop, it's just that nobody is meant to read what comes back; the whole point is resolution failed and there is nothing deliverable.
Failure Questions
Failure Questions
Use the source file to answer these:
- Four packets are enqueued in order
p1, p2, p3, p4for the same IP. Which three survive, and what outcome value does the fourthenqueue()call return? - What does
enqueue()return on the very first packet for a brand-new IP — is it everQUEUED_DROPPED_OLDESTon the first call? - After
flush(queue, ip)runs, what doesqueue.waiting.get(ip)return if you callenqueue()again for that same IP right after? - What is the return type of
drop_all(), and does its caller in this module ever use the return value? - If
MAX_QUEUE_PER_IPpackets are already queued and one more arrives, exactly how many packets are inqueue.waiting[ip]immediately after that call?
Walkthrough
Walkthrough
Run this:
PYTHONPATH=src python3 examples/arp/session_02_walkthrough.py
The walkthrough queues three packets in order, queues a fourth to show the oldest gets dropped, flushes the survivors and confirms the line empties, and separately shows drop_all() discarding a queue without returning anything useful.
Done When
Done When
The learner can say all of the following without looking at notes:
- "The IP layer never blocks on ARP — an unresolved packet parks in a per-IP queue instead."
- "The queue caps at MAX_QUEUE_PER_IP and drops the oldest packet, not the newest, once it's full."
- "flush() hands back everything and empties the line; drop_all() empties the line and hands back nothing anyone uses."
References
References
- RFC 826 (An Ethernet Address Resolution Protocol)
Continue