CIDR Notation Explained: A Practical Guide for Programmers with Examples


2 views

CIDR (Classless Inter-Domain Routing) notation is a compact representation of IP addresses and their associated routing prefix. Instead of using subnet masks like 255.255.255.0, CIDR uses a simpler format: IP_address/prefix_length (e.g., 192.168.1.0/24).

The number after the slash represents the number of bits in the network portion of the address. For example:

192.168.1.0/24 means:
- First 24 bits are network address
- Last 8 bits (32-24) are host addresses
- Equivalent to subnet mask 255.255.255.0

Let's break down some common CIDR blocks:

10.0.0.0/8:
- Network portion: 10.0.0.0
- Host range: 10.0.0.1 - 10.255.255.254
- Total addresses: 16,777,214

172.16.0.0/12:
- Network portion: 172.16.0.0
- Host range: 172.16.0.1 - 172.31.255.254
- Total addresses: 1,048,574

Here's a Python function to calculate the network range from a CIDR:

import ipaddress

def cidr_info(cidr):
    network = ipaddress.ip_network(cidr, strict=False)
    print(f"Network: {network.network_address}")
    print(f"Broadcast: {network.broadcast_address}")
    print(f"Host range: {network.network_address+1} - {network.broadcast_address-1}")
    print(f"Total hosts: {network.num_addresses-2}")

cidr_info("192.168.1.0/24")

When working with AWS, GCP, or Azure, you'll often see these CIDR ranges:

VPC default range: 10.0.0.0/16 (65,534 hosts)
Subnet typical range: 10.0.1.0/24 (254 hosts)
Kubernetes pod network: 10.244.0.0/16
Service network: 10.96.0.0/12

When designing networks, it's crucial to avoid overlaps. Here's how to check:

def check_overlap(cidr1, cidr2):
    n1 = ipaddress.ip_network(cidr1)
    n2 = ipaddress.ip_network(cidr2)
    return n1.overlaps(n2)

print(check_overlap("10.0.0.0/16", "10.0.1.0/24"))  # Returns True

CIDR (Classless Inter-Domain Routing) might seem like just a "/" followed by a number, but it's actually a powerful way to represent IP addresses and their routing prefixes. Let me show you what really happens when you see something like 192.168.1.0/24.


// Binary representation of 192.168.1.0/24
IP:     11000000.10101000.00000001.00000000
Mask:   11111111.11111111.11111111.00000000
Network:11000000.10101000.00000001.00000000

The number after the slash (prefix length) indicates how many bits are fixed as the network portion. Here's how it translates:

CIDR Subnet Mask Usable Hosts
/24 255.255.255.0 254
/25 255.255.255.128 126
/26 255.255.255.192 62

Here's my mental shortcut for /24 to /30 ranges:


1. Remember the key numbers: 0, 128, 192, 224, 240, 248, 252, 254, 255
2. For /25 and above, subtract 256 - the last octet value to get block size
   Example: /26 = 255.255.255.192 → 256-192=64 → block size=64
3. Valid subnets increment by the block size

Here's how you can work with CIDR in different programming languages:

Python example using ipaddress module:


import ipaddress
network = ipaddress.IPv4Network('192.168.1.0/24')
print(f"Network: {network.network_address}")
print(f"Broadcast: {network.broadcast_address}")
print(f"Hosts: {network.num_addresses - 2}")

JavaScript implementation:


function cidrToRange(cidr) {
  const [ip, prefix] = cidr.split('/');
  const mask = 0xffffffff << (32 - parseInt(prefix));
  const start = (ipToLong(ip) & mask) + 1;
  const end = start + (1 << (32 - parseInt(prefix))) - 3;
  return [longToIp(start), longToIp(end)];
}

In real-world scenarios, you'll often see these CIDR notations:

  • /32 - Single host (used in firewall rules)
  • /31 - Point-to-point links (RFC 3021)
  • /30 - Common for WAN links (2 usable hosts)
  • /29 - Small networks (6 usable hosts)
  • /28 - Medium networks (14 usable hosts)
  • /24 - Default for many private networks (254 hosts)

When you need to combine or split networks:


# Python example - creating subnets
base_net = ipaddress.IPv4Network('192.168.0.0/16')
subnets = list(base_net.subnets(prefixlen_diff=8))
# Creates 256 /24 networks from a /16

Remember that supernetting (combining networks) requires contiguous address space with matching prefix bits.