Subnetting
Overview
Subnetting divides a single IP network into smaller segments. Each subnet has its own network address, broadcast address, and usable host range. Security professionals need subnetting for scope definition (what to scan and what not to), network segmentation analysis, firewall rule interpretation, and understanding how hosts communicate within and across subnets. CIDR (Classless Inter-Domain Routing) notation is the standard way to express subnet sizes.
Key Concepts
Binary Foundation
An IPv4 address is 32 bits split into four octets. A subnet mask identifies which bits represent the network portion and which represent hosts.
IP: 192.168.1.100
Binary: 11000000.10101000.00000001.01100100
Mask: 255.255.255.0
Binary: 11111111.11111111.11111111.00000000
|-------- network --------||hosts-|
CIDR: /24 (24 network bits, 8 host bits)
The network address is the result of a bitwise AND between the IP and the mask. Hosts on the same network share the same network address and can communicate directly (Layer 2). Hosts on different networks require a router.
IP: 11000000.10101000.00000001.01100100 (192.168.1.100)
Mask: 11111111.11111111.11111111.00000000 (255.255.255.0)
AND: 11000000.10101000.00000001.00000000 (192.168.1.0) ← network address
Broadcast: 11000000.10101000.00000001.11111111 (192.168.1.255) ← all host bits = 1
CIDR Notation
CIDR replaced classful addressing. The prefix length (/N) indicates how many bits are the network portion. The remaining bits are for hosts.
Complete CIDR reference table:
CIDR Subnet Mask Wildcard Mask Usable Hosts Addresses
----- ---------------- ---------------- ------------- ----------
/32 255.255.255.255 0.0.0.0 1 (host only) 1
/31 255.255.255.254 0.0.0.1 2 (P2P link) 2
/30 255.255.255.252 0.0.0.3 2 4
/29 255.255.255.248 0.0.0.7 6 8
/28 255.255.255.240 0.0.0.15 14 16
/27 255.255.255.224 0.0.0.31 30 32
/26 255.255.255.192 0.0.0.63 62 64
/25 255.255.255.128 0.0.0.127 126 128
/24 255.255.255.0 0.0.0.255 254 256
/23 255.255.254.0 0.0.1.255 510 512
/22 255.255.252.0 0.0.3.255 1,022 1,024
/21 255.255.248.0 0.0.7.255 2,046 2,048
/20 255.255.240.0 0.0.15.255 4,094 4,096
/19 255.255.224.0 0.0.31.255 8,190 8,192
/18 255.255.192.0 0.0.63.255 16,382 16,384
/17 255.255.128.0 0.0.127.255 32,766 32,768
/16 255.255.0.0 0.0.255.255 65,534 65,536
/15 255.254.0.0 0.1.255.255 131,070 131,072
/14 255.252.0.0 0.3.255.255 262,142 262,144
/13 255.248.0.0 0.7.255.255 524,286 524,288
/12 255.240.0.0 0.15.255.255 1,048,574 1,048,576
/11 255.224.0.0 0.31.255.255 2,097,150 2,097,152
/10 255.192.0.0 0.63.255.255 4,194,302 4,194,304
/9 255.128.0.0 0.127.255.255 8,388,606 8,388,608
/8 255.0.0.0 0.255.255.255 16,777,214 16,777,216
Formulas: - Total addresses = 2^(32 - prefix) - Usable hosts = 2^(32 - prefix) - 2 (subtract network and broadcast) - Exception: /31 provides 2 usable addresses (RFC 3021, point-to-point links) - Exception: /32 is a single host address
Wildcard Mask
The inverse of the subnet mask. Used in ACLs (Cisco), OSPF configuration, and some firewall rules. Calculated by subtracting the subnet mask from 255.255.255.255.
Subnet mask: 255.255.255.240 (/28)
Wildcard mask: 0.0.0.15
Meaning: match the first 28 bits exactly, ignore the last 4 bits
Subnet Calculation Method
To calculate a subnet's properties from any IP/CIDR:
Example: 10.50.100.200/21
Step 1 — Find the block size
Host bits = 32 - 21 = 11
Block size = 2^11 = 2048 addresses
In the third octet: 2048 / 256 = 8 (block increments by 8 in the 3rd octet)
Step 2 — Find the network address
Third octet: 100 / 8 = 12 remainder 4
Network starts at: 12 × 8 = 96
Network address: 10.50.96.0
Step 3 — Find the broadcast address
Next network: 10.50.104.0
Broadcast: 10.50.103.255
Step 4 — Usable host range
First host: 10.50.96.1
Last host: 10.50.103.254
Usable hosts: 2046
Quick method for common subnets (last octet):
CIDR Block Size Network Boundaries (last octet)
/25 128 0, 128
/26 64 0, 64, 128, 192
/27 32 0, 32, 64, 96, 128, 160, 192, 224
/28 16 0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240
/29 8 0, 8, 16, 24, 32, 40, ...
/30 4 0, 4, 8, 12, 16, 20, ...
To find which subnet an IP belongs to: divide the relevant octet by the block size, drop the remainder, multiply back.
Example: Which /26 subnet contains 192.168.1.200?
Block size for /26 = 64
200 / 64 = 3 remainder 8
Network: 3 × 64 = 192 → 192.168.1.192/26
Broadcast: 192.168.1.255
Host range: 192.168.1.193 - 192.168.1.254
Private Address Ranges (RFC 1918)
Reserved for internal use. Not routable on the public internet.
Range CIDR Addresses Typical Use
----------------------- ----------- -------------- -----------------
10.0.0.0 - 10.255.255.255 10.0.0.0/8 16,777,216 Enterprise networks
172.16.0.0 - 172.31.255.255 172.16.0.0/12 1,048,576 Mid-size networks
192.168.0.0 - 192.168.255.255 192.168.0.0/16 65,536 Home/small office
Special-Purpose Addresses (RFC 6890)
Range CIDR Purpose
----------------- --------------- ----------------------------
0.0.0.0 0.0.0.0/8 "This" network
127.0.0.0 127.0.0.0/8 Loopback
169.254.0.0 169.254.0.0/16 Link-local (APIPA)
192.0.2.0 192.0.2.0/24 Documentation (TEST-NET-1)
198.51.100.0 198.51.100.0/24 Documentation (TEST-NET-2)
203.0.113.0 203.0.113.0/24 Documentation (TEST-NET-3)
224.0.0.0 224.0.0.0/4 Multicast
240.0.0.0 240.0.0.0/4 Reserved
255.255.255.255 255.255.255.255/32 Limited broadcast
Supernetting (Aggregation)
Combining multiple smaller subnets into a single larger prefix. Used for route summarization and scope definition.
Individual subnets:
192.168.0.0/24
192.168.1.0/24
192.168.2.0/24
192.168.3.0/24
Aggregated (supernet):
192.168.0.0/22 (covers .0.0 through .3.255)
Aggregation works when subnets are contiguous and align on the supernet boundary. Check alignment: the network address must be divisible by the total addresses in the supernet.
192.168.0.0 / 1024 = 192 × 65536 + 168 × 256 + 0 = divisible → valid /22 boundary
192.168.1.0 / 1024 = not on boundary → cannot start a /22 here
Practical Examples
Subnet Calculation with Python
Python's ipaddress module (standard library, no install needed) handles all subnet calculations:
# python3
import ipaddress
# Calculate subnet properties
net = ipaddress.ip_network('10.50.100.0/21', strict=False)
print(f'Network: {net.network_address}')
print(f'Broadcast: {net.broadcast_address}')
print(f'Netmask: {net.netmask}')
print(f'Wildcard: {net.hostmask}')
print(f'Hosts: {net.num_addresses - 2}')
print(f'First: {list(net.hosts())[0]}')
print(f'Last: {list(net.hosts())[-1]}')
Output:
Network: 10.50.96.0
Broadcast: 10.50.103.255
Netmask: 255.255.248.0
Wildcard: 0.0.7.255
Hosts: 2046
First: 10.50.96.1
Last: 10.50.103.254
# python3
import ipaddress
# Find which subnet an IP belongs to
ip = ipaddress.ip_address('192.168.1.200')
net = ipaddress.ip_network('192.168.1.192/26')
print(f'{ip} in {net}: {ip in net}')
# Check if two IPs are on the same subnet
net1 = ipaddress.ip_network('10.0.0.50/24', strict=False)
net2 = ipaddress.ip_network('10.0.0.200/24', strict=False)
print(f'Same subnet: {net1 == net2}')
# List all /28 subnets within a /24
parent = ipaddress.ip_network('192.168.1.0/24')
for subnet in parent.subnets(new_prefix=28):
hosts = list(subnet.hosts())
print(f'{subnet} range: {hosts[0]} - {hosts[-1]}')
Subnet Calculation with ipcalc
ipcalc provides quick subnet lookups from the command line:
# ipcalc
# https://jodies.de/ipcalc
# Install if not present
sudo apt install -y ipcalc
# Calculate subnet details
ipcalc 192.168.1.100/26
Output:
Address: 192.168.1.100 11000000.10101000.00000001.01 100100
Netmask: 255.255.255.192 = 26 11111111.11111111.11111111.11 000000
Wildcard: 0.0.0.63 00000000.00000000.00000000.00 111111
=>
Network: 192.168.1.64/26 11000000.10101000.00000001.01 000000
HostMin: 192.168.1.65 11000000.10101000.00000001.01 000001
HostMax: 192.168.1.126 11000000.10101000.00000001.01 111110
Broadcast: 192.168.1.127 11000000.10101000.00000001.01 111111
Hosts/Net: 62 Class C, Private Internet
Scanning by Subnet
Understanding subnets is essential for scoping scans correctly:
# List all hosts in a subnet without scanning (dry run)
# Nmap
# https://nmap.org/
nmap -sL 192.168.1.0/24
# Ping sweep a specific subnet
# Nmap
# https://nmap.org/
nmap -sn 10.10.10.0/24
# Scan multiple subnets
# Nmap
# https://nmap.org/
nmap -sn 192.168.1.0/24 192.168.2.0/24 10.0.0.0/16
# Exclude specific hosts from a subnet scan
# Nmap
# https://nmap.org/
nmap -sn 192.168.1.0/24 --exclude 192.168.1.1,192.168.1.254
# Scan from a target list (one IP/CIDR per line)
# Nmap
# https://nmap.org/
nmap -sn -iL targets.txt
Identifying the Local Subnet
# Show IP address and subnet mask for all interfaces
ip addr show
# Example output:
# inet 192.168.1.50/24 brd 192.168.1.255 scope global eth0
# ↑ IP address ↑ CIDR ↑ broadcast
# Show routing table — reveals connected subnets and gateway
ip route show
# Example output:
# default via 192.168.1.1 dev eth0 ← default gateway
# 192.168.1.0/24 dev eth0 scope link ← directly connected subnet
Scope Definition for Penetration Testing
Engagements define scope using CIDR notation. Understanding subnetting prevents scanning out-of-scope targets.
Scope: 10.10.10.0/24
In scope: 10.10.10.1 through 10.10.10.254
Out of scope: 10.10.11.1 (different subnet)
Scope: 172.16.0.0/20
In scope: 172.16.0.1 through 172.16.15.254
Out of scope: 172.16.16.1 (different subnet)
Verify before scanning:
# python3
import ipaddress
scope = ipaddress.ip_network('172.16.0.0/20')
target = ipaddress.ip_address('172.16.15.200')
print(f'{target} in scope: {target in scope}')
target2 = ipaddress.ip_address('172.16.16.1')
print(f'{target2} in scope: {target2 in scope}')
Output:
172.16.15.200 in scope: True
172.16.16.1 in scope: False
VLSM (Variable Length Subnet Masking)
Real networks use different subnet sizes for different segments. VLSM allocates subnets efficiently by matching the prefix length to the number of required hosts.
Example: Assign subnets from 10.0.0.0/24 for the following needs:
Requirement Hosts Needed Best Fit Subnet Range
-------------------- ----------- -------- ---------------- ---------------------
Server VLAN 50 /26 (62) 10.0.0.0/26 10.0.0.1 - .62
Workstation VLAN 25 /27 (30) 10.0.0.64/27 10.0.0.65 - .94
Management VLAN 10 /28 (14) 10.0.0.96/28 10.0.0.97 - .110
Point-to-point link 2 /30 (2) 10.0.0.112/30 10.0.0.113 - .114
Allocate largest subnets first to avoid fragmentation. Each subnet starts at the next available boundary aligned to its block size.
IPv6 Subnetting Basics
IPv6 uses 128-bit addresses with a different subnetting model. The standard allocation is /64 for a single subnet (2^64 host addresses).
IPv6 address: 2001:0db8:0001:000a:0000:0000:0000:0001
Structure: |------ 48 ------||16|| -------- 64 --------|
Global prefix Sub Interface ID
net
Common allocations:
/48 → Organization (65,536 subnets)
/64 → Single subnet (standard)
/128 → Single host
# Show IPv6 addresses and prefix length
ip -6 addr show
# Calculate IPv6 subnet
python3 -c "
import ipaddress
net = ipaddress.ip_network('2001:db8:1:a::/64')
print(f'Network: {net.network_address}')
print(f'Prefix: /{net.prefixlen}')
print(f'Addresses: {net.num_addresses}')
"