CrackedRuby CrackedRuby

IP Addressing and Subnetting

Overview

IP addressing forms the foundation of network communication, providing unique identifiers for devices on networks. An IP address consists of a network portion identifying the subnet and a host portion identifying the specific device. Subnetting divides larger networks into smaller segments, improving network management, security isolation, and address space efficiency.

IPv4 addresses use 32 bits represented as four octets in dotted decimal notation (192.168.1.100). IPv6 addresses use 128 bits represented as eight groups of four hexadecimal digits (2001:0db8:85a3:0000:0000:8a2e:0370:7334). The subnet mask determines which bits represent the network and which represent hosts.

require 'ipaddr'

# Creating IPv4 address
ip = IPAddr.new("192.168.1.100/24")
puts ip.to_s          # => "192.168.1.100"
puts ip.prefix        # => 24
puts ip.netmask       # => "255.255.255.0"

Network administrators use subnetting to create logical network segments within larger address spaces. A Class C network (192.168.1.0/24) contains 256 addresses, but subnetting divides this into smaller ranges for different departments, VLANs, or security zones. Each subnet requires its own network and broadcast address, reducing usable host addresses.

Key Principles

IP addresses operate at Layer 3 of the OSI model, providing logical addressing independent of physical network topology. The address structure consists of two components: network bits (prefix) and host bits (suffix). The boundary between these components determines the subnet size and the number of available host addresses.

Subnet masks use consecutive 1 bits for the network portion and consecutive 0 bits for the host portion. A mask of 255.255.255.0 (binary 11111111.11111111.11111111.00000000) indicates the first three octets identify the network and the last octet identifies hosts. CIDR notation expresses this as /24, representing 24 network bits.

Binary operations determine subnet membership. The bitwise AND operation between an IP address and subnet mask produces the network address. Two addresses belong to the same subnet when their network addresses match.

require 'ipaddr'

# Network address calculation
ip1 = IPAddr.new("192.168.1.100")
ip2 = IPAddr.new("192.168.1.200")
mask = IPAddr.new("255.255.255.0")

network1 = ip1 & mask
network2 = ip2 & mask

puts network1 == network2  # => true (same subnet)

Subnetting follows specific mathematical rules. For a given prefix length, the number of subnets equals 2^borrowed_bits, where borrowed_bits represents bits taken from the host portion. The number of hosts per subnet equals 2^host_bits - 2, subtracting two addresses reserved for network and broadcast.

Address classes historically divided the IPv4 space, though classless routing (CIDR) replaced this system. Class A networks (0.0.0.0 to 127.255.255.255) used /8 prefixes. Class B networks (128.0.0.0 to 191.255.255.255) used /16 prefixes. Class C networks (192.0.0.0 to 223.255.255.255) used /24 prefixes. Modern networks use variable-length subnet masks (VLSM) for efficient address allocation.

Private address ranges (RFC 1918) provide non-routable addresses for internal networks: 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16. The loopback range 127.0.0.0/8 routes packets to the local host. Link-local addresses 169.254.0.0/16 enable automatic addressing when DHCP fails.

Broadcast addresses send packets to all hosts in a subnet. The broadcast address contains all 1 bits in the host portion. For network 192.168.1.0/24, the broadcast address is 192.168.1.255. Directed broadcasts target specific subnets, while limited broadcasts (255.255.255.255) remain within the local network.

Ruby Implementation

Ruby's standard library includes the IPAddr class for IP address manipulation. This class handles both IPv4 and IPv6 addresses, supporting network calculations, range operations, and address validation. The class stores addresses internally as integers, enabling efficient bitwise operations.

require 'ipaddr'

# Creating and inspecting IP addresses
ipv4 = IPAddr.new("192.168.1.0/24")
puts ipv4.to_s              # => "192.168.1.0"
puts ipv4.to_string         # => "192.168.1.0/24"
puts ipv4.prefix            # => 24
puts ipv4.netmask.to_s      # => "255.255.255.0"
puts ipv4.ipv4?             # => true
puts ipv4.ipv6?             # => false

# IPv6 addresses
ipv6 = IPAddr.new("2001:db8::1/64")
puts ipv6.prefix            # => 64
puts ipv6.ipv6?             # => true

The IPAddr class supports subnet membership testing through the include? method. This method checks whether an address falls within a network range, handling both individual addresses and subnet comparisons.

require 'ipaddr'

