Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Computer Networks  Module 7 – IPv4 addressing, subnetting and NAT

Subnetting step by step

Split an IPv4 network into equal subnets for a number of subnets or hosts, list them with the block-size method and find the subnet of any address.

  • Intermediate
  • 35 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free: 35 minutes

What you will learn

  • Split a network into equal subnets for a required number of subnets or hosts
  • Use the block-size method to list subnet ranges quickly
  • Decide which subnet an address belongs to and whether it is a usable host address

Before you start

Subnetting splits one network into smaller networks of equal size by moving the boundary between the network part and the host part to the right. Every host bit you borrow for the network part doubles the number of subnets and halves the size of each one: borrowing s bits from a network with h host bits gives 2^s subnets of 2^(h − s) addresses. The method has five steps. First, work out how many bits to borrow from what you need, a number of subnets or a number of hosts per subnet. Second, write the new prefix length and mask. Third, find the block size: 256 minus the mask value in the octet where the boundary falls. Fourth, list the subnets, which start at multiples of the block size. Fifth, check each subnet’s network address, host range and broadcast address. This lesson runs every step on worked examples and in Python.

Why networks are split

RFC 950, the standard that introduced subnets, describes organisations that divided one network into several subnets instead of asking for more network numbers. The reasons still hold. Each subnet is its own broadcast domain, so a broadcast from one floor of a building does not reach every device on the campus. Each subnet can carry its own rules: the guest Wi-Fi can be kept away from the accounts office by filtering between two subnets. And a router can reach a whole building through one route instead of one route per device.

Equal subnets are the simplest plan: every subnet gets the same prefix length. The next lesson of this module, on VLSM, gives each subnet a size of its own. Both rest on the same arithmetic, which is what this lesson practises.

Step 1: decide how many bits to borrow

Start from what you need:

  • A number of subnets N. Borrow the smallest s with 2^s ≥ N. Six subnets need s = 3, because 2³ = 8 is the first power of two that reaches 6, so a /24 becomes a /27.
  • A number of hosts per subnet H. Keep the smallest number of host bits h with 2^h − 2 ≥ H; the new prefix is 32 − h. Fifty hosts need h = 6 (2⁶ − 2 = 62), so the subnets are /26.
  • Both. The prefix must be at least the one the subnets need and at most the one the hosts need. When those two limits cross, no equal split works.
From what you need to a prefix length Python · borrow_bits.py
# Step 1 of subnetting: how many bits to borrow from 192.168.10.0/24, from a number of subnets, a number of
# hosts per subnet, or both.
import math


def bits_for_subnets(count):
    return (count - 1).bit_length()  # the smallest s with 2**s >= count


def bits_for_hosts(count):
    return (count + 1).bit_length()  # the smallest h with 2**h - 2 >= count


def describe(prefix):
    return f"/{prefix}, {2 ** (prefix - BASE)} subnets of {2 ** (32 - prefix) - 2} hosts"


def need_text(subnets, hosts):
    if subnets and hosts:
        return f"{subnets} subnets of {hosts} hosts"
    return f"{subnets} subnets" if subnets else f"{hosts} hosts each"


BASE = 24
for subnets, hosts in [(6, None), (None, 50), (6, 25), (6, 40)]:
    shortest = BASE + bits_for_subnets(subnets) if subnets else BASE  # enough subnets from here on
    longest = 32 - bits_for_hosts(hosts) if hosts else 30           # enough hosts up to here
    need = need_text(subnets, hosts)
    if shortest > longest:
        print(f"{need}: impossible")
        print(f"  (subnets need /{shortest} or longer, hosts /{longest} or shorter)")
    else:
        print(f"{need}: {describe(shortest if subnets else longest)}")

# Whole numbers avoid the rounding of a floating-point logarithm at an exact power of two.
print("ceil(log(2**29, 2)) =", math.ceil(math.log(2**29, 2)))
print("bits_for_subnets(2**29) =", bits_for_subnets(2**29))

Output

