# Copyright 2007 Google Inc.
# Licensed to PSF under a Contributor Agreement.
"""A fast, lightweight IPv4/IPv6 manipulation library in Python.
This library is used to create/poke/manipulate IPv4 and IPv6 addresses
class AddressValueError(ValueError):
"""A Value Error related to the address."""
class NetmaskValueError(ValueError):
"""A Value Error related to the netmask."""
"""Take an IP string/int and return an object of the correct type.
address: A string or integer, the IP address. Either IPv4 or
IPv6 addresses may be supplied; integers less than 2**32 will
be considered to be IPv4 by default.
An IPv4Address or IPv6Address object.
ValueError: if the *address* passed isn't either a v4 or a v6
return IPv4Address(address)
except (AddressValueError, NetmaskValueError):
return IPv6Address(address)
except (AddressValueError, NetmaskValueError):
raise ValueError('%r does not appear to be an IPv4 or IPv6 address' %
def ip_network(address, strict=True):
"""Take an IP string/int and return an object of the correct type.
address: A string or integer, the IP network. Either IPv4 or
IPv6 networks may be supplied; integers less than 2**32 will
be considered to be IPv4 by default.
An IPv4Network or IPv6Network object.
ValueError: if the string passed isn't either a v4 or a v6
address. Or if the network has host bits set.
return IPv4Network(address, strict)
except (AddressValueError, NetmaskValueError):
return IPv6Network(address, strict)
except (AddressValueError, NetmaskValueError):
raise ValueError('%r does not appear to be an IPv4 or IPv6 network' %
def ip_interface(address):
"""Take an IP string/int and return an object of the correct type.
address: A string or integer, the IP address. Either IPv4 or
IPv6 addresses may be supplied; integers less than 2**32 will
be considered to be IPv4 by default.
An IPv4Interface or IPv6Interface object.
ValueError: if the string passed isn't either a v4 or a v6
The IPv?Interface classes describe an Address on a particular
Network, so they're basically a combination of both the Address
return IPv4Interface(address)
except (AddressValueError, NetmaskValueError):
return IPv6Interface(address)
except (AddressValueError, NetmaskValueError):
raise ValueError('%r does not appear to be an IPv4 or IPv6 interface' %
def v4_int_to_packed(address):
"""Represent an address as 4 packed bytes in network (big-endian) order.
address: An integer representation of an IPv4 IP address.
The integer address packed as 4 bytes in network (big-endian) order.
ValueError: If the integer is negative or too large to be an
return address.to_bytes(4, 'big')
raise ValueError("Address negative or too large for IPv4")
def v6_int_to_packed(address):
"""Represent an address as 16 packed bytes in network (big-endian) order.
address: An integer representation of an IPv6 IP address.
The integer address packed as 16 bytes in network (big-endian) order.
return address.to_bytes(16, 'big')
raise ValueError("Address negative or too large for IPv6")
def _split_optional_netmask(address):
"""Helper to split the netmask and raise AddressValueError if needed"""
addr = str(address).split('/')
raise AddressValueError("Only one '/' permitted in %r" % address)
def _find_address_range(addresses):
"""Find a sequence of sorted deduplicated IPv#Address.
addresses: a list of IPv#Address objects.
A tuple containing the first and last IP addresses in the sequence.
if ip._ip != last._ip + 1:
def _count_righthand_zero_bits(number, bits):
"""Count the number of zero bits on the right hand side.
bits: maximum number of bits to count.
The number of zero bits on the right hand side of the number.
return min(bits, (~number & (number-1)).bit_length())
def summarize_address_range(first, last):
"""Summarize a network range given the first and last IP addresses.
>>> list(summarize_address_range(IPv4Address('192.0.2.0'),
... IPv4Address('192.0.2.130')))
... #doctest: +NORMALIZE_WHITESPACE
[IPv4Network('192.0.2.0/25'), IPv4Network('192.0.2.128/31'),
IPv4Network('192.0.2.130/32')]
first: the first IPv4Address or IPv6Address in the range.
last: the last IPv4Address or IPv6Address in the range.
An iterator of the summarized IPv(4|6) network objects.
If the first and last objects are not IP addresses.
If the first and last objects are not the same version.
If the last object is not greater than the first.
If the version of the first address is not 4 or 6.
if (not (isinstance(first, _BaseAddress) and
isinstance(last, _BaseAddress))):
raise TypeError('first and last must be IP addresses, not networks')
if first.version != last.version:
raise TypeError("%s and %s are not of the same version" % (
raise ValueError('last IP address must be greater than first')
raise ValueError('unknown IP version')
ip_bits = first._max_prefixlen
while first_int <= last_int:
nbits = min(_count_righthand_zero_bits(first_int, ip_bits),
(last_int - first_int + 1).bit_length() - 1)
net = ip((first_int, ip_bits - nbits))
if first_int - 1 == ip._ALL_ONES:
def _collapse_addresses_internal(addresses):
"""Loops through the addresses, collapsing concurrent netblocks.
ip1 = IPv4Network('192.0.2.0/26')
ip2 = IPv4Network('192.0.2.64/26')
ip3 = IPv4Network('192.0.2.128/26')
ip4 = IPv4Network('192.0.2.192/26')
_collapse_addresses_internal([ip1, ip2, ip3, ip4]) ->
[IPv4Network('192.0.2.0/24')]
This shouldn't be called directly; it is called via
addresses: A list of IPv4Network's or IPv6Network's
A list of IPv4Network's or IPv6Network's depending on what we were
to_merge = list(addresses)
supernet = net.supernet()
existing = subnets.get(supernet)
# Merge consecutive subnets
to_merge.append(supernet)
# Then iterate over resulting networks, skipping subsumed subnets
for net in sorted(subnets.values()):
# Since they are sorted, last.network_address <= net.network_address
if last.broadcast_address >= net.broadcast_address:
def collapse_addresses(addresses):
"""Collapse a list of IP objects.
collapse_addresses([IPv4Network('192.0.2.0/25'),
IPv4Network('192.0.2.128/25')]) ->
[IPv4Network('192.0.2.0/24')]
addresses: An iterator of IPv4Network or IPv6Network objects.
An iterator of the collapsed IPv(4|6)Network objects.
TypeError: If passed a list of mixed version objects.
# split IP addresses and networks
if isinstance(ip, _BaseAddress):
if ips and ips[-1]._version != ip._version:
raise TypeError("%s and %s are not of the same version" % (
elif ip._prefixlen == ip._max_prefixlen:
if ips and ips[-1]._version != ip._version:
raise TypeError("%s and %s are not of the same version" % (
ips.append(ip.network_address)
if nets and nets[-1]._version != ip._version:
raise TypeError("%s and %s are not of the same version" % (
# find consecutive address ranges in the sorted sequence and summarize them
for first, last in _find_address_range(ips):
addrs.extend(summarize_address_range(first, last))
return _collapse_addresses_internal(addrs + nets)
def get_mixed_type_key(obj):
"""Return a key suitable for sorting between networks and addresses.
Address and Network objects are not sortable by default; they're
fundamentally different so the expression
IPv4Address('192.0.2.0') <= IPv4Network('192.0.2.0/24')
doesn't make any sense. There are some times however, where you may wish
to have ipaddress sort these for you anyway. If you need to do this, you
can use this function as the key= argument to sorted().
obj: either a Network or Address object.
if isinstance(obj, _BaseNetwork):
return obj._get_networks_key()
elif isinstance(obj, _BaseAddress):
return obj._get_address_key()
"""Return the longhand version of the IP address as a string."""
return self._explode_shorthand_ip_string()
"""Return the shorthand version of the IP address as a string."""
def reverse_pointer(self):
"""The name of the reverse DNS pointer for the IP address, e.g.:
>>> ipaddress.ip_address("127.0.0.1").reverse_pointer
>>> ipaddress.ip_address("2001:db8::1").reverse_pointer
'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'
return self._reverse_pointer()
msg = '%200s has no version specified' % (type(self),)
raise NotImplementedError(msg)
def _check_int_address(self, address):
msg = "%d (< 0) is not permitted as an IPv%d address"
raise AddressValueError(msg % (address, self._version))
if address > self._ALL_ONES:
msg = "%d (>= 2**%d) is not permitted as an IPv%d address"
raise AddressValueError(msg % (address, self._max_prefixlen,
def _check_packed_address(self, address, expected_len):
address_len = len(address)
if address_len != expected_len:
msg = "%r (len %d != %d) is not permitted as an IPv%d address"
raise AddressValueError(msg % (address, address_len,
expected_len, self._version))
def _ip_int_from_prefix(cls, prefixlen):
"""Turn the prefix length into a bitwise netmask
prefixlen: An integer, the prefix length.
return cls._ALL_ONES ^ (cls._ALL_ONES >> prefixlen)
def _prefix_from_ip_int(cls, ip_int):
"""Return prefix length from the bitwise netmask.
ip_int: An integer, the netmask in expanded bitwise format
An integer, the prefix length.
ValueError: If the input intermingles zeroes & ones
trailing_zeroes = _count_righthand_zero_bits(ip_int,
prefixlen = cls._max_prefixlen - trailing_zeroes
leading_ones = ip_int >> trailing_zeroes
all_ones = (1 << prefixlen) - 1
if leading_ones != all_ones:
byteslen = cls._max_prefixlen // 8
details = ip_int.to_bytes(byteslen, 'big')
msg = 'Netmask pattern %r mixes zeroes & ones'
raise ValueError(msg % details)
def _report_invalid_netmask(cls, netmask_str):
msg = '%r is not a valid netmask' % netmask_str
raise NetmaskValueError(msg) from None
def _prefix_from_prefix_string(cls, prefixlen_str):
"""Return prefix length from a numeric string
prefixlen_str: The string to be converted
An integer, the prefix length.
NetmaskValueError: If the input is not a valid netmask
# int allows a leading +/- as well as surrounding whitespace,
# so we ensure that isn't the case
if not (prefixlen_str.isascii() and prefixlen_str.isdigit()):
cls._report_invalid_netmask(prefixlen_str)
prefixlen = int(prefixlen_str)
cls._report_invalid_netmask(prefixlen_str)
if not (0 <= prefixlen <= cls._max_prefixlen):
cls._report_invalid_netmask(prefixlen_str)