# 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
from __future__ import unicode_literals
# Compatibility functions
_compat_int_types = (int,)
_compat_int_types = (int, long)
if b'\0'[0] == 0: # Python 3 semantics
def _compat_bytes_to_byte_vals(byt):
def _compat_bytes_to_byte_vals(byt):
return [struct.unpack(b'!B', b)[0] for b in byt]
_compat_int_from_byte_vals = int.from_bytes
def _compat_int_from_byte_vals(bytvals, endianess):
assert endianess == 'big'
assert isinstance(bv, _compat_int_types)
def _compat_to_bytes(intval, length, endianess):
assert isinstance(intval, _compat_int_types)
assert endianess == 'big'
if intval < 0 or intval >= 2 ** 32:
raise struct.error("integer out of range for 'I' format code")
return struct.pack(b'!I', intval)
if intval < 0 or intval >= 2 ** 128:
raise struct.error("integer out of range for 'QQ' format code")
return struct.pack(b'!QQ', intval >> 64, intval & 0xffffffffffffffff)
raise NotImplementedError()
if hasattr(int, 'bit_length'):
# Not int.bit_length , since that won't work in 2.7 where long exists
def _compat_bit_length(i):
def _compat_bit_length(i):
for res in itertools.count():
def _compat_range(start, end, step=1):
class _TotalOrderingMixin(object):
# Helper that derives the other comparison operations from
# We avoid functools.total_ordering because it doesn't handle
# NotImplemented correctly yet (http://bugs.python.org/issue10042)
raise NotImplementedError
equal = self.__eq__(other)
if equal is NotImplemented:
raise NotImplementedError
less = self.__lt__(other)
if less is NotImplemented or not less:
return self.__eq__(other)
less = self.__lt__(other)
if less is NotImplemented:
equal = self.__eq__(other)
if equal is NotImplemented:
return not (less or equal)
less = self.__lt__(other)
if less is NotImplemented:
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):
if isinstance(address, bytes):
'%r does not appear to be an IPv4 or IPv6 address. '
'Did you pass in a bytes (str in Python 2) instead of'
' a unicode object?' % address)
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):
if isinstance(address, bytes):
'%r does not appear to be an IPv4 or IPv6 network. '
'Did you pass in a bytes (str in Python 2) instead of'
' a unicode object?' % address)
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 _compat_to_bytes(address, 4, 'big')
except (struct.error, OverflowError):
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 _compat_to_bytes(address, 16, 'big')
except (struct.error, OverflowError):
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 = _compat_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, _compat_bit_length(~number & (number - 1)))
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),
_compat_bit_length(last_int - first_int + 1) - 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()):
# last.network_address <= net.network_address is a given.
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):