network = IPAddr.new("10.0.0.0/8")
subnet = IPAddr.new("10.20.30.0/24")
host = IPAddr.new("10.20.30.100")

puts network.include?(host)      # => true
puts network.include?(subnet)    # => true
puts subnet.include?(host)       # => true

outside = IPAddr.new("192.168.1.1")
puts network.include?(outside)   # => false

Subnet calculations require converting between CIDR notation and traditional subnet masks. The IPAddr class provides methods for both representations, along with network address extraction and address range enumeration.

require 'ipaddr'

# Network calculations
subnet = IPAddr.new("192.168.1.64/26")

# First usable host address
first_host = subnet.to_range.first.succ
puts first_host.to_s        # => "192.168.1.65"

# Last usable host address
broadcast = subnet.to_range.last
last_host = broadcast.to_i - 1
puts IPAddr.new(last_host, Socket::AF_INET).to_s  # => "192.168.1.126"

# Total addresses in subnet
range = subnet.to_range
total_addresses = range.last.to_i - range.first.to_i + 1
puts total_addresses        # => 64

# Usable host addresses (excluding network and broadcast)
usable_hosts = total_addresses - 2
puts usable_hosts          # => 62

Ruby's IPAddr handles address masking through bitwise operations. The & operator performs AND operations, the | operator performs OR operations, and the ~ operator performs NOT operations. These operations enable custom subnet calculations and address manipulation.

require 'ipaddr'

# Bitwise operations
ip = IPAddr.new("192.168.1.130")
mask = IPAddr.new("255.255.255.192")

# Calculate network address
network = ip & mask
puts network.to_s           # => "192.168.1.128"

# Calculate broadcast address
host_mask = ~mask
broadcast = network | host_mask
puts broadcast.to_s         # => "192.168.1.191"

# Check if address is network address
puts ip == network          # => false

# Check if address is broadcast address
puts ip == broadcast        # => false

The Socket library provides low-level network operations complementing IPAddr. It includes constants for address families and methods for address conversion.

require 'socket'
require 'ipaddr'

# Address family detection
ip = IPAddr.new("192.168.1.1")
puts ip.family == Socket::AF_INET   # => true

ipv6 = IPAddr.new("::1")
puts ipv6.family == Socket::AF_INET6  # => true

# Converting between formats
packed = ip.hton
unpacked = IPAddr.ntop(packed)
puts unpacked               # => "192.168.1.1"

IPAddr supports supernet operations for aggregating multiple networks into larger blocks. This functionality supports route summarization and address space management.

require 'ipaddr'

# Finding common supernet
net1 = IPAddr.new("192.168.0.0/24")
net2 = IPAddr.new("192.168.1.0/24")

# Calculate supernet that contains both networks
supernet = net1.to_range.first & IPAddr.new("255.255.254.0")
puts IPAddr.new(supernet.to_i, Socket::AF_INET).to_s  # => "192.168.0.0"

Practical Examples

A common scenario involves dividing a Class C network into four equal subnets for different departments. Starting with 192.168.10.0/24, borrowing two bits from the host portion creates four /26 subnets, each containing 62 usable host addresses.

require 'ipaddr'

# Divide 192.168.10.0/24 into 4 subnets
base_network = "192.168.10.0"
original_prefix = 24
new_prefix = 26  # Borrowed 2 bits

subnets = []
(0..3).each do |i|
  subnet_base = IPAddr.new(base_network).to_i + (i * 2**(32 - new_prefix))
  subnet = IPAddr.new(subnet_base, Socket::AF_INET)
  subnet_str = "#{subnet}/#{new_prefix}"
  
  subnet_obj = IPAddr.new(subnet_str)
  range = subnet_obj.to_range
  
  subnets << {
    subnet: subnet_str,
    network: range.first.to_s,
    first_host: range.first.succ.to_s,
    last_host: IPAddr.new(range.last.to_i - 1, Socket::AF_INET).to_s,
    broadcast: range.last.to_s,
    hosts: 2**(32 - new_prefix) - 2
  }
end

subnets.each_with_index do |subnet, i|
  puts "Subnet #{i + 1}: #{subnet[:subnet]}"
  puts "  Network: #{subnet[:network]}"
  puts "  First Host: #{subnet[:first_host]}"
  puts "  Last Host: #{subnet[:last_host]}"
  puts "  Broadcast: #{subnet[:broadcast]}"
  puts "  Usable Hosts: #{subnet[:hosts]}"
  puts
end

