---
title: 'Variable-Length Subnetting'
source: 'https://academia.sh/en/courses/network-models/variable-length-subnetting'
course: 'Network Models and Protocols'
language: en
updated: '2026-08-17T18:07:11+00:00'
license: 'CC BY-SA 4.0'
---

# Variable-Length Subnetting

The method of splitting with different masks, the alignment rule, building the example organization's plan with an overlap test, and route summarization.

The previous lesson showed that equal splitting could not stretch the `192.168.10.0/24`
block to cover the organization's requirements: the method needed 640 addresses, and only
256 were on hand. The source of the flaw was the requirement that every subnet carry the
same mask.

CIDR removes this requirement. In the **variable length subnet mask (VLSM)** approach,
each subnet is sized to its own requirement. This lesson's question is what rule governs
this split and how the result is tested.

## The Alignment Rule

Variable splitting carries exactly one constraint: **a subnet's starting address must be
a multiple of its own size.**

The rule is not a formatting preference; it is the forced consequence of mask arithmetic.
A `/27` block's last five bits are host bits, and all of them must be zero in the network
address; this only holds when the address is a multiple of 32. The notation
`192.168.10.200/27` does not define a valid network, because 200 is not a multiple of
32 — that address is a host inside the `192.168.10.192/27` network.

| Prefix | Block size | Valid starting points (last byte) |
|---|---|---|
| /25 | 128 | 0, 128 |
| /26 | 64 | 0, 64, 128, 192 |
| /27 | 32 | 0, 32, 64, …, 224 |
| /28 | 16 | 0, 16, 32, …, 240 |
| /30 | 4 | 0, 4, 8, …, 252 |

The rule's consequence for planning is this: **larger blocks must be placed first.** If
smaller blocks are placed first, the next larger block is forced to jump ahead to its own
alignment point, leaving an unusable gap in between.

## Building the Plan

The method is three steps:

1. Sort the sections by host requirement in **descending** order.
2. For each section, pick the longest prefix satisfying $2^{32-p} - 2 \ge N$.
3. Place the blocks in order, respecting alignment.

The organization's requirements, already sorted: guest wireless 100, lab 50,
administration 20, server room 10, point-to-point link 2 hosts.

**Guest wireless, 100 hosts.** Since $2^7 - 2 = 126 \ge 100$, `/25` is sufficient; `/26`
gives only 62. Block `192.168.10.0/25`, range `.1` – `.126`, broadcast `.127`.

**Lab, 50 hosts.** `/26` gives 62 hosts. The next aligned address is 128. Block
`192.168.10.128/26`, range `.129` – `.190`, broadcast `.191`.

**Administration, 20 hosts.** `/27` gives 30 hosts. The next aligned address is 192.
Block `192.168.10.192/27`, range `.193` – `.222`, broadcast `.223`.

**Server room, 10 hosts.** `/28` gives 14 hosts. The next aligned address is 224. Block
`192.168.10.224/28`, range `.225` – `.238`, broadcast `.239`.

**Point-to-point link, 2 hosts.** `/30` gives exactly 2 hosts. The next aligned address
is 240. Block `192.168.10.240/30`, range `.241` – `.242`, broadcast `.243`.

## Verifying the Plan

A hand computation must be tested with a program. The program below encodes the method
and produces the plan.

