summaryrefslogtreecommitdiffstats
path: root/netaddr/strategy/ipv6.py
diff options
context:
space:
mode:
Diffstat (limited to 'netaddr/strategy/ipv6.py')
-rw-r--r--netaddr/strategy/ipv6.py259
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)