VLSM (Variable Length Subnet Masking) allocates different subnet sizes based on requirements. A network with 100 hosts in one department and 50 hosts in another requires different prefix lengths for efficient address usage.

require 'ipaddr'

# VLSM allocation from 172.16.0.0/16
base = IPAddr.new("172.16.0.0")
allocations = []

# Department A needs 100 hosts (requires /25 = 126 hosts)
dept_a_size = 25
dept_a = IPAddr.new("#{base}/#{dept_a_size}")
allocations << {
  name: "Department A",
  network: "#{base}/#{dept_a_size}",
  hosts: 2**(32 - dept_a_size) - 2
}

# Department B needs 50 hosts (requires /26 = 62 hosts)
# Start after Department A's range
next_addr = dept_a.to_range.last.succ
dept_b_size = 26
dept_b = IPAddr.new("#{next_addr}/#{dept_b_size}")
allocations << {
  name: "Department B",
  network: "#{next_addr}/#{dept_b_size}",
  hosts: 2**(32 - dept_b_size) - 2
}

# Department C needs 10 hosts (requires /28 = 14 hosts)
next_addr = dept_b.to_range.last.succ
dept_c_size = 28
dept_c = IPAddr.new("#{next_addr}/#{dept_c_size}")
allocations << {
  name: "Department C",
  network: "#{next_addr}/#{dept_c_size}",
  hosts: 2**(32 - dept_c_size) - 2
}

allocations.each do |alloc|
  puts "#{alloc[:name]}: #{alloc[:network]} (#{alloc[:hosts]} usable hosts)"
end

Point-to-point links between routers require only two host addresses, making /30 subnets (2 usable hosts) optimal for conserving address space.

require 'ipaddr'

# Point-to-point link subnetting
def create_p2p_subnets(base_network, count)
  base = IPAddr.new(base_network)
  prefix = 30  # /30 provides 2 usable addresses
  block_size = 2**(32 - prefix)
  
  links = []
  count.times do |i|
    network_int = base.to_i + (i * block_size)
    network = IPAddr.new(network_int, Socket::AF_INET)
    subnet = IPAddr.new("#{network}/#{prefix}")
    range = subnet.to_range
    
    links << {
      link_id: i + 1,
      network: "#{network}/#{prefix}",
      router_a: range.first.succ.to_s,
      router_b: IPAddr.new(range.last.to_i - 1, Socket::AF_INET).to_s,
      broadcast: range.last.to_s
    }
  end
  
  links
end

# Create 5 point-to-point links from 10.1.1.0/24
links = create_p2p_subnets("10.1.1.0/24", 5)

links.each do |link|
  puts "Link #{link[:link_id]}: #{link[:network]}"
  puts "  Router A: #{link[:router_a]}"
  puts "  Router B: #{link[:router_b]}"
  puts
end

Supernetting combines multiple networks into a larger aggregate, reducing routing table entries. Four consecutive /24 networks summarize into a single /22 route.

require 'ipaddr'

# Route summarization
networks = [
  "192.168.0.0/24",
  "192.168.1.0/24",
  "192.168.2.0/24",
  "192.168.3.0/24"
]

