Working with IP Addresses in Python
When you need to work with IP addresses, whether validating user input, parsing network configurations, or building networking tools, the ipaddress module is your go-to solution. It provides a clean, object-oriented way to handle both IPv4 and IPv6 addresses.
Why Use the ipaddress Module?
Before Python 3.3, developers had to parse IP addresses manually using string manipulation or regex. This was error-prone and tedious. The ipaddress module solves this by providing automatic validation of IP address format, easy network range calculations, intuitive comparison operators, and support for both IPv4 and IPv6.
Creating IP Addresses
The module provides separate classes for IPv4 and IPv6:
import ipaddress
# Create IPv4 addresses
ipv4 = ipaddress.IPv4Address("192.168.1.1")
print(ipv4) # 192.168.1.1
# Create IPv6 addresses
ipv6 = ipaddress.IPv6Address("2001:db8::1")
print(ipv6) # 2001:db8::1
# From integers
ipv4_int = ipaddress.IPv4Address(3232235777)
print(ipv4_int) # 192.168.1.1
# IPv6 from integer
ipv6_int = ipaddress.IPv6Address(1)
print(ipv6_int) # ::1
Validating IP Addresses
A common use case is validating user input:
import ipaddress
def is_valid_ipv4(address):
"""Check if a string is a valid IPv4 address."""
try:
ipaddress.IPv4Address(address)
return True
except ipaddress.AddressValueError:
return False
# Test the function
print(is_valid_ipv4("192.168.1.1")) # True
print(is_valid_ipv4("256.1.1.1")) # False
print(is_valid_ipv4("192.168.1")) # False
print(is_valid_ipv4("not.an.ip.address")) # False
You can also check if an address is private, public, or reserved:
import ipaddress
# Check address types
private_ip = ipaddress.IPv4Address("192.168.1.1")
public_ip = ipaddress.IPv4Address("8.8.8.8")
loopback = ipaddress.IPv4Address("127.0.0.1")
print(private_ip.is_private) # True
print(public_ip.is_private) # False
print(loopback.is_loopback) # True
# IPv6 examples
link_local = ipaddress.IPv6Address("fe80::1")
unique_local = ipaddress.IPv6Address("fc00::1")
print(link_local.is_link_local) # True
print(unique_local.is_unique_local) # True
Working with Networks
The ipaddress module makes network operations intuitive:
import ipaddress
# Create a network
network = ipaddress.IPv4Network("192.168.1.0/24")
print(network) # 192.168.1.0/24
# Get network information
print(network.network_address) # 192.168.1.0
print(network.broadcast_address) # 192.168.1.255
print(network.num_addresses) # 256
print(network.prefix_len) # 24
# Iterate over all hosts
for host in network.hosts():
print(host)
# 192.168.1.1, 192.168.1.2, ..., 192.168.1.254
Checking IP Address Membership
You can easily check if an IP belongs to a network:
import ipaddress
network = ipaddress.IPv4Network("10.0.0.0/8")
# Check if an address is in the network
ip1 = ipaddress.IPv4Address("10.1.2.3")
ip2 = ipaddress.IPv4Address("172.16.0.1")
print(ip1 in network) # True
print(ip2 in network) # False
This is perfect for implementing access control or IP allowlists:
import ipaddress
ALLOWED_NETWORKS = [
ipaddress.IPv4Network("192.168.1.0/24"),
ipaddress.IPv4Network("10.0.0.0/8"),
]
def is_allowed(ip_address):
"""Check if an IP is in any allowed network."""
addr = ipaddress.IPv4Address(ip_address)
return any(addr in network for network in ALLOWED_NETWORKS)
print(is_allowed("192.168.1.50")) # True
print(is_allowed("172.16.5.5")) # True
print(is_allowed("8.8.8.8")) # False
Network Calculations
Find the supernet or subnets of a network:
import ipaddress
network = ipaddress.IPv4Network("192.168.1.0/24")
# Supernet (larger network that contains this one)
print(network.supernet()) # 192.168.0.0/23
# Subnets (smaller networks that fit inside)
subnets = list(network.subnets(prefixlen_diff=1))
print(subnets) # [IPv4Network('192.168.1.0/25'), IPv4Network('192.168.1.128/25')]
# Check subnet relationships
small_network = ipaddress.IPv4Network("192.168.1.0/25")
print(small_network.subnet_of(network)) # True
print(network.supernet_of(small_network)) # True
IP Address Arithmetic
You can perform arithmetic on IP addresses:
import ipaddress
ip = ipaddress.IPv4Address("192.168.1.1")
# Increment
next_ip = ip + 1
print(next_ip) # 192.168.1.2
# Decrement
prev_ip = ip - 1
print(prev_ip) # 192.168.1.0
# Difference
ip1 = ipaddress.IPv4Address("192.168.2.100")
ip2 = ipaddress.IPv4Address("192.168.2.1")
print(int(ip1) - int(ip2)) # 99
Interface Addresses
For representing an IP address with its network prefix:
import ipaddress
# Create an interface (address + prefix length)
interface = ipaddress.IPv4Interface("192.168.1.100/24")
print(interface) # 192.168.1.100/24
print(interface.ip) # 192.168.1.100
print(interface.network) # 192.168.1.0/24
Converting Between Formats
import ipaddress
# String to integer and back
ip_str = "192.168.1.1"
ip = ipaddress.IPv4Address(ip_str)
print(int(ip)) # 3232235777
print(ipaddress.IPv4Address(3232235777)) # 192.168.1.1
# IPv6 to integer
ipv6 = ipaddress.IPv6Address("2001:db8::1")
print(int(ipv6)) # 42540766411282592856903984951653826561
# To packed bytes (big-endian)
ip = ipaddress.IPv4Address("192.168.1.1")
print(ip.packed) # b'\xc0\xa8\x01\x01'
Practical Examples
Parsing DHCP Configuration
import ipaddress
def parse_dhcp_range(start, end):
"""Parse DHCP range and return list of usable IPs."""
start_ip = ipaddress.IPv4Address(start)
end_ip = ipaddress.IPv4Address(end)
usable_ips = []
current = start_ip + 1 # Typically skip network address
while current < end_ip:
usable_ips.append(str(current))
current += 1
return usable_ips
# Example: 192.168.1.10 to 192.168.1.20
ips = parse_dhcp_range("192.168.1.10", "192.168.1.20")
print(ips) # ['192.168.1.11', '192.168.1.12', ...]
CIDR to Range Conversion
import ipaddress
def cidr_to_range(cidr):
"""Convert CIDR notation to start and end IP."""
network = ipaddress.IPv4Network(cidr, strict=False)
return str(network.network_address), str(network.broadcast_address)
start, end = cidr_to_range("192.168.1.0/24")
print(f"Range: {start} - {end}") # Range: 192.168.1.0 - 192.168.1.255
IP Range to CIDR
import ipaddress
def range_to_cidr(start_ip, end_ip):
"""Convert IP range to list of CIDR blocks."""
start = ipaddress.IPv4Address(start_ip)
end = ipaddress.IPv4Address(end_ip)
if start > end:
start, end = end, start
cidrs = []
current = start
while current <= end:
# Find the largest prefix that fits
max_prefix = 32
while max_prefix > 0:
network = ipaddress.IPv4Network(f"{current}/{max_prefix}", strict=False)
if network.broadcast_address <= end:
cidrs.append(str(network))
current = network.broadcast_address + 1
break
max_prefix -= 1
return cidrs
cidrs = range_to_cidr("192.168.1.0", "192.168.1.255")
print(cidrs) # ['192.168.1.0/24']
See Also
- ipaddress-module — The reference documentation for the ipaddress module
- socket-module — Low-level networking with sockets
- os-module — Network-related OS utilities