Working with IP Addresses in Python

· 4 min read · Updated March 14, 2026 · intermediate
python stdlib ipaddress network validation

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