IP Addressing and Subnetting with Worked Examples

IPv4 addresses, classes A to E, private ranges, CIDR and subnet masks, fully worked subnetting and VLSM examples, supernetting, and IPv4 vs IPv6 compared.

What is subnetting in computer networks?

Subnetting divides one IP network into smaller networks, called subnets, by borrowing host bits for the network part of the address. A subnet mask or CIDR prefix such as /26 says how many leading bits identify the network; the remaining h bits identify hosts. Each subnet has 2^h addresses and 2^h − 2 usable hosts, because the first is the network address and the last is the broadcast address.

Every device on an IP network needs an address so packets can find it, and every router needs a way to tell which addresses live behind which link. An IPv4 address answers both: its leading bits name a network and its trailing bits name a host on that network. Subnetting is the skill of moving that boundary to cut one block of addresses into smaller networks, and it is the arithmetic most often asked on paper in networking interviews. This note works every number out; check them yourself as you read.

IPv4 address structure

An IPv4 address is 32 bits, written as four 8-bit octets in decimal separated by dots: 192.168.10.150. Each octet is 0 to 255. There are 2^32 = 4,294,967,296 possible addresses.

Converting an octet to binary uses the place values 128, 64, 32, 16, 8, 4, 2, 1:

DecimalSum of place valuesBinary
192128 + 6411000000
150128 + 16 + 4 + 210010110
255all eight11111111

An address has two parts: the network portion (identical for every host on that network) and the host portion (unique per host). Where the split falls is given by the subnet mask.

Classful addressing: classes A to E

Before 1993, the first bits of an address fixed its class and therefore its network/host split.

classleading bitsfirst octet, 0 to 255A00–127 · /8B10128–191 · /16C110192–223 · /24D1110224–239E1111240–2550128192224
Address classes by their leading bits. A class is fixed by the first bits of the first octet: a leading 0 is class A, half of all addresses (0 to 127); 10 is B and 110 is C, each half of what is left. D (1110) is multicast and E (1111) reserved, with no default mask.
ClassLeading bitsFirst octetDefault maskNetworksUsable hosts eachUse
A00 to 127255.0.0.0 (/8)128 (126 usable)2^24 − 2 = 16,777,214Very large networks
B10128 to 191255.255.0.0 (/16)2^14 = 16,3842^16 − 2 = 65,534Medium networks
C110192 to 223255.255.255.0 (/24)2^21 = 2,097,1522^8 − 2 = 254Small networks
D1110224 to 239nonenonenoneMulticast groups
E1111240 to 255nonenonenoneReserved, experimental

In class A, network 0 and network 127 are reserved, which is why only 126 class A networks are usable. Classes wasted addresses badly: an organisation needing 300 hosts was too big for a class C and got a class B with 65,534 host addresses. That waste is why CIDR replaced classes.

Special and private addresses

RangePurpose
10.0.0.0/8Private (10.0.0.0 to 10.255.255.255)
172.16.0.0/12Private (172.16.0.0 to 172.31.255.255)
192.168.0.0/16Private (192.168.0.0 to 192.168.255.255)
127.0.0.0/8Loopback: 127.0.0.1 means "this machine" and never leaves it
169.254.0.0/16Link-local (APIPA): self-assigned when DHCP fails
0.0.0.0"This host" before it has an address; 0.0.0.0/0 is the default route
255.255.255.255Limited broadcast: every host on the local network
224.0.0.0/4Multicast (class D)
100.64.0.0/10Shared space for carrier-grade NAT inside ISPs

Private addresses (RFC 1918) can be reused by any organisation because they are never routed on the public internet; a router doing NAT translates them to a public address on the way out.

Subnet masks and CIDR

A subnet mask is 32 bits with 1s for the network part and 0s for the host part. CIDR (Classless Inter-Domain Routing, 1993) writes the number of 1s as a prefix length: 255.255.255.192 is /26 because 11111111.11111111.11111111.11000000 has 26 ones. With h = 32 − prefix host bits:

  • addresses per subnet = 2^h, usable hosts = 2^h − 2;
  • borrowing s bits from the host part creates 2^s subnets;
  • block size = 256 − (mask value in the octet where the boundary falls). Subnets start at multiples of it.
PrefixMaskHost bitsAddressesUsable hostsBlock size
/8255.0.0.02416,777,21616,777,2141 in octet 1
/16255.255.0.01665,53665,5341 in octet 2
/20255.255.240.0124,0964,09416 in octet 3
/24255.255.255.082562541 in octet 3
/25255.255.255.1287128126128 in octet 4
/26255.255.255.1926646264 in octet 4
/27255.255.255.2245323032 in octet 4
/28255.255.255.2404161416 in octet 4
/29255.255.255.2483868 in octet 4
/30255.255.255.2522424 in octet 4