6 subnets: /27, 8 subnets of 30 hosts
50 hosts each: /26, 4 subnets of 62 hosts
6 subnets of 25 hosts: /27, 8 subnets of 30 hosts
6 subnets of 40 hosts: impossible
  (subnets need /27 or longer, hosts /26 or shorter)
ceil(log(2**29, 2)) = 30
bits_for_subnets(2**29) = 29

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 borrow_bits.py

The fourth case is worth a second look. Six subnets need at least 3 borrowed bits, which leaves 5 host bits and 30 hosts, while 40 hosts need 6 host bits, which leaves room to borrow only 2. The answer is to ask for a larger network, or to give the subnets different sizes with VLSM.

In code, (n - 1).bit_length() is the smallest s with 2^s ≥ n, and (n + 1).bit_length() is the smallest h with 2^h − 2 ≥ n. Both stay in whole numbers. A formula built on math.ceil(math.log(n, 2)) can be one too high at an exact power of two, as the last two lines show: math.log(2**29, 2) comes out a little above 29, so its ceiling is 30.

Steps 2 and 3: the new mask and the block size

The new prefix gives the new mask, and only one octet of the mask is interesting: the one where the boundary falls. Prefixes /1 to /8 put the boundary in the first octet, /9 to /16 in the second, /17 to /24 in the third and /25 to /32 in the fourth. Inside that octet the mask can take only these values:

Network bits in the octet Mask value Block size
1 128 128
2 192 64
3 224 32
4 240 16
5 248 8
6 252 4
7 254 2
8 255 1

The block size is 256 minus the mask value: how far apart two neighbouring subnets are in that octet. For a /27 the fourth octet of the mask is 224, so the subnets are 32 apart. For a /20 the third octet of the mask is 240, so the third octet counts up in steps of 16. When the prefix is a multiple of 8, as with /16 or /24, the mask value in the interesting octet is 255 and the block size is 1: each subnet is one value of that octet, such as 10.1.0.0/16, 10.2.0.0/16 and 10.3.0.0/16.

Subnet Mask Cheat Sheet Every prefix with its mask, wildcard mask and host count, to look up a mask value or a block size.

Step 4: list the subnets

Subnets start at every multiple of the block size in the interesting octet. Within one subnet:

  • the network address is the start of the block;
  • the first host is the start plus 1;
  • the last host is the start plus the block size minus 2;
  • the broadcast address is the start plus the block size minus 1, one less than the next subnet’s network address.
The fourth octet of 192.168.10.0/24 as eight /27 blocks starting at .0, .32, .64 and so on; .77 falls in the .64 block.192.168.10.0/24 cut into eight /27 blocks of 32.0.32.96.128.160.192.224.64.77192.168.10.77 lies in 192.168.10.64/27:network .64 · hosts .65 to .94 · broadcast .95

The block-size method on a number line

Text description of the diagram

The diagram draws the fourth octet of 192.168.10.0/24, the values 0 to 255, as one bar cut into eight equal blocks of 32 addresses, one per /27 subnet.

  • The blocks start at .0, .32, .64, .96, .128, .160, .192 and .224: every multiple of the block size, 32.
  • An arrow under the bar points at .77, inside the third block, which is shaded.
  • The text under the bar says that 192.168.10.77 lies in 192.168.10.64/27, whose network address is .64, whose hosts are .65 to .94 and whose broadcast address is .95.

To place any address, round its fourth octet down to a multiple of 32: 77 rounds down to 64. The broadcast address is one less than the start of the next block, 96 − 1 = 95.

For 192.168.10.0/24 cut into /27s the block size is 32, so the eight subnets start at .0, .32, .64, .96, .128, .160, .192 and .224. The program lists them from the block size and compares the list with ipaddress.subnets():

Eight /27 subnets from the block size Python · block_size.py
# Steps 2 to 4: the /27 subnets of 192.168.10.0/24 from the block size, compared with ipaddress.subnets().
import ipaddress

