Skip to content
academia.sh

Lesson 17 / 17

Address and Naming Plan

On the same topology, a summarizable plan holds the table at 73 rows as the subnet count climbs from nine to thirty-six, while a plan distributed by creation order climbs from 90 rows to 335 on the same path; a single moved network leaves a summary announcing an address it no longer owns, and two packets fall into a black hole.

Contents

The previous three lessons measured topology as a graph, and table row count appeared as a column in every measurement: 272 in the three-layer design, 468 in the four-spine design. Every time, what determined the row count was the topology.

There is a second thing that determines row count, and it has nothing to do with topology: how destinations are named. On the same devices, with the same links, with the same number of subnets, two different address plans fill the same table with wildly different row counts. This lesson’s question is what the plan does to the table.

The Plan’s One Condition

A routing table can summarize neighboring addresses in a single row. This is called route summarization, and it has a single condition: every address in the summarized range must be reachable through the same next hop. The range must be aligned, and no address heading in another direction may fall inside it.

The netmask, alignment, and block-stride arithmetic were established in the Subnetting and Variable-Length Subnetting lessons and are not repeated here. What is measured is not the arithmetic itself, but its effect on table rows.

A plan either satisfies this condition or it does not. A plan that satisfies it divides the address space the same way the topology is divided: an aligned range for each block, an aligned sub-range for each leaf within that range, and growth room in both. A plan that does not satisfy it hands out addresses from a single pool in creation order.

# taught transcript, not run

aligned plan — 64-slot space, four blocks, three in use
  block 0   slots  0-15      y1: 0-3   y2: 4-7   y3: 8-11   12-15 growth room
  block 1   slots 16-31      block 2   slots 32-47      block 3   48-63 reserved
  c1's table:  0-15 -> d1     16-31 -> d3     32-47 -> d5

creation-order plan — single pool, creation order
  slot 0 -> y1   slot 1 -> y2   slot 2 -> y3   slot 3 -> y4   ...
  two consecutive slots fall into separate blocks; no summarizable range forms

The assumptions the measurement rests on:

  • TD27 — The topology is the first lesson’s three-layer design: nine leaves, three blocks, six distribution and two core nodes. The topology is the same in both plans; the only thing that changes is the plan.
  • TD28 — The address space is 64 slots, and a slot represents one subnet. In the aligned plan, each block gets 16 slots and each leaf gets 4; k slots are used per leaf, and the rest is growth room.
  • TD29 — Row count is counted as the fewest aligned prefixes that cover a node’s table. An empty slot may fall inside a prefix; a slot heading in another direction may not.
  • TD30 — The oracle is known because we built the topology ourselves, and it is, for every node, the first hop of the shortest path to every leaf. The packet is run with longest-prefix match.
  • TD31 — The count of name zones is the fewest ranges countable without changing block when names are laid out in the plan’s order. Name resolution itself is the subject of The Domain Name System lesson and is not repeated here.
  • TD32 — In a set of forty packets, the smallest measurable difference is 1/40 = 0.025.

The Measurement

"""Address and naming plan: the plan's effect on table row count.

Part 1 - aligned plan vs. creation-order plan, 1-4 subnets per leaf.
Part 2 - a summary announcing an address it no longer owns.
"""
SEED = 20260810
SLOT = 64
LEAF = [f"y{i}" for i in range(1, 10)]
BLOCK = {0: LEAF[0:3], 1: LEAF[3:6], 2: LEAF[6:9]}
DISTRIBUTION = {"d1": 0, "d2": 0, "d3": 1, "d4": 1, "d5": 2, "d6": 2}
NODES = LEAF + sorted(DISTRIBUTION) + ["c1", "c2"]
LINKS = ([(y, d) for d, b in DISTRIBUTION.items() for y in BLOCK[b]]
         + [(d, c) for d in sorted(DISTRIBUTION) for c in ("c1", "c2")]
         + [("c1", "c2")])


def make_rng(seed):
    state = seed % 2147483646 + 1

    def rand(n):
        nonlocal state
        state = (state * 48271) % 2147483647
        return state % n
    return rand


def neighbors():
    k = {u: set() for u in NODES}
    for x, y in LINKS:
        k[x].add(y)
        k[y].add(x)
    return k


def oracle():
    """The first hop of the shortest path to every destination, for every node."""
    k, table = neighbors(), {}
    for source in NODES:
        prev, front = {}, [source]
        while front:
            new = []
            for u in front:
                for v in sorted(k[u]):
                    if v != source and v not in prev:
                        prev[v] = u
                        new.append(v)
            front = new
        for h in prev:
            step = h
            while prev[step] != source:
                step = prev[step]
            table[(source, h)] = step
    return table


def aligned(k):
    """Each block gets 16 slots, each leaf 4; k of them are filled, the rest is growth room."""
    return {16 * b + 4 * j + i: y for b, leaves in BLOCK.items()
            for j, y in enumerate(leaves) for i in range(k)}


def creation_order(k):
    """Subnets are handed out from a single pool, in creation order."""
    return {n: LEAF[n % 9] for n in range(9 * k)}


