Networking & DNS

CIDR notation and subnets explained (/24, /32, /64)

Learn what CIDR suffixes like /24, /32 and /64 mean, how many addresses each covers, and how to use them safely in firewall allowlists and blocklists.

Beginner6 min read

Firewall rules, Fail2ban settings and VPN configs all ask for addresses like 198.51.100.0/24 or 2001:db8::/64. The number after the slash decides how many addresses the rule covers, so getting it wrong can open your server to far more of the internet than you meant, or block your own office. This guide explains CIDR notation in plain terms and shows how to check a range before you put it in a rule.

Before you start

  • You only need a basic idea of what an IP address is. If IPv6 is new to you, read IPv4 vs IPv6 first.
  • For the calculation examples, any Linux machine with python3 works. That includes a Linux VPS.
  • Examples use the documentation ranges 203.0.113.0/24, 198.51.100.0/24 and 2001:db8::/32. Replace them with your real addresses.

What CIDR notation means

An IPv4 address is 32 bits long, written as four numbers from 0 to 255. CIDR (Classless Inter-Domain Routing) adds a slash and a number, called the prefix length. It says how many bits, counted from the left, are fixed. The remaining bits can be anything, and every combination is part of the range.

  • /32 fixes all 32 bits. It means exactly one address.
  • /24 fixes the first 24 bits (the first three numbers). The last number can be 0 to 255, so it covers 256 addresses.
  • /16 fixes the first two numbers. It covers 65,536 addresses.
  • /0 fixes nothing. It means every IPv4 address on the internet.

The rule of thumb: a smaller number after the slash means a bigger range. Each step down doubles the size. A /23 is two /24s, and a /25 is half of one.

The number of addresses is 2 to the power of (32 minus the prefix):

/32 -> 2^0  = 1 address
/30 -> 2^2  = 4 addresses
/28 -> 2^4  = 16 addresses
/24 -> 2^8  = 256 addresses
/16 -> 2^16 = 65,536 addresses

You will also see the older netmask form. 255.255.255.0 is the same as /24, and 255.255.255.255 is the same as /32. Linux tools such as ip addr show the CIDR form.

The network address must line up

A range always starts on a boundary that matches its size. 198.51.100.0/24 is valid because the last 8 bits are zero. 198.51.100.77/24 is not a clean network address. Some tools quietly round it down to 198.51.100.0/24, others reject it. Write the network address itself to avoid surprises.

The same applies to smaller blocks. A /28 holds 16 addresses, so valid starts are .0, .16, .32, .48 and so on. 198.51.100.20/28 is not a clean start; the block it belongs to is 198.51.100.16/28, which covers .16 to .31.

IPv6 prefixes: /128, /64 and /48

IPv6 addresses are 128 bits long, so the numbers are bigger, but the idea is the same.

  • /128 is one single IPv6 address, like /32 in IPv4.
  • /64 is the standard size of one IPv6 subnet. A single server is often given a whole /64. That is more addresses than the entire IPv4 internet.
  • /56 and /48 are common sizes handed to a home connection or a site. They contain many /64 subnets.

This matters for security. An attacker who controls a /64 can rotate through billions of addresses, so blocking one IPv6 address (/128) often does nothing. When you block an IPv6 source, block its /64. When you allow your own IPv6 connection, allow the prefix your provider gives you, because your device address inside it can change.

Note: Your home or office IPv6 prefix may change when the router restarts, depending on your ISP. If you allowlist it, keep a second way in, such as the browser console in your VPS provider's control panel.

Calculate a range before you use it

Python's ipaddress module is on every modern Linux system and gives an exact answer. This prints the size, first and last address of a range:

Bash
python3 -c "import ipaddress as i; n=i.ip_network('198.51.100.16/28'); print(n.num_addresses, n[0], n[-1])"
16 198.51.100.16 198.51.100.31

If you pass an address that is not on a boundary, Python refuses with has host bits set. That is a useful check: it tells you the range is not what you typed.

To check whether one address falls inside a range:

Bash
python3 -c "import ipaddress as i; print(i.ip_address('198.51.100.25') in i.ip_network('198.51.100.16/28'))"

On Debian and Ubuntu you can also install ipcalc with sudo apt install ipcalc for a friendlier summary of any IPv4 range.

Using CIDR in firewall rules

Most Linux firewalls take CIDR directly. With UFW, allow SSH only from one office range:

Bash
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp

With firewalld, trust an admin subnet:

Bash
sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.0/24

In Fail2ban's jail.local, keep your own range from ever being banned:

INI
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24

On Windows, New-NetFirewallRule -RemoteAddress 203.0.113.0/24 works the same way.

Warning: Before you restrict SSH (or RDP on port 3389 on Windows) to a range, make sure your current IP is inside it. Check it with the Python test above. A typo such as /42 or the wrong start address can lock you out the moment the rule applies.

Good habits:

  • Allow narrowly. Use /32 for a single static IP. Only widen to /24 if you really connect from several addresses in that block.
  • Block broadly with care. Blocking a /16 can cut off a whole ISP or country region, including real customers.
  • Never allow 0.0.0.0/0 or ::/0 by accident. Those mean "everyone".
  • Comment your rules. Write down who owns each range so you can remove it later.

Check it works

After adding a CIDR rule, confirm the firewall stored the range you meant. For UFW, sudo ufw status numbered lists each source. For firewalld, sudo firewall-cmd --zone=trusted --list-sources. Then test from an address inside the range and, if you can, from one outside it.

Troubleshooting

  • Rule shows a different range than you typed: the tool rounded your address down to the network boundary. Recalculate and use the true network address.
  • has host bits set in Python or an "invalid" error in a tool: the address is not the start of a block of that size. Fix the start address or the prefix.
  • Your IPv6 allow rule stopped working: your ISP changed your prefix. Log in through your VPS provider's browser console and update the rule.
  • Blocking one IPv6 address had no effect: the attacker moved to another address in the same /64. Block the /64 instead.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.