```python
import ipaddress
import math

BLOCK = ipaddress.ip_network("192.168.10.0/24")
REQUIREMENTS = [
    ("Guest wireless", 100),
    ("Lab", 50),
    ("Administration", 20),
    ("Server room", 10),
    ("Link", 2),
]


def required_prefix(hosts: int) -> int:
    """Longest sufficient prefix for `hosts` usable addresses."""
    return 32 - math.ceil(math.log2(hosts + 2))


def vlsm_plan(block: ipaddress.IPv4Network,
              requirements: list[tuple[str, int]]) -> list[tuple]:
    sorted_reqs = sorted(requirements, key=lambda k: k[1], reverse=True)
    cursor = int(block.network_address)
    plan = []
    for name, hosts in sorted_reqs:
        prefix = required_prefix(hosts)
        size = 2 ** (32 - prefix)
        if cursor % size:                       # alignment: the block must land on its own size
            cursor += size - (cursor % size)
        subnet = ipaddress.ip_network((cursor, prefix))
        if not subnet.subnet_of(block):
            raise ValueError(f"no room left for {name}")
        plan.append((name, hosts, subnet))
        cursor += size
    return plan


plan = vlsm_plan(BLOCK, REQUIREMENTS)
print(f"{'Section':18s} {'Hosts':>7s}  {'Block':<19s} {'Mask':<16s} "
      f"{'Broadcast':<15s} {'Range':<31s} {'Spare':>4s}")
for name, hosts, subnet in plan:
    d = list(subnet.hosts())
    rng = f"{d[0]} - {d[-1]}"
    print(f"{name:18s} {hosts:7d}  {str(subnet):<19s} {str(subnet.netmask):<16s} "
          f"{str(subnet.broadcast_address):<15s} {rng:<31s} {len(d) - hosts:4d}")

used = sum(subnet.num_addresses for _, _, subnet in plan)
print(f"\naddresses used: {used} / {BLOCK.num_addresses}  "
      f"spare: {BLOCK.num_addresses - used}")

networks = [subnet for _, _, subnet in plan]
for i in range(len(networks)):
    for j in range(i + 1, len(networks)):
        assert not networks[i].overlaps(networks[j]), (networks[i], networks[j])
print("overlap test: passed")
```

```
Section              Hosts  Block               Mask             Broadcast       Range                           Spare
Guest wireless         100  192.168.10.0/25     255.255.255.128  192.168.10.127  192.168.10.1 - 192.168.10.126     26
Lab                     50  192.168.10.128/26   255.255.255.192  192.168.10.191  192.168.10.129 - 192.168.10.190   12
Administration          20  192.168.10.192/27   255.255.255.224  192.168.10.223  192.168.10.193 - 192.168.10.222   10
Server room             10  192.168.10.224/28   255.255.255.240  192.168.10.239  192.168.10.225 - 192.168.10.238    4
Link                     2  192.168.10.240/30   255.255.255.252  192.168.10.243  192.168.10.241 - 192.168.10.242    0

addresses used: 244 / 256  spare: 12
overlap test: passed
```

The hand computation is fully confirmed. Where equal splitting demanded 640 addresses,
variable splitting made do with 244, leaving 12 addresses spare.

The `hosts + 2` term inside `required_prefix` encodes the fact that every network spends
its own network and broadcast addresses. The call to `math.ceil(math.log2(...))` rounds
the required host count up: for 102, $\lceil \log_2 102 \rceil = 7$, giving prefix
$32 - 7 = 25$.

The overlap test is not a formality. The most common mistake in hand-built plans is one
block falling inside the previous one; the `overlaps` call catches this mistake instead
of letting it pass silently.

## The Finalized Plan

This is the plan the course uses from here on:

| Section | Block | Mask | Gateway | Usable range | Broadcast |
|---|---|---|---|---|---|
| Guest wireless | `192.168.10.0/25` | 255.255.255.128 | `.1` | `.1` – `.126` | `.127` |
| Lab | `192.168.10.128/26` | 255.255.255.192 | `.129` | `.129` – `.190` | `.191` |
| Administration | `192.168.10.192/27` | 255.255.255.224 | `.193` | `.193` – `.222` | `.223` |
| Server room | `192.168.10.224/28` | 255.255.255.240 | `.225` | `.225` – `.238` | `.239` |
| Link | `192.168.10.240/30` | 255.255.255.252 | — | `.241` – `.242` | `.243` |

