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 |