Python ipaddress Subnet Overlaps and Host Ranges
Python’s standard library includes the ipaddress module,
which provides native, robust classes for parsing, analyzing, and
manipulating IPv4 and IPv6 network definitions. This article outlines
the specific methods and attributes available in
IPv4Network and IPv6Network to determine if
CIDR blocks intersect, evaluate hierarchical containment, and extract
usable host ranges and boundary addresses.
Detecting Subnet Overlaps and Containment
The ipaddress module provides built-in methods to
determine whether two CIDR blocks collide or nest within one
another.
Checking for Overlaps with
overlaps()
The network.overlaps(other) method accepts another
network object and returns a boolean indicating whether the two subnets
share any IP addresses. This checks for partial, total, or mutual
overlap.
import ipaddress
net1 = ipaddress.ip_network("192.168.1.0/24")
net2 = ipaddress.ip_network("192.168.1.128/25")
net3 = ipaddress.ip_network("192.168.2.0/24")
print(net1.overlaps(net2)) # True
print(net1.overlaps(net3)) # FalseChecking
Subnet Hierarchy with subnet_of() and
supernet_of()
To determine direct containment rather than general intersection, use
subnet_of() and supernet_of():
net_a.subnet_of(net_b): ReturnsTrueifnet_ais entirely contained withinnet_b.net_a.supernet_of(net_b): ReturnsTrueifnet_afully encompassesnet_b.
parent = ipaddress.ip_network("10.0.0.0/16")
child = ipaddress.ip_network("10.0.5.0/24")
print(child.subnet_of(parent)) # True
print(parent.supernet_of(child)) # TrueYou can also use Python's in operator to test if an
individual IP address or a smaller network falls within a target
network:
ip = ipaddress.ip_address("10.0.5.15")
print(ip in parent) # TrueComputing Host Ranges and Boundary Addresses
When working with CIDR blocks, determining the usable host addresses and the theoretical boundaries requires different properties depending on your use case.
Boundary Properties
Network objects provide direct access to edge boundaries through property attributes:
network_address: Returns the first IP address of the CIDR block (the network ID).broadcast_address: Returns the last IP address of the block (the broadcast IP for IPv4).num_addresses: Returns the total count of addresses in the block, including network and broadcast IDs.- **
netmask/hostmask: Returns the subnet mask or inverse wildcard mask.
net = ipaddress.ip_network("172.16.0.0/22")
print(net.network_address) # 172.16.0.0
print(net.broadcast_address) # 172.16.3.255
print(net.num_addresses) # 1024Iterating Usable Hosts with
hosts()
The network.hosts() method returns an iterator over
usable IP addresses. By default, for IPv4 subnets with a prefix length
shorter than /31, it automatically excludes the network
address and the broadcast address:
net = ipaddress.ip_network("192.168.10.0/29")
# Iterate through usable host IPs
for host in net.hosts():
print(host)
# Outputs: 192.168.10.1 through 192.168.10.6(Note: For /31 point-to-point links defined under
RFC 3021 and /32 single-host blocks, hosts()
returns all addresses in the block rather than excluding the
boundaries.)
Calculating First and Last Usable Hosts Directly
For large subnets, iterating through network.hosts() to
find the first and last usable IP can be slow and memory-intensive.
Because network objects support indexing, boundaries can be retrieved
directly in constant time:
net = ipaddress.ip_network("10.50.0.0/16")
if net.prefixlen <= 30:
first_usable = net[1]
last_usable = net[-2]
else:
first_usable = net[0]
last_usable = net[-1]
print(f"First Host: {first_usable}") # 10.50.0.1
print(f"Last Host: {last_usable}") # 10.50.255.254