In each section, the first usable address is reserved for the router interface. The
client introduced in the course's first lesson, `192.168.10.196`, is in the
administration network, and its gateway is `192.168.10.193` — the plan confirms both
values.

The 12 spare addresses lie between `192.168.10.244` and `192.168.10.255` and correspond
to three `/30` blocks: `.244/30`, `.248/30`, `.252/30`. These are available for
point-to-point links added later.

## The Effect of Ordering

What would happen if the sections were placed in ascending order? The smallest block
goes first, and each subsequent block jumps ahead to align:

| Order | Section | Block | Gap skipped |
|---|---|---|---|
| Ascending | Link | `192.168.10.0/30` | — |
| Ascending | Server room | `192.168.10.16/28` | `.4` – `.15` (12 addresses) |
| Ascending | Administration | `192.168.10.32/27` | — |
| Ascending | Lab | `192.168.10.64/26` | — |
| Ascending | Guest | `192.168.10.128/25` | — |

The plan still fits, but the spare 12 addresses sit in the **middle** of the block and
split into two separate pieces. Under descending order, the same 12 addresses sit at
the **end** of the block, contiguous.

The difference shows up in growth. A contiguous gap sitting at the end can be converted
into a larger subnet; a fragmented gap left in the middle can only be used at its own
size. This is the address-space counterpart of the fragmentation problem introduced in
the context of allocators in the Memory Layout — Stack and Heap lesson of the How
Computers Work course.

## Route Summarization

Variable splitting's second benefit is in the routing table. Contiguous, aligned subnets
can be **summarized** into a single prefix — a technique called **route summarization**.

The organization's five subnets are all inside the `192.168.10.0/24` block. The
provider's router does not need to know these five networks individually; a single line
is enough:

```
192.168.10.0/24  ->  203.0.113.10
```

Summarization is a direct consequence of the alignment rule. Four contiguous `/24`
blocks — `192.168.8.0`, `192.168.9.0`, `192.168.10.0`, `192.168.11.0` — can be referred
to with a single `192.168.8.0/22` prefix, because all four form an aligned group of four
starting at a multiple of 8. The same group of four could not be summarized if it started
at `192.168.9.0`.

The gain from summarization is direct: the number of rows in the routing table drops,
lookups get cheaper, and a change inside the internal network does not affect external
tables. This is the rationale for distributing addresses hierarchically.

## Documenting the Plan

An address plan is used not only at the moment it is computed but at every subsequent
change. The fields that must be documented are: section name, block, mask, gateway
address, the range reserved for static assignments, the range left for dynamic
distribution, and growth headroom.

Growth headroom is easy to overlook. In the plan above, the guest network has 26 spare
addresses, the lab has 12, and administration has 10; the server room network has only 4
spare addresses left. If the number of servers rises to 15, the `/28` is not enough and
the block must be resized. Whoever builds the plan should note upfront which section
overflows at which threshold.

## Summary

- In variable-length splitting, each subnet is sized to its own requirement; the only
  constraint is that the starting address be a multiple of the block size.
- The method is three steps: sort requirements in descending order, pick the longest
  sufficient prefix for each, and place them respecting alignment.
- The organization's five networks fit inside `192.168.10.0/24` using 244 addresses;
  equal splitting would have needed 640 addresses for the same job.
- Descending order leaves the spare addresses at the end of the block, contiguous;
  ascending order fragments the gap in the middle.
- Contiguous, aligned subnets can be summarized into a single prefix; the five subnets
  are advertised outward as `192.168.10.0/24`.
- Growth headroom must also be recorded when documenting the plan; in the example plan,
  the tightest headroom is 4 addresses, in the server room network.

## Next Step

This plan is one way of coping with the scarcity of the 32-bit address space: using
private addresses and splitting them carefully. There is another way that removes the
scarcity itself — expanding the address space. The next lesson covers 128-bit addresses,
their notation rules, how interfaces configure their own addresses, and the methods by
which the two versions interoperate.
