diff options
Diffstat (limited to 'netaddr/strategy/ipv6.py')
-rw-r--r-- | netaddr/strategy/ipv6.py | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/netaddr/strategy/ipv6.py b/netaddr/strategy/ipv6.py new file mode 100644 index 0000000..de2a935 --- /dev/null +++ b/netaddr/strategy/ipv6.py @@ -0,0 +1,259 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2008 by David P. D. Moss. All rights reserved. +# +# Released under the BSD license. See the LICENSE file for details. +#----------------------------------------------------------------------------- +""" +IPv6 address logic. +""" +import struct as _struct + +OPT_IMPORTS = False + +# Check whether we need to use fallback code or not. +try: + import socket as _socket + # These might all generate exceptions on different platforms. + if not _socket.has_ipv6: + raise Exception('IPv6 disabled') + _socket.inet_pton + _socket.AF_INET6 + from _socket import (inet_pton as _inet_pton, inet_ntop as _inet_ntop, + AF_INET6) + OPT_IMPORTS = True +except Exception: + from netaddr.fbsocket import (inet_pton as _inet_pton, inet_ntop as _inet_ntop, + AF_INET6) + +from netaddr.core import AddrFormatError +from netaddr.strategy import ( + valid_words as _valid_words, int_to_words as _int_to_words, + words_to_int as _words_to_int, valid_bits as _valid_bits, + bits_to_int as _bits_to_int, int_to_bits as _int_to_bits, + valid_bin as _valid_bin, int_to_bin as _int_to_bin, + bin_to_int as _bin_to_int) + +#: The width (in bits) of this address type. +width = 128 + +#: The individual word size (in bits) of this address type. +word_size = 16 + +#: The separator character used between each word. +word_sep = ':' + +#: The AF_* constant value of this address type. +family = AF_INET6 + +#: A friendly string name address type. +family_name = 'IPv6' + +#: The version of this address type. +version = 6 + +#: The number base to be used when interpreting word values as integers. +word_base = 16 + +#: The maximum integer value that can be represented by this address type. +max_int = 2 ** width - 1 + +#: The number of words in this address type. +num_words = width // word_size + +#: The maximum integer value for an individual word in this address type. +max_word = 2 ** word_size - 1 + +#: A dictionary mapping IPv6 CIDR prefixes to the equivalent netmasks. +prefix_to_netmask = dict( + [(i, max_int ^ (2 ** (width - i) - 1)) for i in range(0, width+1)]) + +#: A dictionary mapping IPv6 netmasks to their equivalent CIDR prefixes. +netmask_to_prefix = dict( + [(max_int ^ (2 ** (width - i) - 1), i) for i in range(0, width+1)]) + +#: A dictionary mapping IPv6 CIDR prefixes to the equivalent hostmasks. +prefix_to_hostmask = dict( + [(i, (2 ** (width - i) - 1)) for i in range(0, width+1)]) + +#: A dictionary mapping IPv6 hostmasks to their equivalent CIDR prefixes. +hostmask_to_prefix = dict( + [((2 ** (width - i) - 1), i) for i in range(0, width+1)]) + +#----------------------------------------------------------------------------- +# Dialect classes. +#----------------------------------------------------------------------------- + +class ipv6_compact(object): + """An IPv6 dialect class - compact form.""" + #: The format string used to converting words into string values. + word_fmt = '%x' + + #: Boolean flag indicating if IPv6 compaction algorithm should be used. + compact = True + +class ipv6_full(ipv6_compact): + """An IPv6 dialect class - 'all zeroes' form.""" + + #: Boolean flag indicating if IPv6 compaction algorithm should be used. + compact = False + +class ipv6_verbose(ipv6_compact): + """An IPv6 dialect class - extra wide 'all zeroes' form.""" + + #: The format string used to converting words into string values. + word_fmt = '%.4x' + + #: Boolean flag indicating if IPv6 compaction algorithm should be used. + compact = False + + +def valid_str(addr, flags=0): + """ + :param addr: An IPv6 address in presentation (string) format. + + :param flags: decides which rules are applied to the interpretation of the + addr value. Future use - currently has no effect. + + :return: ``True`` if IPv6 address is valid, ``False`` otherwise. + """ + if addr == '': + raise AddrFormatError('Empty strings are not supported!') + + try: + _inet_pton(AF_INET6, addr) + except: + return False + return True + + +def str_to_int(addr, flags=0): + """ + :param addr: An IPv6 address in string form. + + :param flags: decides which rules are applied to the interpretation of the + addr value. Future use - currently has no effect. + + :return: The equivalent unsigned integer for a given IPv6 address. + """ + try: + packed_int = _inet_pton(AF_INET6, addr) + return packed_to_int(packed_int) + except Exception: + raise AddrFormatError('%r is not a valid IPv6 address string!' % (addr,)) + + +def int_to_str(int_val, dialect=None): + """ + :param int_val: An unsigned integer. + + :param dialect: (optional) a Python class defining formatting options. + + :return: The IPv6 presentation (string) format address equivalent to the + unsigned integer provided. + """ + if dialect is None: + dialect = ipv6_compact + + addr = None + + try: + packed_int = int_to_packed(int_val) + if dialect.compact: + # Default return value. + addr = _inet_ntop(AF_INET6, packed_int) + else: + # Custom return value. + words = list(_struct.unpack('>8H', packed_int)) + tokens = [dialect.word_fmt % word for word in words] + addr = word_sep.join(tokens) + except Exception: + raise ValueError('%r is not a valid 128-bit unsigned integer!' % (int_val,)) + + return addr + + +def int_to_arpa(int_val): + """ + :param int_val: An unsigned integer. + + :return: The reverse DNS lookup for an IPv6 address in network byte + order integer form. + """ + addr = int_to_str(int_val, ipv6_verbose) + tokens = list(addr.replace(':', '')) + tokens.reverse() + # We won't support ip6.int here - see RFC 3152 for details. + tokens = tokens + ['ip6', 'arpa', ''] + return '.'.join(tokens) + + +def int_to_packed(int_val): + """ + :param int_val: the integer to be packed. + + :return: a packed string that is equivalent to value represented by an + unsigned integer. + """ + words = int_to_words(int_val, 4, 32) + return _struct.pack('>4I', *words) + + +def packed_to_int(packed_int): + """ + :param packed_int: a packed string containing an unsigned integer. + It is assumed that string is packed in network byte order. + + :return: An unsigned integer equivalent to value of network address + represented by packed binary string. + """ + words = list(_struct.unpack('>4I', packed_int)) + + int_val = 0 + for i, num in enumerate(reversed(words)): + word = num + word = word << 32 * i + int_val = int_val | word + + return int_val + + +def valid_words(words): + return _valid_words(words, word_size, num_words) + + +def int_to_words(int_val, num_words=None, word_size=None): + if num_words is None: + num_words = globals()['num_words'] + if word_size is None: + word_size = globals()['word_size'] + return _int_to_words(int_val, word_size, num_words) + + +def words_to_int(words): + return _words_to_int(words, word_size, num_words) + + +def valid_bits(bits): + return _valid_bits(bits, width, word_sep) + + +def bits_to_int(bits): + return _bits_to_int(bits, width, word_sep) + + +def int_to_bits(int_val, word_sep=None): + if word_sep is None: + word_sep = globals()['word_sep'] + return _int_to_bits(int_val, word_size, num_words, word_sep) + + +def valid_bin(bin_val): + return _valid_bin(bin_val, width) + + +def int_to_bin(int_val): + return _int_to_bin(int_val, width) + + +def bin_to_int(bin_val): + return _bin_to_int(bin_val, width) |