base = ipaddress.ip_network("192.168.10.0/24")
new_prefix = 27
mask = ipaddress.ip_network(f"0.0.0.0/{new_prefix}").netmask
block = 256 - int(str(mask).split(".")[3])  # the boundary falls in the fourth octet
print(f"mask {mask}, block size {block}")

by_hand = []
for start in range(0, 256, block):
    hosts = f".{start + 1}-.{start + block - 2}"
    print(f"192.168.10.{start:<3}  hosts {hosts:<10} broadcast .{start + block - 1}")
    by_hand.append(f"192.168.10.{start}/{new_prefix}")

print("same as ipaddress:", by_hand == [str(n) for n in base.subnets(new_prefix=new_prefix)])

Output

mask 255.255.255.224, block size 32
192.168.10.0    hosts .1-.30     broadcast .31
192.168.10.32   hosts .33-.62    broadcast .63
192.168.10.64   hosts .65-.94    broadcast .95
192.168.10.96   hosts .97-.126   broadcast .127
192.168.10.128  hosts .129-.158  broadcast .159
192.168.10.160  hosts .161-.190  broadcast .191
192.168.10.192  hosts .193-.222  broadcast .223
192.168.10.224  hosts .225-.254  broadcast .255
same as ipaddress: True

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 block_size.py

Step 5: find the subnet of an address

Going the other way, from an address to its subnet, takes three moves. Take the interesting octet of the address and round it down to a multiple of the block size: that is the network address in that octet. Add the block size minus 1 to get the broadcast address in that octet. Octets before the interesting one stay as they are; octets after it become 0 in the network address and 255 in the broadcast address.

Take 172.16.45.77/20. The boundary is in the third octet, where the mask is 240 and the block size 16. 45 rounds down to 32, so the network address is 172.16.32.0, and 32 + 15 = 47 gives the broadcast address 172.16.47.255. The address lies strictly between them, so it is a usable host.

Placing six addresses in their subnets Python · which_subnet.py
# Step 5: which subnet holds an address, by the block-size method, and whether the address is a usable host.
import ipaddress


def locate(address, prefix):
    octets = [int(x) for x in address.split(".")]
    i = (prefix - 1) // 8                    # the octet where the boundary falls (0 to 3)
    bits_in_octet = prefix - 8 * i            # network bits inside that octet (1 to 8)
    block = 2 ** (8 - bits_in_octet)
    start = octets[i] // block * block
    network = octets[:i] + [start] + [0] * (3 - i)
    broadcast = octets[:i] + [start + block - 1] + [255] * (3 - i)
    return ".".join(map(str, network)), ".".join(map(str, broadcast)), i + 1, block


for address, prefix in [("172.16.45.77", 20), ("192.168.10.95", 27), ("192.168.10.96", 27),
                        ("10.200.15.255", 21), ("10.200.12.255", 21), ("10.200.12.0", 21)]:
    network, broadcast, octet, block = locate(address, prefix)
    role = {network: "network address", broadcast: "broadcast address"}.get(address, "usable host")
    print(f"{address}/{prefix}: octet {octet}, block {block}")
    print(f"  {network} - {broadcast}, {role}")
    net = ipaddress.ip_network(f"{address}/{prefix}", strict=False)
    assert (str(net.network_address), str(net.broadcast_address)) == (network, broadcast)

Output

172.16.45.77/20: octet 3, block 16
  172.16.32.0 - 172.16.47.255, usable host
192.168.10.95/27: octet 4, block 32
  192.168.10.64 - 192.168.10.95, broadcast address
192.168.10.96/27: octet 4, block 32
  192.168.10.96 - 192.168.10.127, network address
10.200.15.255/21: octet 3, block 8
  10.200.8.0 - 10.200.15.255, broadcast address
10.200.12.255/21: octet 3, block 8
  10.200.8.0 - 10.200.15.255, usable host
10.200.12.0/21: octet 3, block 8
  10.200.8.0 - 10.200.15.255, usable host

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 which_subnet.py

An address that ends in .255 is not always a broadcast address, and one that ends in .0 is not always a network address. In 10.200.8.0/21 the broadcast address is 10.200.15.255, so 10.200.12.255 and 10.200.12.0 are ordinary hosts. Only the interesting octet and the octets after it decide.