Worked example: four subnets from 192.168.10.0/24

Task. Split 192.168.10.0/24 into four equal subnets and give each one's network address, host range and broadcast address.

  1. Subnets needed: 4. Borrow s bits so that 2^s ≥ 4, so s = 2.
  2. New prefix: 24 + 2 = /26. Mask: the last octet is 11000000 = 192, so 255.255.255.192.
  3. Host bits: h = 32 − 26 = 6. Addresses per subnet 2^6 = 64; usable hosts 64 − 2 = 62.
  4. Block size: 256 − 192 = 64, so the subnets start at .0, .64, .128 and .192.
SubnetNetwork addressFirst hostLast hostBroadcastUsable hosts
1192.168.10.0/26192.168.10.1192.168.10.62192.168.10.6362
2192.168.10.64/26192.168.10.65192.168.10.126192.168.10.12762
3192.168.10.128/26192.168.10.129192.168.10.190192.168.10.19162
4192.168.10.192/26192.168.10.193192.168.10.254192.168.10.25562

Check: each broadcast is one less than the next network address, and 4 × 64 = 256 covers the whole /24.

network11000000.192broadcast11111111.255hosts .193 to .254: 2^6 − 2 = 62.0/26.64/26.128/26.192/26064128192255
Splitting a /24 into four /26 subnets. Example: 192.168.10.0/24 into 4 equal subnets
  1. Four subnets need 2 borrowed bits, since 2^2 = 4, so the prefix grows from /24 to /26. That leaves 6 host bits: each subnet is a block of 64 addresses.
  2. Subnet bits 00: with the host bits all 0 the last octet is 0, the network address; all 1 gives 63, the broadcast. Everything between is a host.
  3. Subnet bits 01: the network is .64 and the broadcast .127, so this block starts exactly where the last one ended.
  4. Subnet bits 10: the network is .128 and the broadcast .191, so this block starts exactly where the last one ended.
  5. Subnet bits 11: the network is .192 and the broadcast .255, so this block starts exactly where the last one ended. The four blocks fill the /24: 4 × 64 = 256.

Which subnet does 192.168.10.150/26 belong to?

AND the address with the mask to get the network address; set every host bit to 1 to get the broadcast.

address1100000010101000000010101001011019216810150mask /2611111111111111111111111111000000255255255192network1100000010101000000010101000000019216810128broadcast1100000010101000000010101011111119216810191/26network bitshost bits
Finding the network and broadcast address with bits. Example: 192.168.10.150/26
  1. Every IPv4 address is 32 bits, four octets of eight. The last octet here is 150 = 10010110 in binary; the first three are 192, 168 and 10.
  2. /26 means the mask is 26 ones and then 6 zeros, 255.255.255.192. Bits left of the line name the network; the 6 to its right name a host on it.
  3. AND each bit with the mask: the network bits pass through and the host bits become 0. The last octet 10010110 becomes 10000000, so the network address is 192.168.10.128.
  4. Set every host bit to 1 instead and you get the broadcast address, 192.168.10.191. The 6 host bits give 2^6 = 64 addresses; less these two, 62 hosts, .129 to .190.

On paper, only the last octet changes:

Dotted decimalLast octet in binary
Address192.168.10.15010010110
Mask255.255.255.19211000000
Address AND mask = network192.168.10.12810000000
Host bits all 1 = broadcast192.168.10.19110111111

So the host is in subnet 3: hosts .129 to .190, broadcast .191. The shortcut gives the same answer: 150 lies between the block starts 128 and 192, so the network is .128.

A prefix off the octet boundary: 172.16.45.200/20

  1. /20 means the boundary falls inside the third octet: 20 = 8 + 8 + 4, so the third octet of the mask is 11110000 = 240. Mask: 255.255.240.0.
  2. Block size in the third octet: 256 − 240 = 16. Blocks start at 0, 16, 32, 48 …
  3. The third octet 45 lies in the block 32 to 47 (in binary, 00101101 AND 11110000 = 00100000 = 32).
  4. Network 172.16.32.0, broadcast 172.16.47.255 (third octet 47, fourth octet all 1s).
  5. First host 172.16.32.1, last host 172.16.47.254, usable hosts 2^12 − 2 = 4,094.

The same calculation as a program

The program converts the address to a 32-bit number, builds the mask from the prefix, and gets the network with AND and the broadcast with OR of the inverted mask.

