Topic Overview

Subnetting & CIDR

Master subnetting and CIDR notation for efficient IP address allocation, network segmentation, and network design.

Subnetting divides a network into smaller subnetworks, enabling efficient IP address allocation, improved security through network segmentation, and better network management. CIDR (Classless Inter-Domain Routing) provides a flexible way to represent subnet masks.


What is Subnetting?

Subnetting is the process of dividing a network into smaller logical networks called subnets. Each subnet has its own network address, broadcast address, and range of usable host addresses.

Benefits:

  • Efficient IP usage: Allocate only needed addresses
  • Network segmentation: Isolate traffic, improve security
  • Reduced broadcast domains: Smaller broadcast domains improve performance
  • Better organization: Logical grouping of devices

CIDR Notation

CIDR (Classless Inter-Domain Routing) uses slash notation to specify the network prefix:

192.168.1.0/24
  • 192.168.1.0: Network address
  • /24: Prefix length (24 bits for network, 8 bits for hosts)
  • Subnet mask: 255.255.255.0

CIDR to Subnet Mask Conversion

CIDRSubnet MaskHost BitsUsable Hosts
/24255.255.255.08254
/25255.255.255.1287126
/26255.255.255.192662
/27255.255.255.224530
/28255.255.255.240414
/30255.255.255.25222 (point-to-point)

Subnetting Process

Step 1: Determine Requirements

  • Number of subnets needed
  • Number of hosts per subnet
  • Future growth requirements

Step 2: Calculate Subnet Mask

  • Determine number of subnet bits needed
  • Calculate new prefix length
  • Verify host count meets requirements

Step 3: Calculate Subnet Addresses

  • Network address (all host bits = 0)
  • Broadcast address (all host bits = 1)
  • Usable host range

Examples

Example 1: Subnet 192.168.1.0/24 into 4 Subnets

Original Network: 192.168.1.0/24

  • Network bits: 24
  • Host bits: 8
  • Total addresses: 256
  • Usable hosts: 254

Requirement: 4 subnets

  • Need 2 additional bits (2^2 = 4 subnets)
  • New prefix: /26 (24 + 2)
  • Hosts per subnet: 2^(32-26) - 2 = 64 - 2 = 62

Subnets:

SubnetNetworkBroadcastUsable RangeHosts
1192.168.1.0/26192.168.1.63192.168.1.1 - 192.168.1.6262
2192.168.1.64/26192.168.1.127192.168.1.65 - 192.168.1.12662
3192.168.1.128/26192.168.1.191192.168.1.129 - 192.168.1.19062
4192.168.1.192/26192.168.1.255192.168.1.193 - 192.168.1.25462

Calculation:

Subnet size = 256 / 4 = 64 addresses per subnet
Subnet 1: 192.168.1.0 - 192.168.1.63
Subnet 2: 192.168.1.64 - 192.168.1.127
Subnet 3: 192.168.1.128 - 192.168.1.191
Subnet 4: 192.168.1.192 - 192.168.1.255

Example 2: Variable Length Subnetting (VLSM)

Network: 10.0.0.0/16 Requirements:

  • Subnet A: 1000 hosts → /22 (1022 hosts)
  • Subnet B: 500 hosts → /23 (510 hosts)
  • Subnet C: 250 hosts → /24 (254 hosts)
  • Subnet D: 100 hosts → /25 (126 hosts)

Allocation:

10.0.0.0/16
├── 10.0.0.0/22 (Subnet A: 1000 hosts)
│   └── 10.0.0.0 - 10.0.3.255
├── 10.0.4.0/23 (Subnet B: 500 hosts)
│   └── 10.0.4.0 - 10.0.5.255
├── 10.0.6.0/24 (Subnet C: 250 hosts)
│   └── 10.0.6.0 - 10.0.6.255
└── 10.0.7.0/25 (Subnet D: 100 hosts)
    └── 10.0.7.0 - 10.0.7.127