def prefixes(u, plan, start=0, size=SLOT):
    """The fewest aligned prefixes that cover u's table."""
    inside = {("local" if y == u else ORACLE[(u, y)])
              for slot, y in plan.items() if start <= slot < start + size}
    if len(inside) < 2:
        return [(start, size, inside.pop())] if inside else []
    return (prefixes(u, plan, start, size // 2)
            + prefixes(u, plan, start + size // 2, size // 2))


def forward(source, slot, prefix, neigh, truth):
    """Runs with longest-prefix match; arrival is checked against actual ownership."""
    u, hops = source, 0
    while hops < 12:
        candidates = [p for p in prefix[u] if p[0] <= slot < p[0] + p[1]]
        if not candidates:
            return "black hole"
        v = min(candidates, key=lambda p: p[1])[2]
        if v == "local":
            return "reached" if truth.get(slot) == u else "black hole"
        if v not in neigh[u]:
            return "black hole"
        u, hops = v, hops + 1
    return "loop"


def pairs(count=40, seed=SEED):
    rand, out = make_rng(seed), []
    while len(out) < count:
        x, y = LEAF[rand(9)], LEAF[rand(9)]
        if x != y:
            out.append((x, y))
    return out


def zones(plan):
    """The fewest regions countable in the name sequence without changing block."""
    prev, count = None, 0
    for _, y in sorted(plan.items()):
        b = next(i for i, ys in BLOCK.items() if y in ys)
        if b != prev:
            count, prev = count + 1, b
    return count


def measure(plan, dest=None, truth=None):
    """Rows come from the plan, destinations from the address plan, arrival from ground truth."""
    dest, truth = dest or plan, truth or plan
    neigh = neighbors()
    prefix = {u: prefixes(u, plan) for u in NODES}
    first, counts = {}, {"reached": 0, "loop": 0, "black hole": 0}
    for slot, y in sorted(dest.items()):
        first.setdefault(y, slot)
    for x, y in pairs():
        counts[forward(x, first[y], prefix, neigh, truth)] += 1
    return sum(len(p) for p in prefix.values()), len(prefix["c1"]), counts


ORACLE = oracle()
print(f"{'plan':<16s} {'per leaf':>8s} {'subnets':>7s} "
      f"{'total rows':>10s} {'c1':>3s} {'name zones':>10s} {'reached':>7s}")
for name, build in (("aligned", aligned), ("creation order", creation_order)):
    for k in (1, 2, 3, 4):
        plan = build(k)
        rows, c1, s = measure(plan)
        print(f"{name:<16s} {k:8d} {len(plan):7d} {rows:10d} {c1:3d} "
              f"{zones(plan):10d} {s['reached']:7d}")

print()
home = aligned(4)
moved = {slot: ("y1" if y == "y9" else y) for slot, y in home.items()}
print(f"{'state':<32s} {'total rows':>10s} {'c1':>3s} {'reached':>7s} "
      f"{'black hole':>10s}")
for name, plan, truth in (("plan in place", home, None),
                           ("moved, summary not updated", home, moved),
                           ("moved, separate row added", moved, moved)):
    rows, c1, s = measure(plan, home, truth)
    print(f"{name:<32s} {rows:10d} {c1:3d} {s['reached']:7d} "
          f"{s['black hole']:10d}")
plan             per leaf subnets total rows  c1 name zones reached
aligned                 1       9         73   3          3      40
aligned                 2      18         73   3          3      40
aligned                 3      27         73   3          3      40
aligned                 4      36         73   3          3      40
creation order          1       9         90   6          3      40
creation order          2      18        171  12          6      40
creation order          3      27        249  17          9      40
creation order          4      36        335  24         12      40

state                            total rows  c1 reached black hole
plan in place                            73   3      40          0
moved, summary not updated               73   3      38          2
moved, separate row added                76   4      40          0

Row Count Can Be Independent of Subnet Count

The first four rows of the top table are the whole lesson. In the aligned plan, as subnet count climbs from 9 to 36, total table rows stay at 73. A network that quadruples in size adds not a single row to its table. The core’s table holds at 3 rows: one prefix per block.

The bottom four rows show the same topology with addresses distributed by creation order. Total rows climb 90, 171, 249, 335 — linear with subnet count. At thirty-six subnets, the gap is 73 against 335, a factor of 4.6. The core’s table holds 24 rows instead of 3.

The source of the gap is a single condition. In the aligned plan, every address in a block is reachable through the same next hop, so a single prefix covers all of them. In the creation-order plan, two consecutive addresses fall into separate blocks; no coverable range remains, and the table drops to one row per address.

The name zones column repeats the same accounting for names. In the aligned plan, when names are laid out in the plan’s order, 3 ranges result — one zone per block. In names laid out by creation order, the count grows 3, 6, 9, 12. A naming plan is not a separate design; it is the same question as the address plan, and it is measured with the same arithmetic.

One thing does not change: the reached column reads 40 in all eight rows. The plan does not lose packets, it only grows the table. The plan’s cost is not in latency, it is in the state carried.

The Address the Summary Does Not Own

The bottom table measures the summary’s single weak point. If the networks attached to y9 are moved to a different leaf and their addresses stay the same, the tables do not change at all — total rows are still 73, the core still 3. But 2 of the forty packets fall into a black hole.

The reason is this: the core’s 32-47 -> d5 row now announces an address it no longer owns. A summary says that every address it covers lies in that direction; the moved network does not lie in that direction. The summary is not wrong, it is over-broad — and a truth that over-covers is a falsehood.

The fix is in the last row. If a separate, narrower row is added for the moved network, all forty of the forty packets reach; the cost is rows climbing from 73 to 76 in total, and from 3 to 4 at the core. Three rows look small, but the rule is plain: every exception is a row, and exceptions accumulate. The plan is the only thing protecting the table from growth; the way to keep protecting it is to renumber the moved network.

Summary

  • Route summarization has a single condition: every address in the summarized range must be reachable through the same next hop; a plan either satisfies this condition or it does not.
  • In the aligned plan, as subnet count climbs from 9 to 36, total rows stay at 73 and the core’s table stays at 3.
  • In the plan distributed by creation order, the same topology holds 90, 171, 249, 335 rows; at thirty-six subnets the gap is a factor of 4.6.
  • A naming plan is not a separate design: the name zone count stays at 3 in the aligned plan, and climbs from 3 to 12 for names laid out by creation order.
  • A single moved network leaves the summary announcing an address it no longer owns, and 2 packets fall into a black hole; adding a separate row fixes it but raises the row count from 73 to 76.

Course Wrap-Up

The course opened with a single question: if the decision is read not from what is written in the packet but from the device’s own table, and that table is a copy of a shared truth, what happens to the packet for as long as the copies disagree? Seventeen lessons asked this question with seventeen separate disagreements, and every one counted the same three fates.

lesson disagreement measured reached loop or black hole
Repeater, Hub, and Switch the absence of any table 40 frames, 280 deliveries none; 240 unnecessary copies, 34 collisions
MAC Address Tables the entry’s age 110 deliveries, 278 once lifetime is shortened 3 black holes, 0 once lifetime is shortened
VLANs the segment’s boundary against the table’s scope flooding delivery 280, 51, 8 black holes 0, 23, 29, 32
Spanning Tree Protocol the tree was renewed, tables are stale 40/40 on the tree, 106 and 129 hops 19 and 20 black holes; 10 loops once two switches refresh
Link Aggregation leg mapping against the leg’s state 8 flows distributed without splitting 2 black holes in a five-frame window
Routing Decision the copy’s age and the reading rule converged 40/40, 83 and 99 hops 14 black holes at the moment of the break
Static and Dynamic Routing the static table never hears about the break 40 in two rounds under dynamic 14 black holes under static, fixed for six rounds; 6 loops once two nodes hear
Distance Vector Protocols the age of the neighbor’s decision 0, 9, 20, 32, 40 — four rounds sixty of sixty packets loop at an unreachable destination; split horizon brings it down to 5
Link State Protocols the database’s missing entry 9, 20, 32, 40 — three rounds no loop; a wrong table comes only from the missing entry
Border Gateway Protocol policy against the shortest path 40/40 under the transit ban, 92 hops 14 black holes once the advertisement closes; 7 packets outside the contract in the leak
Routing Instances the same destination’s two decisions across two instances 40/40 across three instances, 83, 99, 91 hops 14 black holes once reduced to a single table
MPLS and Label Switching the label path against the underlying link’s state 40 full-view and 43 label-view hops 14 black holes when the chord breaks and the path is not renewed
Overlay Networks the overlay’s table against the underlay’s table 40/40 with both layers converged, overlay 74 hops 21 tunnel black holes while the underlay is stale; 12 overlay loops once the tunnel drops and both ends hear
Layered Design cold start and the busiest link breaking 40/40 across three designs; 121, 80, 138 hops 21 permanent black holes in the flat chain
Spine–Leaf Design the loss of a leaf-spine link 40/40, 80 hops, every path 2 hops 14 permanent black holes with a single spine, 0 from two onward
Redundancy and Failover who holds the virtual address 40/40 with failover 40 black holes when nobody has heard; 40 loops when both believe the peer active
Address and Naming Plan what the summary covers against what it owns 40/40 with 73 rows 2 black holes in the moved network

The table repeats three things over and over. Partial information produces a more expensive failure than no information: a break nobody hears about gives a black hole, and the failure is unmistakable; a break a few nodes hear about gives a loop, and it looks like congestion. No single device can put a packet into a loop by itself: a loop is a disagreement between at least two tables, and a packet leaving a node whose own table is correct can still die. And convergence is not a speed, it is a round count: how many rounds it takes depends on how far the information has to travel.

All of these measurements shared one silent assumption. A link either exists or it does not; a neighbor either hears or it does not; a frame either gets forwarded or it does not. If a cable exists between two devices, what that cable carries reaches the other side — throughout the course, the opposite was never considered, except in the case of a break. The next course lifts this assumption: on a wireless link, whether a transmitted frame arrives is not a certainty but a probability, the medium is shared, and who is listening is unknown. Everything the wired link took as given will be asked again there.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close