#include <cstdint>
#include <cstdio>
#include <string>

uint64_t toInt(const std::string& ip) {
    uint64_t n = 0, part = 0;
    for (char c : ip + ".") {
        if (c == '.') { n = (n << 8) | part; part = 0; }
        else part = part * 10 + (c - '0');
    }
    return n;
}

std::string toIp(uint64_t n) {
    return std::to_string((n >> 24) & 255) + "." + std::to_string((n >> 16) & 255) + "." +
           std::to_string((n >> 8) & 255) + "." + std::to_string(n & 255);
}

void describe(const std::string& cidr) {
    size_t slash = cidr.find('/');
    int prefix = std::stoi(cidr.substr(slash + 1));
    uint64_t mask = (0xFFFFFFFFull << (32 - prefix)) & 0xFFFFFFFFull;
    uint64_t network = toInt(cidr.substr(0, slash)) & mask;
    uint64_t broadcast = network | (~mask & 0xFFFFFFFFull);
    std::string rows[6][2] = {
        {"mask", toIp(mask)},
        {"network", toIp(network)},
        {"broadcast", toIp(broadcast)},
        {"first host", toIp(network + 1)},
        {"last host", toIp(broadcast - 1)},
        {"hosts", std::to_string((1ull << (32 - prefix)) - 2)},
    };
    std::printf("%s\n", cidr.c_str());
    for (auto& row : rows) std::printf("  %-11s%s\n", row[0].c_str(), row[1].c_str());
}

int main() {
    describe("192.168.10.150/26");
    describe("172.16.45.200/20");
    return 0;
}
public class Main {
    static long toLong(String ip) {
        long n = 0;
        for (String part : ip.split("[.]")) n = (n << 8) | Long.parseLong(part);
        return n;
    }

    static String toIp(long n) {
        return ((n >> 24) & 255) + "." + ((n >> 16) & 255) + "." + ((n >> 8) & 255) + "." + (n & 255);
    }

    static void describe(String cidr) {
        String[] parts = cidr.split("/");
        int prefix = Integer.parseInt(parts[1]);
        long mask = (0xFFFFFFFFL << (32 - prefix)) & 0xFFFFFFFFL;
        long network = toLong(parts[0]) & mask;
        long broadcast = network | (~mask & 0xFFFFFFFFL);
        String[][] rows = {
            {"mask", toIp(mask)},
            {"network", toIp(network)},
            {"broadcast", toIp(broadcast)},
            {"first host", toIp(network + 1)},
            {"last host", toIp(broadcast - 1)},
            {"hosts", String.valueOf((1L << (32 - prefix)) - 2)},
        };
        System.out.println(cidr);
        for (String[] row : rows) System.out.println(String.format("  %-11s%s", row[0], row[1]));
    }

    public static void main(String[] args) {
        describe("192.168.10.150/26");
        describe("172.16.45.200/20");
    }
}
def to_int(ip):
    a, b, c, d = (int(x) for x in ip.split("."))
    return (a << 24) | (b << 16) | (c << 8) | d


def to_ip(n):
    return ".".join(str((n >> shift) & 255) for shift in (24, 16, 8, 0))


def describe(cidr):
    ip, prefix = cidr.split("/")
    prefix = int(prefix)
    mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF
    network = to_int(ip) & mask
    broadcast = network | (~mask & 0xFFFFFFFF)
    rows = [
        ("mask", to_ip(mask)),
        ("network", to_ip(network)),
        ("broadcast", to_ip(broadcast)),
        ("first host", to_ip(network + 1)),
        ("last host", to_ip(broadcast - 1)),
        ("hosts", 2 ** (32 - prefix) - 2),
    ]
    print(cidr)
    for label, value in rows:
        print(f"  {label:<11}{value}")


for cidr in ["192.168.10.150/26", "172.16.45.200/20"]:
    describe(cidr)
function toInt(ip) {
  return ip.split(".").reduce((n, part) => ((n << 8) | Number(part)) >>> 0, 0);
}

function toIp(n) {
  return [24, 16, 8, 0].map((shift) => (n >>> shift) & 255).join(".");
}

function describe(cidr) {
  const [ip, bits] = cidr.split("/");
  const prefix = Number(bits);
  const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
  const network = (toInt(ip) & mask) >>> 0;
  const broadcast = (network | (~mask >>> 0)) >>> 0;
  const rows = [
    ["mask", toIp(mask)],
    ["network", toIp(network)],
    ["broadcast", toIp(broadcast)],
    ["first host", toIp(network + 1)],
    ["last host", toIp(broadcast - 1)],
    ["hosts", 2 ** (32 - prefix) - 2],
  ];
  console.log(cidr);
  for (const [label, value] of rows) console.log("  " + label.padEnd(11) + value);
}