Subnetting Calculator (Python)

import ipaddress

def calculate_subnet(base_network, num_subnets):
    """Calculate subnets from base network"""
    network = ipaddress.IPv4Network(base_network, strict=False)
    
    # Calculate prefix length for num_subnets
    subnet_bits = (num_subnets - 1).bit_length()
    new_prefix = network.prefixlen + subnet_bits
    
    subnets = list(network.subnets(new_prefix=new_prefix))
    
    results = []
    for i, subnet in enumerate(subnets[:num_subnets], 1):
        results.append({
            'subnet': i,
            'network': str(subnet.network_address),
            'broadcast': str(subnet.broadcast_address),
            'netmask': str(subnet.netmask),
            'cidr': f"{subnet.network_address}/{subnet.prefixlen}",
            'hosts': subnet.num_addresses - 2,
            'first_host': str(subnet.network_address + 1),
            'last_host': str(subnet.broadcast_address - 1)
        })
    
    return results

# Usage
subnets = calculate_subnet("192.168.1.0/24", 4)
for s in subnets:
    print(f"Subnet {s['subnet']}: {s['cidr']}")
    print(f"  Network: {s['network']}")
    print(f"  Broadcast: {s['broadcast']}")
    print(f"  Hosts: {s['hosts']}")
    print(f"  Range: {s['first_host']} - {s['last_host']}")
    print()

Determine if IP is in Subnet

def is_ip_in_subnet(ip, subnet_cidr):
    """Check if IP address belongs to subnet"""
    ip_obj = ipaddress.IPv4Address(ip)
    network = ipaddress.IPv4Network(subnet_cidr, strict=False)
    return ip_obj in network

# Usage
print(is_ip_in_subnet("192.168.1.100", "192.168.1.0/24"))  # True
print(is_ip_in_subnet("192.168.2.100", "192.168.1.0/24"))  # False

Common Pitfalls

  • Forgetting network and broadcast addresses: Usable hosts = 2^n - 2, not 2^n. Fix: Always subtract 2
  • Incorrect subnet mask calculation: Wrong binary math. Fix: Use subnet calculators or verify with tools
  • Overlapping subnets: Subnets that overlap cause routing issues. Fix: Plan subnets carefully, verify no overlap
  • Not accounting for growth: Subnets too small for future needs. Fix: Plan for 20-30% growth
  • Wasting IP addresses: Using /24 when /26 would suffice. Fix: Use VLSM for efficient allocation
  • Confusing network bits and host bits: /24 means 24 network bits, not 24 host bits. Fix: Remember CIDR = network prefix length
  • Not reserving addresses: Forgetting to reserve IPs for routers, gateways. Fix: Reserve first few IPs in each subnet

Interview Questions

Beginner

Q: What is subnetting and why is it used?

A:

Subnetting is dividing a network into smaller logical networks (subnets). Each subnet has its own network address, broadcast address, and range of usable host addresses.

Why used:

  1. Efficient IP allocation: Allocate only needed addresses, reduce waste
  2. Network segmentation: Isolate traffic, improve security
  3. Reduce broadcast domains: Smaller broadcast domains improve performance
  4. Better organization: Logical grouping of devices (departments, floors)
  5. Security: Isolate sensitive networks, control traffic flow

Example:

Original: 192.168.1.0/24 (254 hosts)
Subnetted: 4 subnets of /26 (62 hosts each)
  - Sales: 192.168.1.0/26
  - Engineering: 192.168.1.64/26
  - Marketing: 192.168.1.128/26
  - Admin: 192.168.1.192/26

Benefits:

  • Better security (isolate departments)
  • Reduced broadcast traffic
  • Easier management
  • Efficient IP usage

Intermediate

Q: Given the network 172.16.0.0/16, create 8 subnets. What are the network addresses, subnet masks, and usable host ranges?

A:

Original Network: 172.16.0.0/16

  • Network bits: 16
  • Host bits: 16
  • Total addresses: 65,536
  • Usable hosts: 65,534

Requirement: 8 subnets

  • Need 3 additional bits (2^3 = 8 subnets)
  • New prefix: /19 (16 + 3)
  • Hosts per subnet: 2^(32-19) - 2 = 8,192 - 2 = 8,190

Calculation:

Subnet size = 65,536 / 8 = 8,192 addresses per subnet
Increment = 8,192

Subnets:

SubnetNetworkSubnet MaskBroadcastUsable RangeHosts
1172.16.0.0/19255.255.224.0172.16.31.255172.16.0.1 - 172.16.31.2548,190
2172.16.32.0/19255.255.224.0172.16.63.255172.16.32.1 - 172.16.63.2548,190
3172.16.64.0/19255.255.224.0172.16.95.255172.16.64.1 - 172.16.95.2548,190
4172.16.96.0/19255.255.224.0172.16.127.255172.16.96.1 - 172.16.127.2548,190
5172.16.128.0/19255.255.224.0172.16.159.255172.16.128.1 - 172.16.159.2548,190
6172.16.160.0/19255.255.224.0172.16.191.255172.16.160.1 - 172.16.191.2548,190
7172.16.192.0/19255.255.224.0172.16.223.255172.16.192.1 - 172.16.223.2548,190
8172.16.224.0/19255.255.224.0172.16.255.255172.16.224.1 - 172.16.255.2548,190

Verification:

  • All subnets are /19 (consistent)
  • No overlapping addresses
  • Total addresses: 8 × 8,192 = 65,536 ✓
  • Usable hosts: 8 × 8,190 = 65,520

Senior

Q: Design a subnetting scheme for a cloud provider that needs to allocate subnets to customers dynamically. How do you handle variable subnet sizes, prevent IP exhaustion, and ensure efficient allocation?

A:

class DynamicSubnetAllocator {
  private ipPool: IPPool;
  private allocations: Map<string, Allocation>;
  private subnetTree: SubnetTree; // Binary tree for efficient search
  
  constructor() {
    // Large IP pool (e.g., 10.0.0.0/8)
    this.ipPool = new IPPool("10.0.0.0/8");
    this.subnetTree = new SubnetTree();
  }
  
  // 1. Variable Length Subnet Allocation (VLSM)
  async allocateSubnet(customerId: string, size: number): Promise<Allocation> {
    // Calculate required CIDR
    const requiredCIDR = this.calculateCIDR(size);
    
    // Find smallest available subnet that fits
    const subnet = await this.findSmallestFit(requiredCIDR);
    
    if (!subnet) {
      // Try to split larger subnet
      const largerSubnet = await this.findLargerSubnet(requiredCIDR);
      if (largerSubnet) {
        return await this.splitAndAllocate(largerSubnet, requiredCIDR, customerId);
      }
      throw new Error("IP pool exhausted");
    }
    
    // Allocate and track
    const allocation = {
      customerId,
      subnet,
      allocatedAt: Date.now(),
      status: 'active'
    };
    
    this.allocations.set(customerId, allocation);
    this.subnetTree.markAllocated(subnet);
    
    return allocation;
  }
  
  // 2. Calculate Required CIDR
  calculateCIDR(hosts: number): number {
    // Add 2 for network and broadcast
    // Add 10% buffer for growth
    const required = Math.ceil(hosts * 1.1) + 2;
    const bits = Math.ceil(Math.log2(required));
    return 32 - bits;
  }
  
  // 3. Find Smallest Fit (Efficient Search)
  async findSmallestFit(requiredCIDR: number): Promise<Subnet | null> {
    // Use binary tree for O(log n) search
    return await this.subnetTree.findSmallestAvailable(requiredCIDR);
  }
  
