Core Question
Core Question
RIP needs a metric value that fits in the same integer field as every real distance but means "not reachable at all." Why is that value 16 — not 255, not a billion — and what does picking a small number actually buy?
Outcome
Outcome
By the end of this session, the learner should be able to:
- state
INFINITY = 16and explain why the value is deliberately small rather than deliberately large - give the exact boundary condition
is_unreachable()uses, and evaluate it at 15 and at 16 - explain what
clamp_metric()does to a sum that overshoots 16, with a concrete example - state what
poison()changes about a route and what it leaves untouched
Read Order
Read Order
- Read the module docstring
- Read
INFINITY - Read
is_unreachable() - Read
clamp_metric() - Read
poison() - Run
examples/rip/session_03_walkthrough.py
Read It Like Code
Read It Like Code
is_unreachable(metric) # metric >= INFINITY clamp_metric(metric) # min(metric, INFINITY) poison(route) # replace(route, metric=INFINITY)
Fields That Matter
Fields That Matter
There is no dataclass introduced in this module — infinity.py is three small functions and one constant, operating on plain integers and on the RipRoute defined in session 01.
| Name | Why it matters |
|---|---|
INFINITY | 16. The one constant every function in this module measures against. |
metric (in is_unreachable, clamp_metric) | A raw integer distance, already summed by the caller — session 02's candidate_metric computation is the main caller. |
route (in poison) | A full RipRoute from session 01. poison() returns a new one rather than mutating — RipRoute is frozen, so there is no other option. |
Decision Flow
Decision Flow
is_unreachable(metric):
metric >= INFINITY -> True
otherwise -> False
clamp_metric(metric):
metric > INFINITY -> INFINITY
otherwise -> metric (min() collapses both cases into one expression)
poison(route):
-> RipRoute(route.prefix, INFINITY, route.next_hop, route.learned_from)
(every field but metric is copied unchanged)Reading Lens
Reading Lens
This module is pure and tiny in exactly the way tcp/seqnum.py is pure and tiny: no dataclasses to inspect, no branching state machine, just arithmetic that has to be exactly right at one boundary. The important move is to stop treating "16" as an arbitrary magic number and start asking:
- what specific comparison operator does
is_unreachable()use at the boundary — is16itself unreachable, or is17the first unreachable value? - when
clamp_metric()fires, what real-world event does an overshoot like15 + 1 = 16oradvertised 100represent? poison()builds a newRipRoutewith one field changed. Which fields does it have to carry forward unchanged for the result to still mean "this is a route to the same destination, just now marked dead"?
Toy Model Boundary
Toy Model Boundary
The module docstring itself sets up the next session: a small infinity bounds how many rounds a routing loop can circulate before the bad rumor tops out, at the cost of also bounding how large a real network RIP can cover (15 hops maximum, since 16 means unreachable). This module only implements the boundary check and the poisoning operation — it does not implement the loop, the timers, or the periodic-update behavior that actually drives a metric upward round by round. That is count_to_infinity.py's job, in the session this one is deliberately positioned before.
This module's specific numbers — INFINITY = 16, and the discussion of hop-count semantics — are RIPv2's. RIPng (RFC 2080) reuses the same metric field and the same value of 16 for IPv6, but carries it differently on the wire; this module does not model that difference.
Code Landmarks
Code Landmarks
Module docstring
Frames 16 not as a limit on network size so much as a deliberately short fuse on how long a bad rumor can circulate — a design tradeoff, not an accident. Read this before the functions below; it is the argument the Failure Questions test.
is_unreachable()
One comparison, metric >= INFINITY. Note the >=, not > — a metric of exactly 16 already counts as unreachable, it does not have to exceed 16 first.
clamp_metric()
min(metric, INFINITY) — a single expression that leaves any metric of 16 or below untouched and caps anything above it at exactly 16. This is the function session 02's process_advertisement() calls on every candidate before it ever compares metrics, so no route in the table can ever silently carry a metric above 16.
poison()
dataclasses.replace(route, metric=INFINITY) — the only field touched is metric; prefix, next_hop, and learned_from all pass through unchanged. Poisoning a route does not erase who it came from, it just declares the distance to it infinite.
Failure Questions
Failure Questions
Use the source file to answer these:
is_unreachable(16)andis_unreachable(15)— what does each return, and which operator (>=versus>) makes 16 itself count as unreachable rather than the last reachable hop count?clamp_metric(15 + 1)andclamp_metric(100)both return the same value. What does that tell you about how much informationclamp_metric()preserves about *how far* a metric overshot infinity?poison()usesdataclasses.replace()rather than constructing a newRipRoutefield by field. What has to be true aboutRipRoute(see session 01) forreplace()to be usable at all here?- After
poison(route), ispoisoned.learned_from == route.learned_from? Read thereplace()call to justify your answer — does poisoning a route change who it was learned from? - The module docstring says a small infinity is "a deliberately short fuse," not a network-size limit as an end in itself. If
INFINITYwere256instead of16, what specifically aboutis_unreachable()andclamp_metric()would still work correctly, and what would only change in degree (how long a bad rumor could circulate) rather than in kind?
Walkthrough
Walkthrough
Run this:
PYTHONPATH=src python3 examples/rip/session_03_walkthrough.py
The walkthrough clamps 15 + 1 and a wildly overshot 100 to confirm both land on exactly 16, checks the is_unreachable() boundary at 16 and at 15, and poisons a route to confirm the metric becomes 16 while prefix, next_hop, and learned_from all survive unchanged.
Done When
Done When
The learner can say all of the following without looking at notes:
- "16 is
INFINITY— small on purpose, so a routing loop's metric tops out in a handful of rounds instead of climbing forever." - "
is_unreachable()uses>=, so a metric of exactly 16 is already unreachable, not the last valid hop count." - "
poison()only ever changesmetric; every other field of the route — including who it was learned from — is carried forward untouched."
References
References
- RFC 2453 Section 3.8 (Distance Metric: RIP's use of hop count and the choice of 16 as infinity)
- RFC 2453 Section 3.10 (Timers, referenced here only to note what this toy model deliberately omits — the periodic and triggered-update timing that actually drives count-to-infinity)
Continue