for (const cidr of ["192.168.10.150/26", "172.16.45.200/20"]) describe(cidr);
192.168.10.150/26
  mask       255.255.255.192
  network    192.168.10.128
  broadcast  192.168.10.191
  first host 192.168.10.129
  last host  192.168.10.190
  hosts      62
172.16.45.200/20
  mask       255.255.240.0
  network    172.16.32.0
  broadcast  172.16.47.255
  first host 172.16.32.1
  last host  172.16.47.254
  hosts      4094

VLSM: subnets of different sizes

Equal subnets waste addresses when needs differ. Variable Length Subnet Masking gives each subnet the smallest prefix that fits, allocating the largest first so every block stays aligned.

Task. From 192.168.10.0/24, serve a LAN of 100 hosts, a LAN of 50, a LAN of 20 and a point-to-point router link needing 2.

NeedSmallest fitPrefixNetworkUsable rangeBroadcast
100 hosts2^7 − 2 = 126/25192.168.10.0.1 to .126.127
50 hosts2^6 − 2 = 62/26192.168.10.128.129 to .190.191
20 hosts2^5 − 2 = 30/27192.168.10.192.193 to .222.223
2 hosts2^2 − 2 = 2/30192.168.10.224.225 to .226.227

Addresses 192.168.10.228 to .255 stay free for later. With equal /26 subnets, the 100-host LAN would not have fitted at all.

needs10050202.0/25.128/26.192/27.224/30.228–.255 free2 hosts: 2^2 − 2 = 2 fits, so /30, block of 4
VLSM: each subnet the smallest block that fits. Example: 192.168.10.0/24 for LANs of 100, 50 and 20 hosts and a 2-host link
  1. Sort the needs largest first. 100 hosts need 7 host bits, because 2^7 − 2 = 126 is the first that fits, so a /25 of 128 addresses at .0, hosts .1 to .126.
  2. 50 hosts fit in 2^6 − 2 = 62: a /26. The next free address, .128, is a multiple of 64, so the block starts there, hosts .129 to .190.
  3. 20 hosts fit in 2^5 − 2 = 30: a /27. The next free address, .192, is a multiple of 32, so the block starts there, hosts .193 to .222.
  4. The router link needs 2 addresses, so a /30 of 4 at .224. Because each block went largest first, every one started on a multiple of its size, and .228 to .255 stay free for later.

Supernetting (route aggregation)

Supernetting is the reverse: combining contiguous networks into one shorter prefix so a router advertises one route instead of many. The four networks 192.168.0.0/24 to 192.168.3.0/24 share their first 22 bits, so they summarise as 192.168.0.0/22. The networks must be contiguous, their count must be a power of 2, and the first network must start on a multiple of the block (here the third octet 0 is a multiple of 4). 192.168.1.0 to 192.168.4.0 cannot form one /22, because 1 is not a multiple of 4.

third octet192.168.1.0/2400000001192.168.2.0/2400000010192.168.3.0/2400000011192.168.4.0/2400000100common prefix: 16 + 5 = 21 bits192.168.0.0/21 would hold 8 networks, not 4
Summarising four networks into one route. Example: 192.168.0.0/24 to 192.168.3.0/24, then 192.168.1.0/24 to 192.168.4.0/24
  1. The four third octets differ only in their last 2 bits, so the first 22 bits are shared and one route, 192.168.0.0/22, covers all four and nothing else.
  2. Shift the set by one and the third octets 1 to 4 agree only in their first 5 bits. The shared prefix shrinks to /21, a block of 8 networks from 192.168.0.0, so these four cannot be one route.

IPv4 vs IPv6

IPv4's 4.3 billion addresses ran out (IANA handed out its last free /8 blocks in 2011), and NAT has stretched them since. IPv6 is the long-term fix.

AspectIPv4IPv6
Address length32 bits128 bits
Address space2^32, about 4.3 × 10^92^128, about 3.4 × 10^38
NotationDotted decimal: 192.168.1.10Eight hex groups with colons: 2001:db8::1
Header20 to 60 bytes, with options40 bytes fixed, plus chained extension headers
Header checksumYesNo
FragmentationSender and routersSender only
BroadcastYesNo: multicast and anycast instead
ConfigurationManual or DHCPSLAAC auto-configuration, DHCPv6 or manual
Finding a MAC addressARPNeighbour Discovery (ICMPv6)
Loopback127.0.0.1::1

