Python ipaddress: Validate and Subnet IP Addresses
Python's standard ipaddress module provides a robust,
built-in framework for inspecting, manipulating, and validating IPv4 and
IPv6 network addresses without external dependencies. This article
covers how to use the module to check whether an IP string is valid,
inspect network boundaries, perform subnet calculations, and determine
whether specific hosts belong to a given subnet.
Validating IP Addresses
Validating IP addresses manually using regular expressions can be
error-prone and complex, especially with IPv6 notation. The
ipaddress.ip_address() factory function validates both IPv4
and IPv6 addresses natively by attempting to instantiate an address
object, raising a ValueError if the format is invalid.
import ipaddress
def validate_ip(ip_str):
try:
ip_obj = ipaddress.ip_address(ip_str)
return True, ip_obj.version
except ValueError:
return False, None
# Examples
print(validate_ip("192.168.1.1")) # Output: (True, 4)
print(validate_ip("2001:db8::1")) # Output: (True, 6)
print(validate_ip("999.999.999.999")) # Output: (False, None)The returned object exposes useful properties such as
is_private, is_global,
is_multicast, and is_loopback, allowing
programmatic inspection of the address scope.
Defining and Validating Networks
To handle CIDR blocks, use the ipaddress.ip_network()
function. By default, it operates in strict mode
(strict=True), which ensures that host bits are not set in
the network definition.
# Valid network definition
net = ipaddress.ip_network("192.168.1.0/24")
# Strict mode enforcement
try:
# Fails because host bit (.5) is set for a /24 network
invalid_net = ipaddress.ip_network("192.168.1.5/24")
except ValueError as e:
print(e) # 192.168.1.5/24 has host bits set
# Supplying strict=False masks host bits automatically
normalized_net = ipaddress.ip_network("192.168.1.5/24", strict=False)
print(normalized_net) # Output: 192.168.1.0/24Extracting Network Details
An IPv4Network or IPv6Network object
exposes attributes for key routing details:
net = ipaddress.ip_network("10.0.0.0/22")
print(f"Netmask: {net.netmask}") # 255.255.252.0
print(f"Hostmask: {net.hostmask}") # 0.0.3.255
print(f"Broadcast: {net.broadcast_address}") # 10.0.3.255
print(f"Total Addresses: {net.num_addresses}") # 1024The .hosts() method yields a generator of usable host
addresses within the network, omitting the network and broadcast
addresses.
hosts = list(net.hosts())
print(f"First usable host: {hosts[0]}") # 10.0.0.1
print(f"Last usable host: {hosts[-1]}") # 10.0.3.254Subnetting and Supernetting
The module includes built-in methods to divide networks into smaller subnets or merge them into larger ones.
Dividing Networks with
subnets()
Use the .subnets() method to split a network into
smaller allocations. You can define the prefix length increase using
prefixlen_diff or define the target prefix length using
new_prefix.
parent_net = ipaddress.ip_network("172.16.0.0/16")
# Split a /16 into /18 subnets (prefixlen_diff=2)
subnets = list(parent_net.subnets(prefixlen_diff=2))
for subnet in subnets:
print(subnet)
# Output:
# 172.16.0.0/18
# 172.16.64.0/18
# 172.16.128.0/18
# 172.16.192.0/18Combining Networks with
supernet()
The .supernet() method calculates the parent network
containing the current subnet:
net = ipaddress.ip_network("192.168.1.0/24")
super_net = net.supernet(prefixlen_diff=1)
print(super_net) # Output: 192.168.0.0/23Checking Subnet Membership
The ipaddress module supports the Python in
operator to verify if an IP address falls within a network block or if
one subnet is entirely contained within another.
network = ipaddress.ip_network("192.168.0.0/16")
ip_a = ipaddress.ip_address("192.168.10.25")
ip_b = ipaddress.ip_address("10.0.0.1")
print(ip_a in network) # True
print(ip_b in network) # False
# Subnet containment
small_subnet = ipaddress.ip_network("192.168.5.0/24")
print(small_subnet.subnet_of(network)) # True