  // 4. Subnet Splitting
  async splitAndAllocate(
    parentSubnet: Subnet,
    requiredCIDR: number,
    customerId: string
  ): Promise<Allocation> {
    // Split parent subnet
    const subnets = this.splitSubnet(parentSubnet);
    
    // Allocate first subnet to customer
    const allocated = subnets[0];
    this.subnetTree.markAllocated(allocated);
    
    // Add remaining subnets back to pool
    for (let i = 1; i < subnets.length; i++) {
      this.subnetTree.addAvailable(subnets[i]);
    }
    
    return {
      customerId,
      subnet: allocated,
      allocatedAt: Date.now()
    };
  }
  
  // 5. Subnet Reclamation
  async deallocateSubnet(customerId: string): Promise<void> {
    const allocation = this.allocations.get(customerId);
    if (!allocation) return;
    
    // Mark as available
    this.subnetTree.markAvailable(allocation.subnet);
    
    // Try to merge with adjacent subnets
    await this.mergeAdjacentSubnets(allocation.subnet);
    
    this.allocations.delete(customerId);
  }
  
  // 6. Subnet Merging (Defragmentation)
  async mergeAdjacentSubnets(subnet: Subnet): Promise<void> {
    // Find adjacent subnets that can be merged
    const parent = this.getParentSubnet(subnet);
    const siblings = this.getSiblingSubnets(parent);
    
    // If all siblings are available, merge
    if (siblings.every(s => this.isAvailable(s))) {
      this.subnetTree.removeAvailable(siblings);
      this.subnetTree.addAvailable(parent);
    }
  }
  
  // 7. IP Exhaustion Prevention
  async checkPoolHealth(): Promise<HealthStatus> {
    const stats = await this.subnetTree.getStats();
    
    return {
      totalAddresses: stats.total,
      allocatedAddresses: stats.allocated,
      availableAddresses: stats.available,
      utilization: stats.allocated / stats.total,
      fragmentation: this.calculateFragmentation()
    };
  }
  
  // 8. Efficient Data Structure
  class SubnetTree {
    private root: SubnetNode;
    
    findSmallestAvailable(requiredCIDR: number): Subnet | null {
      // Traverse tree to find smallest available subnet
      // that can accommodate requiredCIDR
      return this.traverse(this.root, requiredCIDR);
    }
    
    markAllocated(subnet: Subnet): void {
      // Mark node as allocated, update tree
      this.updateNode(subnet, 'allocated');
    }
    
    markAvailable(subnet: Subnet): void {
      // Mark node as available, update tree
      this.updateNode(subnet, 'available');
    }
  }
}

Features:

  1. VLSM: Variable length subnetting for efficient allocation
  2. Smallest fit: Allocate smallest subnet that fits requirements
  3. Subnet splitting: Split larger subnets when needed
  4. Subnet merging: Merge adjacent subnets when deallocated (defragmentation)
  5. Efficient search: Binary tree for O(log n) subnet search
  6. Pool health: Monitor utilization, prevent exhaustion
  7. Growth buffer: Allocate 10% extra for future growth

Key Takeaways

  • Subnetting: Divides network into smaller logical networks for efficient IP allocation
  • CIDR notation: /n specifies network prefix length (e.g., /24 = 24 network bits)
  • Subnet calculation: Number of subnets = 2^(subnet_bits - network_bits), hosts = 2^(32 - prefix) - 2
  • Network and broadcast: Network address (all host bits 0), broadcast (all host bits 1) are not usable
  • VLSM: Variable Length Subnet Masking for efficient allocation of different sized subnets
  • Subnet planning: Account for growth, reserve IPs for routers/gateways, avoid overlapping
  • Efficient allocation: Use smallest subnet that fits, merge when deallocated, monitor pool health
  • Best practices: Plan carefully, verify no overlaps, use subnet calculators, document allocations

About the author

InterviewCrafted helps you master system design with patience. We believe in curiosity-led engineering, reflective writing, and designing systems that make future changes feel calm.