Core Question
Core Question
A server that answers a DISCOVER has set an address aside for a client that might never come back. How long does it wait, and how does it find out — without ever hearing directly from the client — that it lost?
Outcome
Outcome
By the end of this session, the learner should be able to:
- explain why
Offeris a proposal and not a promise - compute staleness from
made_atandOFFER_HOLD_SECONDSwithout looking at the function body - state the
offer_is_stale()boundary condition precisely, including which side ofnow == made_at + OFFER_HOLD_SECONDSis stale - explain why
accept_offer()echoes bothserver_idandip, and what that echo does for servers that were not chosen
Read Order
Read Order
- Read
OFFER_HOLD_SECONDS - Read
Offer - Read
make_offer() - Read
offer_is_stale() - Read
accept_offer() - Run
examples/dhcp/session_02_walkthrough.py
Read It Like Code
Read It Like Code
Offer(
ip,
lease_seconds,
server_id,
made_at,
)Fields That Matter
Fields That Matter
| Field | Why it matters |
|---|---|
ip | The address the server set aside. This is what gets echoed into requested_ip if the client accepts. |
lease_seconds | How long the client gets to keep the address once it actually accepts — separate from, and much longer than, OFFER_HOLD_SECONDS. |
server_id | This server's identity. Echoed back by the client so every server on the segment can tell who won. |
made_at | When the offer was made. Staleness is computed from this, not stored as a separate flag. |
Decision Flow
Decision Flow
now < made_at + OFFER_HOLD_SECONDS -> offer is live, hold still applies now >= made_at + OFFER_HOLD_SECONDS -> offer is stale, pool can reclaim the address
Reading Lens
Reading Lens
The important move in this session is to stop reading accept_offer() as "the client says yes" and start asking:
- who else, besides the server that made this specific offer, receives the REQUEST that
accept_offer()produces? - what does a losing server learn, and how does it learn it, given that
accept_offer()never mentions "losing servers" at all? - what is
OFFER_HOLD_SECONDSprotecting — the client, the server, or the address itself?
Toy Model Boundary
Toy Model Boundary
This lesson is mostly a single-server world: one Offer, one accept_offer() call, one echoed REQUEST. The echo only becomes interesting the moment a second server exists on the same segment — which is exactly why the docstring on accept_offer() talks about servers that were "not named." This file does not model multiple competing servers directly (there's no broadcast simulation, no set of listening servers reacting to the echo), but the REQUEST it builds carries everything a second server would need to notice it lost. Real DHCP servers also track offered-but-unconfirmed leases with their own internal timers and may recycle addresses more aggressively under pressure; OFFER_HOLD_SECONDS here is a single fixed constant, not a tunable policy.
Code Landmarks
Code Landmarks
OFFER_HOLD_SECONDS = 30
A module-level constant, not a field on Offer. Every offer from every server uses the same hold window — there's no per-offer override. Session 03's pool.py imports this same constant rather than defining its own, which is worth sitting with: the pool's notion of "how long is a hold good for" and the offer's notion of "how long is an offer good for" are the same number because they are, semantically, the same window.
offer_is_stale()
One line: now >= offer.made_at + OFFER_HOLD_SECONDS. The comparison is >=, not > — at the exact instant now equals the deadline, the offer is already stale. There is no grace second.
accept_offer()
Builds a REQUEST DhcpMessage, but notice what it does *not* take as a parameter: there's no dest or target_server argument, because — as in Session 01 — this REQUEST is still broadcast. What it does take is the offer itself, and it copies exactly two of that offer's fields forward: server_id into server_id, and ip into requested_ip. Read the docstring's claim literally: "the echo is the lesson." A client accepting server A's offer produces a message that server B can inspect and immediately know it wasn't named.
Failure Questions
Failure Questions
Use the source file to answer these:
- Where does
OFFER_HOLD_SECONDSlive, and which other file in this track imports it directly instead of redefining its own constant? Why would duplicating the value instead be a bug waiting to happen? - At exactly
now == offer.made_at + OFFER_HOLD_SECONDS, is the offer stale? Which comparison operator inoffer_is_stale()decides this, and what would change if it were>instead of>=? accept_offer()copies two fields fromofferinto the REQUEST it builds. Name both, and name the two fields onDhcpMessagethey land in.accept_offer()'s docstring says the REQUEST is "still broadcast." Given what Session 01 established aboutDhcpMessagehaving no destination field, what inaccept_offer()'s signature or return value would have to change for this message to be addressed to one specific server instead?lease_secondsis a field onOffer, but neitheroffer_is_stale()noraccept_offer()reads it. What doeslease_secondsgovern instead, and how is that different from whatOFFER_HOLD_SECONDSgoverns?
Walkthrough
Walkthrough
Run this:
PYTHONPATH=src python3 examples/dhcp/session_02_walkthrough.py
The walkthrough makes one offer and checks staleness on both sides of the OFFER_HOLD_SECONDS boundary, then accepts the offer and asserts that the resulting REQUEST carries both the offering server's server_id and its ip.
Done When
Done When
The learner can say all of the following without looking at notes:
- "An offer isn't a promise — it's a hold with a deadline, and the deadline is
made_at + OFFER_HOLD_SECONDS, not a separate expiry field." - "At
now == made_at + OFFER_HOLD_SECONDSthe offer is already stale, because the comparison is>=." - "
accept_offer()echoesserver_idandipinto the REQUEST so that every server on the segment — not just the winner — can read the outcome off a message that was never addressed to any of them specifically."
References
References
- RFC 2131 Section 3.1 — the OFFER/REQUEST exchange, including the client's selection among multiple offers and the implicit decline this leaves for non-selected servers
Continue