Subnets that cross an octet boundary

The borrowed bits do not have to fit inside one octet. Suppose 172.16.0.0/16 must be cut into at least 500 subnets. That needs 9 borrowed bits (2⁹ = 512), so the new prefix is /25. The boundary sits in the fourth octet, where the block size is 128, but the borrowed bits run through the whole third octet as well. Two /25 subnets fit in each value of the third octet: 172.16.0.0 and 172.16.0.128, then 172.16.1.0 and 172.16.1.128, and so on.

Subnet number k (counting from 0) therefore starts k × 128 addresses after the base. Dividing k × 128 by 256 gives the third octet, and the remainder gives the fourth: subnet 299, the 300th subnet, is 172.16.149.128/25.

The 300th subnet of 172.16.0.0/16 Python · nth_subnet.py
# Subnetting across an octet boundary: 172.16.0.0/16 cut into at least 500 equal subnets.
import ipaddress

base = ipaddress.ip_network("172.16.0.0/16")
borrowed = (500 - 1).bit_length()  # 2**9 = 512 >= 500
prefix = base.prefixlen + borrowed
size = 2 ** (32 - prefix)
print(f"/{prefix}: {2 ** borrowed} subnets of {size} addresses ({size - 2} hosts)")

# Each subnet is the base address plus k times the subnet size: with 128 addresses per subnet,
# two subnets fit in each value of the third octet.
for k in [0, 1, 2, 3, 299, 511]:
    third, fourth = divmod(k * size, 256)
    print(f"subnet {k:<3}  172.16.{third}.{fourth}/{prefix}")

subnets = list(base.subnets(new_prefix=prefix))
print("ipaddress agrees:", str(subnets[299]) == "172.16.149.128/25", len(subnets))

Output

/25: 512 subnets of 128 addresses (126 hosts)
subnet 0    172.16.0.0/25
subnet 1    172.16.0.128/25
subnet 2    172.16.1.0/25
subnet 3    172.16.1.128/25
subnet 299  172.16.149.128/25
subnet 511  172.16.255.128/25
ipaddress agrees: True 512

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 nth_subnet.py

The zero subnet and the all-ones subnet

RFC 950 asked networks not to assign the subnets whose subnet bits are all zeros or all ones, because those values also meant “this network” and “all hosts”. Ten years later RFC 1878 called excluding them obsolete and noted that modern software can use every subnet that can be defined. Today a /24 split into /27s gives eight usable subnets, the first and the last included.

Some exam questions and older textbooks still follow the old rule. When a question says the zero subnet and the all-ones subnet must not be used, count 2^s − 2 subnets instead of 2^s: a /24 split with 3 borrowed bits then gives 6 usable subnets rather than 8. Without such an instruction, use all of them.

GATE CS

GATE papers mix multiple choice questions (MCQ), multiple select questions (MSQ) and numerical answer type (NAT) questions, and the official question paper pattern says a wrong MSQ or NAT answer costs no marks; only MCQs carry negative marking. A count of hosts or subnets is a number you type in, so a numerical subnetting question rewards checking the arithmetic rather than guessing.

Checking an answer in seconds

A few checks catch almost every slip:

  • The network address’s interesting octet is a multiple of the block size, and every later octet is 0.
  • The broadcast address is one less than the next subnet’s network address.
  • A subnet has 2^h − 2 usable hosts (a /31 point-to-point link is the exception, with 2).
  • The number of subnets times the addresses in each equals the addresses of the network you started with: 8 × 32 = 256 for the /27s of a /24.
  • Writing only the interesting octet in binary confirms an edge: the host bits of a network address are all 0 and those of a broadcast address all 1.
Subnet Calculator Its Split into subnets option lists every smaller subnet of a network, to check a plan worked out by hand.

Interview questions

What is the block size of a /27, and what are the first three /27 subnets of 192.168.10.0/24?

