What is CIDR notation?
CIDR notation is a base address plus a slash and a number — that number is a count of fixed bits, and it tells you exactly how large the range is.
Contents
If you have seen 192.168.0.0/24 in a firewall rule, an allow-list, or a cloud VPC config and wondered what the /24 actually means, that is the whole question this post answers. CIDR notation — Classless Inter-Domain Routing — is just a compact way to write a range of IP addresses instead of one. A base address, a slash, and a number. The number is where all the meaning lives, and it is simpler than it looks.
The one-line answer
192.168.0.0/24 means "every address that shares the first 24 bits with 192.168.0.0." Since an IPv4 address is 32 bits, fixing the first 24 leaves 8 bits free to vary. Eight free bits give you 2⁸ = 256 addresses: 192.168.0.0 through 192.168.0.255.
So CIDR is two facts glued together: a starting point (the base address) and how many leading bits are locked (the prefix length). Everything else — how big the range is, where it ends, whether a given address is inside it — falls out of those two facts.
Prefix length is a count of fixed bits
Picture an IPv4 address as 32 switches. The prefix length says how many switches, counting from the left, are held fixed by the network. The rest are free.
/24→ 24 fixed (network) bits, 8 free (host) bits./8→ 8 fixed, 24 free./32→ all 32 fixed, 0 free. That is a single address./0→ nothing fixed. That is every IPv4 address at once.
The fixed part is the network prefix; it identifies the block. The free part is the host portion; it identifies an address within the block. A bigger prefix number means more bits are pinned down, which means a smaller range — the numbers move in opposite directions, which trips up most people once.
From prefix to host count
Because each free bit doubles the count, the size of any IPv4 block is 2^(32 − prefix):
| CIDR | Host bits | Addresses | Range |
|---|---|---|---|
10.0.0.0/8 |
24 | 16,777,216 | 10.0.0.0 – 10.255.255.255 |
192.168.0.0/16 |
16 | 65,536 | 192.168.0.0 – 192.168.255.255 |
192.168.0.0/24 |
8 | 256 | 192.168.0.0 – 192.168.0.255 |
203.0.113.7/32 |
0 | 1 | 203.0.113.7 only |
IPv6 works the same way, just with 128 bits, so a /64 — the standard size for a single network segment — still leaves a colossal 2^(128 − 64) addresses. The arithmetic is identical; only the total width changes.
Why allow-lists and classification use CIDR
Ranges are exactly what you want when the thing you care about is a group of addresses. Your office egress, a cloud provider's block, or the reserved private ranges from RFC 1918 are all naturally CIDR blocks:
10.0.0.0/8172.16.0.0/12192.168.0.0/16
Writing "allow 10.0.0.0/8" is both shorter and more honest than pasting sixteen million individual addresses. And the membership test — is this one address inside that block? — is a fast bitwise comparison, not a lookup.
This is also how IP classification works. IANA publishes its special-purpose registries as CIDR blocks: 127.0.0.0/8 for loopback, 169.254.0.0/16 for link-local, 100.64.0.0/10 for carrier-grade NAT, 192.0.2.0/24 for documentation. To classify an address, you check which registered block it lands in. Boundstone's IP endpoint does exactly that: range_classification matches the input against the IANA CIDR ranges and reports whether it is public, private, loopback, reserved, and so on — pure structural fact, no external data required.
Doing it in code
For membership and sizing, you almost never need a library beyond your standard library. Python's ipaddress module handles CIDR completely, and for most allow-list logic it is genuinely all you need — so reach for it first:
import ipaddress
net = ipaddress.ip_network("192.168.0.0/24")
print(net.num_addresses) # 256
print(net.network_address) # 192.168.0.0
print(net.broadcast_address) # 192.168.0.255
print(net.netmask) # 255.255.255.0
# the membership test — the whole point of CIDR
print(ipaddress.ip_address("192.168.0.42") in net) # True
print(ipaddress.ip_address("192.168.1.42") in net) # False
That covers "is this address in my range?" and "how big is this block?" cleanly, offline, in every language that ships an IP library. (For a fuller walkthrough of parsing and classifying addresses in Python, see validating IPs in Python.)
The layer past that is when you want the classification itself — public vs. private vs. bogon — over HTTP, as one consistent contract alongside phone and email checks, without wiring the IANA registry into every service yourself:
curl -s https://api.boundstone.io/v1/verify/ip \
-H "Authorization: Bearer bs_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"ip":"8.8.8.8"}'
{
"valid": true,
"version": 4,
"normalized": "8.8.8.8",
"classification": "public",
"is_public": true,
"is_bogon": false,
"checks": {
"performed": ["format", "version", "range_classification"],
"not_performed": ["geolocation", "asn", "hosting_datacenter", "proxy_vpn_tor", "reputation"]
}
}
You can try the same thing with no key on the IP validator tool.
Where CIDR stops
Here is the boundary, stated plainly. CIDR classification tells you structural facts you can verify from the address alone: which reserved block it belongs to, whether it is publicly routable, whether it is a bogon. It does not tell you where the address physically is, which network operates it, whether it is a datacenter, or whether it is a VPN, proxy, or Tor exit. Those live in the not_performed list — geolocation, asn, hosting_datacenter, proxy_vpn_tor, reputation — because Boundstone does not do them today. IP intelligence needs licensed data, and that has not shipped. The classification is honest about being classification. (For more on the private/reserved side specifically, see what is a private IP.)
That is the point of listing what was not checked: when the response says is_public: true, you know it means the address is not in a reserved range — no more, no less.
The short version
- CIDR = base address
+/prefix; the prefix counts the fixed leading bits. - Bigger prefix → smaller range. IPv4 block size =
2^(32 − prefix). /24= 256 addresses,/8= ~16.7M,/32= one address.- Use CIDR for allow-lists and range membership; the stdlib (
ipaddress) handles that offline. - Use Boundstone's
range_classificationwhen you want IANA-block classification as part of one contract — and readchecks.not_performedto know precisely what a "valid" does and does not claim.