import ipaddress
import math
def reverse_ipv4 (ip) :
"""
Return in-addr.arpa reverse for given IPv4 prefix.
"""
# parse
octets = tuple(int(part) for part in ip.split('.'))
for octet in octets :
assert 0 <= octet <= 255
return '.'.join([str(octet) for octet in reversed(octets)] + ['in-addr', 'arpa'])
def reverse_ipv6 (ip6) :
"""
Return ip6.arpa reverse for given IPv6 prefix.
"""
# XXX: this is broken for fd80::1?
parts = [int(part, 16) for part in ip6.split(':')]
parts = ['{0:04x}'.format(part) for part in parts]
parts = ''.join(parts)
return '.'.join(tuple(reversed(parts)) + ( 'ip6', 'arpa'))
def reverse_label (prefix, address) :
"""
Determine the correct label for the given IP address within the reverse zone for the given prefix.
This includes all suffix octets (partially) covered by the prefix.
"""
assert prefix.version == address.version
assert address in prefix
hostbits = prefix.max_prefixlen - prefix.prefixlen
if prefix.version == 4 :
# pack into octets
octets = [ord(x) for x in address.packed]
# take the suffix
octets = octets[-int(math.ceil(hostbits / 8.0)):]
# reverse in decimal
return '.'.join(reversed(["{0:d}".format(x) for x in octets]))
elif prefix.version == 6 :
# pack into nibbles
nibbles = [((ord(x) >> 4) & 0xf, ord(x) & 0xf) for x in address.packed]
nibbles = [nibble for nibblepair in nibbles for nibble in nibblepair]
# take the suffix
nibbles = nibbles[-(hostbits / 4):]
# reveurse in hex
return '.'.join(reversed(["{0:x}".format(x) for x in nibbles]))
else :
raise ValueError("unsupported address version: %s" % (prefix, ))
def _split_ipv6_parts (prefix) :
"""
Split a partial IPv6 address into hexadecimal nibbles
"""
if prefix.endswith('::'):
prefix = prefix[:-2]
for hextet in prefix.split(':') :
for nibble in hextet.rjust(4, '0') :
yield nibble
def _build_ipv6_parts (parts) :
"""
Group an iterable of hexadecimal nibbles into hextets.
"""
for i in xrange(0, len(parts), 4) :
yield u''.join(parts[i:i+4])
# suffix ::
if len(parts) < 32 :
yield u''
yield u''
def parse_prefix (prefix) :
"""
Return an ipaddress.IPNetwork from given filesystem-compatbile IPv4/IPv6 prefix-ish variant.
Supports partial IPv4 prefixes on octet boundaries.
Supports partial IPv6 prefixes on nibble boundaries.
Supports IPv4 prefxies using "-" as the prefix separator in place of "/".
>>> print parse_prefix('127.0.0.0/8')
127.0.0.0/8
>>> print parse_prefix('192.0.2.128/26')
192.0.2.128/26
>>> print parse_prefix('192.0.2.128-26')
192.0.2.128/26
>>> print parse_prefix('127.')
127.0.0.0/8
>>> print parse_prefix('10')
10.0.0.0/8
>>> print parse_prefix('192.168')
192.168.0.0/16
>>> print parse_prefix('fe80::')
fe80::/16
>>> print parse_prefix('2001:db8::')
2001:db8::/32
>>> print parse_prefix('2001:db8:1:2')
2001:db8:1:2::/64
"""
prefix = unicode(prefix)
if '/' in prefix :
return ipaddress.ip_network(prefix)
elif '-' in prefix :
return ipaddress.ip_network(prefix.replace('-', '/'))
elif '.' in prefix or prefix.isdigit() :
parts = prefix.rstrip('.').split('.')
prefixlen = len(parts) * 8
return ipaddress.IPv4Network(u'{prefix}/{prefixlen}'.format(
prefix = u'.'.join(parts + [u'0' for i in xrange(4 - len(parts))]),
prefixlen = prefixlen,
))
elif ':' in prefix :
parts = list(_split_ipv6_parts(prefix))
prefixlen = len(parts) * 4
return ipaddress.IPv6Network(u'{prefix}/{prefixlen}'.format(
prefix = u':'.join(_build_ipv6_parts(parts)),
prefixlen = prefixlen,
))
else :
raise ValueError("Unrecognized IP prefix string: %s" % (prefix, ))