A /27 mask is 255.255.255.224, so the boundary falls in the fourth octet and the block size is 256 − 224 = 32: each /27 holds 32 addresses, 30 of them usable. Subnets start at multiples of 32 in the fourth octet, so the first three are 192.168.10.0/27 (hosts .1 to .30, broadcast .31), 192.168.10.32/27 (hosts .33 to .62, broadcast .63) and 192.168.10.64/27 (hosts .65 to .94, broadcast .95). A /24 holds eight of them; the last is 192.168.10.224/27.

Key takeaways

  • Borrowing s host bits makes 2^s subnets; keeping h host bits leaves 2^h − 2 usable hosts in each.
  • Six subnets need 3 borrowed bits and 50 hosts need 6 host bits; when the two needs cross, no equal split works.
  • The block size is 256 minus the mask value in the octet where the boundary falls, and subnets start at its multiples.
  • An address’s subnet: round its interesting octet down to a multiple of the block size; the broadcast address is one less than the next subnet.
  • Borrowed bits can span octets: subnet k of a split starts k × (subnet size) addresses after the base.
  • Modern networks use the zero subnet and the all-ones subnet; drop them only when a question says so.

Exercise

Exercise · Medium · Python

Split a network into equal subnets

Write plan_equal_subnets(base, need_subnets=None, need_hosts=None), which splits the IPv4 network base (a string such as "192.168.10.0/24") into equal subnets and returns all of them, in address order, as a list of strings such as "192.168.10.32/27".

- With only need_subnets, use the shortest prefix that gives at least that many subnets, so that each subnet is as large as possible. need_subnets=1 returns the base network itself. - With only need_hosts, use the longest prefix whose subnets each hold at least that many usable hosts (2^h − 2), so that there are as many subnets as possible. - With both, use the shortest prefix that gives enough subnets, as long as its subnets still hold need_hosts hosts. - Raise ValueError when the request cannot be met inside base (too many subnets, too many hosts, or both together), and when neither number is given.

Every subnet must keep at least two usable hosts, so the longest prefix you may return is /30. Count every subnet, including the zero subnet and the all-ones subnet. The sample tests import your function from plan.py.

Starter code · plan.py

def plan_equal_subnets(base, need_subnets=None, need_hosts=None):
    """Split base into equal subnets for need_subnets subnets and/or need_hosts hosts per subnet."""
    # Replace this line with your code.
    return []
The sample tests · test_plan.py
from plan import plan_equal_subnets


def refused(base, **numbers):
    """True when plan_equal_subnets raises ValueError for this request."""
    try:
        plan_equal_subnets(base, **numbers)
    except ValueError:
        return True
    return False


def test_by_number_of_subnets():
    """uses the shortest prefix that gives enough subnets"""
    assert plan_equal_subnets("192.168.10.0/24", need_subnets=6) == [
        "192.168.10.0/27", "192.168.10.32/27", "192.168.10.64/27", "192.168.10.96/27",
        "192.168.10.128/27", "192.168.10.160/27", "192.168.10.192/27", "192.168.10.224/27",
    ]


def test_by_hosts_per_subnet():
    """uses the longest prefix that still holds the hosts"""
    assert plan_equal_subnets("192.168.10.0/24", need_hosts=50) == [
        "192.168.10.0/26", "192.168.10.64/26", "192.168.10.128/26", "192.168.10.192/26",
    ]


def test_both_numbers():
    """meets both numbers with the largest subnets that are possible"""
    assert len(plan_equal_subnets("192.168.10.0/24", need_subnets=6, need_hosts=25)) == 8
    assert plan_equal_subnets("10.20.0.0/22", need_subnets=3, need_hosts=100) == [
        "10.20.0.0/24", "10.20.1.0/24", "10.20.2.0/24", "10.20.3.0/24",
    ]


def test_across_an_octet_boundary():
    """lets the subnets cross from one octet into the next"""
    assert plan_equal_subnets("10.20.0.0/22", need_subnets=8) == [
        "10.20.0.0/25", "10.20.0.128/25", "10.20.1.0/25", "10.20.1.128/25",
        "10.20.2.0/25", "10.20.2.128/25", "10.20.3.0/25", "10.20.3.128/25",
    ]