def find_supernet(networks)
  # Convert all networks to integer ranges
  ranges = networks.map do |net|
    subnet = IPAddr.new(net)
    [subnet.to_range.first.to_i, subnet.to_range.last.to_i]
  end
  
  # Find common prefix length
  first_addr = ranges.first.first
  last_addr = ranges.last.last
  
  # Calculate prefix length that covers the range
  prefix = 32
  while prefix > 0
    mask_int = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF
    network = first_addr & mask_int
    broadcast = network | ~mask_int & 0xFFFFFFFF
    
    break if broadcast >= last_addr
    prefix -= 1
  end
  
  network_addr = IPAddr.new(first_addr & ((0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF), Socket::AF_INET)
  "#{network_addr}/#{prefix}"
end

summary = find_supernet(networks)
puts "Networks:"
networks.each { |net| puts "  #{net}" }
puts "\nSummary route: #{summary}"

# Verify summary contains all networks
summary_subnet = IPAddr.new(summary)
networks.each do |net|
  subnet = IPAddr.new(net)
  puts "#{net} included: #{summary_subnet.include?(subnet)}"
end

Tools & Ecosystem

The ipaddress gem extends Ruby's IP address capabilities with additional features for subnet manipulation, address validation, and network calculations. This gem provides a more object-oriented interface than the standard library.

# Using ipaddress gem (requires: gem install ipaddress)
require 'ipaddress'

# Create and inspect addresses
ip = IPAddress("192.168.1.100/24")
puts ip.address         # => "192.168.1.100"
puts ip.prefix          # => 24
puts ip.netmask         # => "255.255.255.0"
puts ip.network         # => "192.168.1.0"
puts ip.broadcast       # => "192.168.1.255"

# Iterate through addresses
ip.each_host do |host|
  puts host  # Prints each usable host address
  break if host.to_s == "192.168.1.110"  # Limit output
end

# Subnet splitting
subnets = ip.split(4)  # Divide into 4 equal subnets
subnets.each { |subnet| puts subnet.to_string }

The resolv library in Ruby's standard library provides DNS resolution capabilities, translating hostnames to IP addresses and performing reverse lookups.

require 'resolv'

# DNS resolution
resolver = Resolv::DNS.new

# Forward lookup
addresses = resolver.getaddresses("www.example.com")
addresses.each { |addr| puts addr }

# Reverse lookup
begin
  name = resolver.getname("8.8.8.8")
  puts name
rescue Resolv::ResolvError
  puts "No PTR record found"
end

Network scanning and discovery operations use IPAddr for address generation and iteration. Building an address scanner requires iterating through subnet ranges and checking host availability.

require 'ipaddr'
require 'socket'
require 'timeout'

def scan_subnet(subnet_str, port, timeout_sec = 1)
  subnet = IPAddr.new(subnet_str)
  active_hosts = []
  
  range = subnet.to_range
  current = range.first.succ  # Skip network address
  broadcast = range.last
  
  while current < broadcast
    begin
      Timeout.timeout(timeout_sec) do
        TCPSocket.new(current.to_s, port).close
        active_hosts << current.to_s
      end
    rescue Errno::ECONNREFUSED
      active_hosts << current.to_s  # Port closed but host up
    rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH, Timeout::Error
      # Host unreachable or timeout
    end
    
    current = current.succ
    break if active_hosts.length >= 5  # Limit for example
  end
  
  active_hosts
end

# Scan subset of network for SSH (port 22)
hosts = scan_subnet("192.168.1.0/28", 22, 0.5)
puts "Active hosts found: #{hosts.length}"

IP geolocation services map addresses to geographic locations. Ruby applications integrate with geolocation APIs using address validation and normalization.

require 'ipaddr'
require 'json'

# IP address validation for geolocation
def validate_and_normalize(address_str)
  begin
    ip = IPAddr.new(address_str)
    
    # Reject private addresses
    private_ranges = [
      IPAddr.new("10.0.0.0/8"),
      IPAddr.new("172.16.0.0/12"),
      IPAddr.new("192.168.0.0/16"),
      IPAddr.new("127.0.0.0/8")
    ]
    
    if private_ranges.any? { |range| range.include?(ip) }
      return { valid: false, reason: "Private address" }
    end
    
    { valid: true, normalized: ip.to_s, family: ip.ipv4? ? "IPv4" : "IPv6" }
  rescue IPAddr::InvalidAddressError
    { valid: false, reason: "Invalid address format" }
  end
end

test_addresses = ["192.168.1.1", "8.8.8.8", "2001:4860:4860::8888", "invalid"]
test_addresses.each do |addr|
  result = validate_and_normalize(addr)
  puts "#{addr}: #{result}"
end

Common Pitfalls

Subnet size calculations frequently produce off-by-one errors when developers forget to subtract network and broadcast addresses. A /24 subnet contains 256 addresses, but only 254 are assignable to hosts.

require 'ipaddr'

# Incorrect: Counting all addresses
subnet = IPAddr.new("192.168.1.0/24")
range = subnet.to_range
wrong_count = range.last.to_i - range.first.to_i + 1
puts "Total addresses: #{wrong_count}"  # => 256

# Correct: Excluding network and broadcast
usable_hosts = wrong_count - 2
puts "Usable host addresses: #{usable_hosts}"  # => 254

# Verification
first_host = range.first.succ
last_host = IPAddr.new(range.last.to_i - 1, Socket::AF_INET)
actual_usable = last_host.to_i - first_host.to_i + 1
puts "Verified usable hosts: #{actual_usable}"  # => 254

Wildcard masks (inverse masks) used in Cisco ACLs confuse developers familiar with subnet masks. A wildcard mask uses 0 bits to match exactly and 1 bits to ignore, opposite of subnet masks.

require 'ipaddr'

# Converting subnet mask to wildcard mask
def subnet_to_wildcard(netmask_str)
  netmask = IPAddr.new(netmask_str)
  wildcard_int = netmask.to_i ^ 0xFFFFFFFF
  IPAddr.new(wildcard_int, Socket::AF_INET)
end

# Examples
netmask = "255.255.255.0"
wildcard = subnet_to_wildcard(netmask)
puts "Subnet mask: #{netmask}"
puts "Wildcard mask: #{wildcard}"  # => "0.0.0.255"

netmask2 = "255.255.252.0"
wildcard2 = subnet_to_wildcard(netmask2)
puts "\nSubnet mask: #{netmask2}"
puts "Wildcard mask: #{wildcard2}"  # => "0.0.3.255"

CIDR notation parsing errors occur when handling addresses without explicit prefix lengths. IPAddr requires explicit prefix specification or assumes a host address (/32 for IPv4).

require 'ipaddr'

# Ambiguous address parsing
addr_without_prefix = "192.168.1.100"
ip1 = IPAddr.new(addr_without_prefix)
puts ip1.to_string  # => "192.168.1.100" (defaults to /32)
puts ip1.prefix     # => 32

# Explicit prefix required for subnets
network = IPAddr.new("192.168.1.0/24")
puts network.to_string  # => "192.168.1.0/24"
puts network.prefix     # => 24

# Checking if address is host or network
def is_network_address?(ip_str)
  ip = IPAddr.new(ip_str)
  # Network address if prefix < 32 (IPv4) or < 128 (IPv6)
  if ip.ipv4?
    ip.prefix < 32
  else
    ip.prefix < 128
  end
end

puts is_network_address?("192.168.1.0/24")   # => true
puts is_network_address?("192.168.1.100")    # => false

Overlapping subnet assignments cause routing conflicts and unreachable hosts. Subnet allocations must not overlap within the same network space.

require 'ipaddr'

# Detect overlapping subnets
def subnets_overlap?(subnet1_str, subnet2_str)
  net1 = IPAddr.new(subnet1_str)
  net2 = IPAddr.new(subnet2_str)
  
  # Check if either subnet contains the other
  net1.include?(net2) || net2.include?(net1)
end

# Test cases
puts subnets_overlap?("192.168.1.0/24", "192.168.1.128/25")  # => true (overlap)
puts subnets_overlap?("192.168.1.0/25", "192.168.1.128/25")  # => false (adjacent)
puts subnets_overlap?("10.0.0.0/8", "10.1.1.0/24")           # => true (contains)

# Validate subnet allocation
def validate_allocation(subnets)
  errors = []
  
  subnets.each_with_index do |subnet1, i|
    subnets[(i+1)..-1].each_with_index do |subnet2, j|
      if subnets_overlap?(subnet1, subnet2)
        errors << "Overlap: #{subnet1} and #{subnet2}"
      end
    end
  end
  
  errors
end

allocations = ["10.0.0.0/24", "10.0.1.0/24", "10.0.0.128/25"]
issues = validate_allocation(allocations)
if issues.empty?
  puts "All subnets valid"
else
  issues.each { |issue| puts issue }
end

Broadcast address calculations fail when using simple arithmetic instead of bitwise operations. The broadcast address requires setting all host bits to 1, not adding the maximum host value.

require 'ipaddr'

# Incorrect broadcast calculation (arithmetic)
network = IPAddr.new("192.168.1.0/25")
range = network.to_range
wrong_broadcast = IPAddr.new(range.first.to_i + 127, Socket::AF_INET)
puts "Wrong: #{wrong_broadcast}"  # => "192.168.1.127"

# Correct broadcast calculation (bitwise)
correct_broadcast = range.last
puts "Correct: #{correct_broadcast}"  # => "192.168.1.127"

# They match here, but consider a different network
network2 = IPAddr.new("10.0.1.0/25")
range2 = network2.to_range
wrong2 = IPAddr.new(range2.first.to_i + 127, Socket::AF_INET)
correct2 = range2.last

puts "\nNetwork: 10.0.1.0/25"
puts "Arithmetic approach: #{wrong2}"
puts "Correct broadcast: #{correct2}"
puts "Match: #{wrong2 == correct2}"  # => true for this case

# Different prefix where arithmetic fails
network3 = IPAddr.new("172.16.0.0/22")
range3 = network3.to_range
hosts = 2**(32 - 22) - 2
wrong3 = IPAddr.new(range3.first.to_i + hosts, Socket::AF_INET)
correct3 = range3.last

puts "\nNetwork: 172.16.0.0/22"
puts "Arithmetic approach: #{wrong3}"  # => "172.16.3.254"
puts "Correct broadcast: #{correct3}"   # => "172.16.3.255"
puts "Match: #{wrong3 == correct3}"    # => false

Reference

CIDR Prefix to Subnet Mask Conversion

CIDR Subnet Mask Wildcard Mask Hosts
/8 255.0.0.0 0.255.255.255 16,777,214
/16 255.255.0.0 0.0.255.255 65,534
/17 255.255.128.0 0.0.127.255 32,766
/18 255.255.192.0 0.0.63.255 16,382
/19 255.255.224.0 0.0.31.255 8,190
/20 255.255.240.0 0.0.15.255 4,094
/21 255.255.248.0 0.0.7.255 2,046
/22 255.255.252.0 0.0.3.255 1,022
/23 255.255.254.0 0.0.1.255 510
/24 255.255.255.0 0.0.0.255 254
/25 255.255.255.128 0.0.0.127 126
/26 255.255.255.192 0.0.0.63 62
/27 255.255.255.224 0.0.0.31 30
/28 255.255.255.240 0.0.0.15 14
/29 255.255.255.248 0.0.0.7 6
/30 255.255.255.252 0.0.0.3 2
/31 255.255.255.254 0.0.0.1 2 (RFC 3021)
/32 255.255.255.255 0.0.0.0 1 (host)

IPv4 Private Address Ranges

Range CIDR Addresses Common Use
10.0.0.0 - 10.255.255.255 10.0.0.0/8 16,777,216 Large private networks
172.16.0.0 - 172.31.255.255 172.16.0.0/12 1,048,576 Medium private networks
192.168.0.0 - 192.168.255.255 192.168.0.0/16 65,536 Small private networks
127.0.0.0 - 127.255.255.255 127.0.0.0/8 16,777,216 Loopback addresses
169.254.0.0 - 169.254.255.255 169.254.0.0/16 65,536 Link-local (APIPA)

IPAddr Class Methods

Method Description Example
new(addr) Create IP address object IPAddr.new("192.168.1.1/24")
include?(addr) Check if address in range network.include?(host)
to_s Address as string ip.to_s returns "192.168.1.1"
to_string Address with prefix ip.to_string returns "192.168.1.1/24"
to_range Address range object subnet.to_range
netmask Subnet mask address ip.netmask.to_s
prefix Prefix length ip.prefix returns 24
ipv4? Check if IPv4 ip.ipv4? returns true/false
ipv6? Check if IPv6 ip.ipv6? returns true/false
mask(prefix) Apply subnet mask ip.mask(24)
& Bitwise AND operation ip & mask
pipe Bitwise OR operation ip pipe mask
~ Bitwise NOT operation ~mask
succ Next address ip.succ
hton Convert to network bytes ip.hton

Subnetting Formulas

Calculation Formula Example
Number of subnets 2^borrowed_bits /24 to /26 = 2^2 = 4 subnets
Hosts per subnet 2^host_bits - 2 /26 = 2^6 - 2 = 62 hosts
Subnet block size 2^host_bits /26 = 2^6 = 64 addresses
Network address IP AND subnet_mask Binary AND operation
Broadcast address Network OR inverse_mask Binary OR with wildcard
First host Network address + 1 First usable address
Last host Broadcast address - 1 Last usable address

Common Subnet Sizes

Requirement CIDR Hosts Block Size Use Case
2 hosts /30 2 4 Point-to-point links
6 hosts /29 6 8 Small office subnet
14 hosts /28 14 16 Printer/server subnet
30 hosts /27 30 32 Department subnet
62 hosts /26 62 64 Floor subnet
126 hosts /25 126 128 Building subnet
254 hosts /24 254 256 Standard subnet
510 hosts /23 510 512 Large department

Binary Subnet Mask Reference

CIDR Binary Representation Decimal
/24 11111111.11111111.11111111.00000000 255.255.255.0
/25 11111111.11111111.11111111.10000000 255.255.255.128
/26 11111111.11111111.11111111.11000000 255.255.255.192
/27 11111111.11111111.11111111.11100000 255.255.255.224
/28 11111111.11111111.11111111.11110000 255.255.255.240
/29 11111111.11111111.11111111.11111000 255.255.255.248
/30 11111111.11111111.11111111.11111100 255.255.255.252