IPv6 shortening rules: drop leading zeros in each group, then replace one run of all-zero groups with ::. So 2001:0db8:0000:0000:0000:0000:0000:0001 becomes 2001:db8:0:0:0:0:0:1 and then 2001:db8::1. Using :: twice would make the address ambiguous.

Common mistakes

  • Using 2^h for usable hosts: subtract 2 (network and broadcast), except on /31 and /32.
  • Treating a host address as a network address: 192.168.10.100/26 is a host in 192.168.10.64/26, not a network.
  • Mixing the old rule of 2^s − 2 subnets (no "subnet zero") with the modern 2^s; say which you use.
  • Calling 172.32.0.1 private: the 172.16.0.0/12 range ends at 172.31.255.255.
  • Forgetting VLSM order: allocate the largest subnet first, or blocks end up misaligned.
  • Writing :: twice in an IPv6 address.

Interview questions

Find the network, broadcast and host range of 192.168.1.77/27. /27 leaves 5 host bits, so the block size is 32 and subnets start at .0, .32, .64, .96. 77 lies between 64 and 95, so the network is 192.168.1.64, the broadcast 192.168.1.95, and hosts .65 to .94, which is 30 usable addresses.

How many subnets and hosts per subnet do you get by dividing a /24 into /28s? Four bits are borrowed, giving 2^4 = 16 subnets. Each has 4 host bits: 16 addresses and 14 usable hosts.

Why was CIDR introduced? Classful addressing wasted addresses (class B was too big for most and class C too small) and filled routing tables with one entry per class C network. CIDR allows any prefix length and lets contiguous blocks be advertised as one aggregate route.

What is the difference between a public and a private IP address? A public address is globally unique and routable on the internet. A private address (10/8, 172.16/12, 192.168/16) is reused inside many organisations and is not routed publicly, so traffic to the internet passes through NAT, which swaps it for a public address.

What is 127.0.0.1 used for? It is the loopback address: packets sent to it never leave the machine. It is used to test the local TCP/IP stack and to reach services running on the same computer, such as a development server on localhost.

Which prefix would you use on a point-to-point link between two routers? Traditionally a /30, which has exactly two usable addresses. RFC 3021 also allows a /31 on point-to-point links, using both addresses since no broadcast is needed, which saves address space.

How do you shorten an IPv6 address? Remove leading zeros in each 16-bit group, then replace the longest single run of consecutive zero groups with ::, once per address. Each group is four hexadecimal digits, eight groups in all.

Next, read TCP and UDP, and test your subnetting with the Computer Networks (Intermediate) skill test.

Common questions

How many usable hosts are there in a /24 network?

  1. A /24 leaves 8 host bits, which give 2^8 = 256 addresses. The first (all host bits 0) is the network address and the last (all host bits 1) is the broadcast address, so 256 − 2 = 254 can be assigned to hosts.

What are the private IP address ranges?

RFC 1918 reserves three IPv4 blocks for private networks: 10.0.0.0/8 (10.0.0.0 to 10.255.255.255), 172.16.0.0/12 (172.16.0.0 to 172.31.255.255) and 192.168.0.0/16 (192.168.0.0 to 192.168.255.255). They are not routed on the public internet, so hosts using them reach it through NAT.

How do you find the network address from an IP address and subnet mask?

Do a bitwise AND of the address and the mask. For 192.168.10.150 with mask 255.255.255.192, only the last octet matters: 150 AND 192 is 10010110 AND 11000000 = 10000000 = 128, so the network address is 192.168.10.128.

What is CIDR notation?

Classless Inter-Domain Routing writes an address with a slash and the number of network bits, such as 192.168.1.0/24, instead of relying on the address class. Prefixes can be any length, so networks can be sized to need, and contiguous networks can be summarised as one route.

What is the difference between IPv4 and IPv6?

IPv4 addresses are 32 bits written in dotted decimal, giving about 4.3 billion addresses. IPv6 addresses are 128 bits written in hexadecimal groups separated by colons. IPv6 also has a fixed 40-byte header, no broadcast, no header checksum and built-in address auto-configuration.

Why do we subtract 2 when counting usable hosts in a subnet?

Two addresses in every subnet are reserved: the one with all host bits 0 names the network itself, and the one with all host bits 1 is the directed broadcast address. The exceptions are /31 point-to-point links, which use both addresses (RFC 3021), and /32, a single host route.

Test yourself

← The TCP/IP Model · TCP and UDP →