def test_one_subnet_and_exact_powers():
    """returns the base for one subnet and borrows no extra bit for a power of two"""
    assert plan_equal_subnets("172.16.0.0/16", need_subnets=1) == ["172.16.0.0/16"]
    assert len(plan_equal_subnets("172.16.0.0/16", need_subnets=4)) == 4
    assert plan_equal_subnets("192.168.10.0/24", need_hosts=62) == [
        "192.168.10.0/26", "192.168.10.64/26", "192.168.10.128/26", "192.168.10.192/26",
    ]
    assert plan_equal_subnets("192.168.10.0/24", need_hosts=63) == ["192.168.10.0/25", "192.168.10.128/25"]


def test_impossible_requests():
    """raises ValueError when the network cannot meet the request"""
    assert refused("192.168.10.0/24", need_subnets=6, need_hosts=40)
    assert refused("192.168.10.0/24", need_hosts=300)
    assert refused("192.168.10.0/24", need_subnets=128)
    assert refused("192.168.10.0/24")
A hint

Work with prefix lengths first and build the list last. (need_subnets - 1).bit_length() is the number of bits to borrow for the subnets, and (need_hosts + 1).bit_length() the number of host bits that hold the hosts. That gives the shortest and the longest prefix you may use; when the shortest is longer than the longest, the request cannot be met. ipaddress.ip_network(base).subnets(new_prefix=...) lists the subnets once you know the prefix.

The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 Put the steps of subnetting a network into equal subnets in order.

    Give each item its position, from 1 (first).

    Show the answer to question 1

    Answer:

    1. Work out how many bits to borrow from the subnets or hosts you need
    2. Write the new prefix length and mask
    3. Find the block size in the octet where the boundary falls
    4. List the subnets at multiples of the block size
    5. Check each subnet's network address, hosts and broadcast address

    The number of borrowed bits fixes the prefix, the prefix fixes the mask, the mask gives the block size, and the block size gives the list. Checking last catches slips such as a broadcast address that is not one less than the next network address.

  2. Question 2 of 6 The mask of a /21 is 255.255.248.0. What is the block size in the third octet?

    Type a number.

    Show the answer to question 2

    Answer: 8

    The block size is 256 minus the mask value in the octet where the boundary falls: 256 − 248 = 8. The /21 subnets therefore start at third-octet values 0, 8, 16, 24 and so on.

  3. Question 3 of 6 How many host bits does a subnet need to hold 120 hosts?

    Type a number.

    Show the answer to question 3

    Answer: 7 bits

    With h host bits a subnet holds 2^h − 2 hosts. Six bits give 62, which is too few; seven bits give 126, so the subnet is a /25.

  4. Question 4 of 6 In 10.200.8.0/21, what is the address 10.200.12.255?

    Choose one answer.

    Show the answer to question 4

    Answer: An ordinary usable host address

    A /21 has a block size of 8 in the third octet, so 10.200.8.0/21 runs from 10.200.8.0 to 10.200.15.255. Only 10.200.15.255 is its broadcast address; 10.200.12.255 is an ordinary host in the middle of the block.

  5. Question 5 of 6 Which of these are usable host addresses in 172.16.32.0/20?

    Choose every answer that is right.

    Show the answer to question 5

    Answer:

    • 172.16.47.254
    • 172.16.40.0

    172.16.32.0/20 runs from 172.16.32.0 (its network address) to 172.16.47.255 (its broadcast address). Everything in between is usable, including 172.16.40.0. 172.16.48.1 belongs to the next /20.

  6. Question 6 of 6 A question says that the zero subnet and the all-ones subnet must not be used. How many usable /27 subnets does a /24 give?

    Choose one answer.

    Show the answer to question 6

    Answer: 6

    Borrowing 3 bits gives 2³ = 8 subnets. The old rule of RFC 950 drops the first and the last, which leaves 2³ − 2 = 6. Without that instruction, modern practice uses all 8.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.