summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Christopher Raaen <brian@brianraaen.com>2015-09-02 15:51:29 -0400
committerBrian Christopher Raaen <brian@brianraaen.com>2015-09-02 15:51:29 -0400
commit8b51cbe99c6f40aa51ed66f422eaba098711ba7c (patch)
tree36d4628d5cb4db18b063ab6bf7bf52c1a5ded7da
parentc7f1f4607d2c37e56937f4889c1481e245139714 (diff)
parent9f2c51a1a6231b3c8ce82f18e06ed563ab13ee9b (diff)
downloadnetaddr-8b51cbe99c6f40aa51ed66f422eaba098711ba7c.tar.gz
Merge https://github.com/drkjam/netaddr into rel-0.7.x
-rw-r--r--CHANGELOG11
-rw-r--r--Makefile3
-rw-r--r--docs/source/changes.rst2
-rw-r--r--docs/source/conf.py4
-rw-r--r--docs/source/index.rst2
-rw-r--r--netaddr/__init__.py5
-rw-r--r--netaddr/eui/__init__.py6
-rw-r--r--netaddr/ip/__init__.py46
-rwxr-xr-xnetaddr/ip/iana.py123
-rw-r--r--netaddr/ip/ipv6-unicast-address-assignments.xml446
-rw-r--r--netaddr/strategy/eui48.py3
-rw-r--r--netaddr/strategy/eui64.py167
-rw-r--r--netaddr/tests/eui/test_eui.py37
-rw-r--r--netaddr/tests/ip/test_ip_categories.py12
-rw-r--r--netaddr/tests/ip/test_ip_ranges.py2
-rw-r--r--netaddr/tests/ip/test_ip_v6.py13
-rw-r--r--netaddr/tests/test_netaddr.py11
-rw-r--r--release.py11
-rw-r--r--tutorials/2.x/ip/tutorial.txt2
19 files changed, 764 insertions, 142 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 9e3c681..8adfee3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,7 +1,7 @@
---------------
Release: 0.7.16
---------------
-Date: ?????? 2015
+Date: 30 Aug 2015
^^^^^^^^^^^^^^^^^^^^
Changes since 0.7.15
@@ -14,9 +14,18 @@ Changes since 0.7.15
Specific bug fixes addressed in this release
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+FIXED Issue 109: https://github.com/drkjam/netaddr/issues/109
+ - Identify registry of global IPv6 unicast allocations
+
FIXED Issue 108: https://github.com/drkjam/netaddr/issues/108
- One part of docs unclear?
+FIXED Issue 106: https://github.com/drkjam/netaddr/issues/106
+ - Eui64 Updated (pull request for Issue 105)
+
+FIXED Issue 105: https://github.com/drkjam/netaddr/issues/105
+ - Support dialects for EUI-64 addresses
+
FIXED Issue 102: https://github.com/drkjam/netaddr/issues/102
- 0.7.15 tarball is missing tests.
diff --git a/Makefile b/Makefile
index f32f70c..415304f 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,7 @@ download:
cd netaddr/ip/ && wget -N http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xml
cd netaddr/ip/ && wget -N http://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xml
cd netaddr/ip/ && wget -N http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml
+ cd netaddr/ip/ && wget -N http://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xml
register:
@echo 'releasing netaddr'
@@ -60,7 +61,7 @@ push_tags:
@echo 'syncing tags'
git push --tags
-test:
+test: clean
@echo 'running test suite'
python setup.py test
@echo 'running doc tests (tutorials)'
diff --git a/docs/source/changes.rst b/docs/source/changes.rst
index 075be18..cb4d8f6 100644
--- a/docs/source/changes.rst
+++ b/docs/source/changes.rst
@@ -1,5 +1,5 @@
============================
-What's new in netaddr 0.7.15
+What's new in netaddr 0.7.16
============================
.. include:: ../../CHANGELOG
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 66a4c71..83cddf8 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -48,9 +48,9 @@ copyright = u'2008-2015, David P. D. Moss. All rights reserved'
# built documents.
#
# The short X.Y version.
-version = '0.7.15'
+version = '0.7.16'
# The full version, including alpha/beta/rc tags.
-release = '0.7.15'
+release = '0.7.16'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 4b82de6..855806a 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,5 +1,5 @@
============================
-netaddr 0.7.15 documentation
+netaddr 0.7.16 documentation
============================
.. toctree::
diff --git a/netaddr/__init__.py b/netaddr/__init__.py
index 504535f..a707d97 100644
--- a/netaddr/__init__.py
+++ b/netaddr/__init__.py
@@ -6,7 +6,7 @@
"""A Python library for manipulating IP and EUI network addresses."""
#: Version info (major, minor, maintenance, status)
-VERSION = (0, 7, 15)
+VERSION = (0, 7, 16)
STATUS = ''
__version__ = '%d.%d.%d' % VERSION[0:3] + STATUS
@@ -42,6 +42,9 @@ from netaddr.strategy.ipv6 import (valid_str as valid_ipv6, ipv6_compact,
from netaddr.strategy.eui48 import (mac_eui48, mac_unix, mac_unix_expanded,
mac_cisco, mac_bare, mac_pgsql, valid_str as valid_mac)
+from netaddr.strategy.eui64 import (eui64_base, eui64_unix, eui64_unix_expanded,
+ eui64_cisco, eui64_bare, valid_str as valid_eui64)
+
__all__ = [
# Constants.
'ZEROFILL', 'Z', 'INET_PTON', 'P', 'NOHOST', 'N',
diff --git a/netaddr/eui/__init__.py b/netaddr/eui/__init__.py
index 3affc21..11a050b 100644
--- a/netaddr/eui/__init__.py
+++ b/netaddr/eui/__init__.py
@@ -11,6 +11,7 @@ identifiers.
from netaddr.core import NotRegisteredError, AddrFormatError, DictDotLookup
from netaddr.strategy import eui48 as _eui48, eui64 as _eui64
from netaddr.strategy.eui48 import mac_eui48
+from netaddr.strategy.eui64 import eui64_base
from netaddr.ip import IPAddress
from netaddr.compat import _is_int, _is_str
@@ -456,7 +457,10 @@ class EUI(BaseIdentifier):
def _set_dialect(self, value):
if value is None:
- self._dialect = mac_eui48
+ if self._module is _eui64:
+ self._dialect = eui64_base
+ else:
+ self._dialect = mac_eui48
else:
if hasattr(value, 'word_size') and hasattr(value, 'word_fmt'):
self._dialect = value
diff --git a/netaddr/ip/__init__.py b/netaddr/ip/__init__.py
index d8f001e..0141b26 100644
--- a/netaddr/ip/__init__.py
+++ b/netaddr/ip/__init__.py
@@ -1548,17 +1548,15 @@ def cidr_merge(ip_addrs):
for ip in ip_addrs:
cidr = IPNetwork(ip)
# Since non-overlapping ranges are the common case, remember the original
- ranges.append( (cidr.version, cidr.first, cidr.last, cidr) )
+ ranges.append( (cidr.version, cidr.last, cidr.first, cidr) )
ranges.sort()
- i = 1
- while i < len(ranges):
- if ranges[i][0] == ranges[i - 1][0] and ranges[i][1] - 1 <= ranges[i - 1][2]:
- ranges[i - 1] = (ranges[i][0], ranges[i - 1][1], max(ranges[i - 1][2], ranges[i][2]))
+ i = len(ranges) - 1
+ while i > 0:
+ if ranges[i][0] == ranges[i - 1][0] and ranges[i][2] - 1 <= ranges[i - 1][1]:
+ ranges[i - 1] = (ranges[i][0], ranges[i][1], min(ranges[i - 1][2], ranges[i][2]))
del ranges[i]
- else:
- i += 1
-
+ i -= 1
merged = []
for range_tuple in ranges:
# If this range wasn't merged we can simply use the old cidr.
@@ -1566,8 +1564,8 @@ def cidr_merge(ip_addrs):
merged.append(range_tuple[3])
else:
version = range_tuple[0]
- range_start = IPAddress(range_tuple[1], version=version)
- range_stop = IPAddress(range_tuple[2], version=version)
+ range_start = IPAddress(range_tuple[2], version=version)
+ range_stop = IPAddress(range_tuple[1], version=version)
merged.extend(iprange_to_cidrs(range_start, range_stop))
return merged
@@ -1850,14 +1848,15 @@ def all_matching_cidrs(ip, cidrs):
#-----------------------------------------------------------------------------
# Cached IPv4 address range lookups.
#-----------------------------------------------------------------------------
-IPV4_LOOPBACK = IPNetwork('127.0.0.0/8')
+IPV4_LOOPBACK = IPNetwork('127.0.0.0/8') # Loopback addresses (RFC 990)
IPV4_PRIVATE = (
- IPNetwork('10.0.0.0/8'), # Private-Use Networks
- IPNetwork('100.64.0.0/10'), # Shared address space
- IPNetwork('172.16.0.0/12'), # Private-Use Networks
- IPNetwork('192.0.2.0/24'), # Test-Net
- IPNetwork('192.168.0.0/16'), # Private-Use Networks
+ IPNetwork('10.0.0.0/8'), # Class A private network local communication (RFC 1918)
+ IPNetwork('100.64.0.0/10'), # Carrier grade NAT (RFC 6598)
+ IPNetwork('172.16.0.0/12'), # Private network - local communication (RFC 1918)
+ IPNetwork('192.0.0.0/24'), # IANA IPv4 Special Purpose Address Registry (RFC 5736)
+ IPNetwork('192.168.0.0/16'), # Class B private network local communication (RFC 1918)
+ IPNetwork('198.18.0.0/15'), # Testing of inter-network communications between subnets (RFC 2544)
IPRange('239.0.0.0', '239.255.255.255'), # Administrative Multicast
)
@@ -1865,19 +1864,20 @@ IPV4_LINK_LOCAL = IPNetwork('169.254.0.0/16')
IPV4_MULTICAST = IPNetwork('224.0.0.0/4')
-IPV4_6TO4 = IPNetwork('192.88.99.0/24') # 6to4 Relay Anycast
+IPV4_6TO4 = IPNetwork('192.88.99.0/24') # 6to4 anycast relays (RFC 3068)
IPV4_RESERVED = (
- IPNetwork('192.0.0.0/24'), # Reserved but subject to allocation
- IPNetwork('240.0.0.0/4'), # Reserved for Future Use
- IPNetwork('198.18.0.0/15'), # Benchmarking
- IPNetwork('198.51.100.0/24'), # Examples for documentation
- IPNetwork('203.0.113.0/24'), # Examples for documentation
+ IPNetwork('0.0.0.0/8'), # Broadcast message (RFC 1700)
+ IPNetwork('192.0.2.0/24'), # TEST-NET examples and documentation (RFC 5737)
+ IPNetwork('240.0.0.0/4'), # Reserved for multicast assignments (RFC 5771)
+ IPNetwork('198.51.100.0/24'), # TEST-NET-2 examples and documentation (RFC 5737)
+ IPNetwork('203.0.113.0/24'), # TEST-NET-3 examples and documentation (RFC 5737)
# Reserved multicast
+ IPNetwork('233.252.0.0/24'), # Multicast test network
IPRange('234.0.0.0', '238.255.255.255'),
IPRange('225.0.0.0', '231.255.255.255'),
-)
+) + (IPV4_LOOPBACK, IPV4_6TO4)
#-----------------------------------------------------------------------------
# Cached IPv6 address range lookups.
diff --git a/netaddr/ip/iana.py b/netaddr/ip/iana.py
index 7e63ac1..6338ec2 100755
--- a/netaddr/ip/iana.py
+++ b/netaddr/ip/iana.py
@@ -29,22 +29,21 @@ More details can be found at the following URLs :-
- IEEE Protocols Information Home Page - http://www.iana.org/protocols/
"""
-import os as _os
import os.path as _path
import sys as _sys
-
from xml.sax import make_parser, handler
-from netaddr.core import Publisher, Subscriber, dos2unix
+from netaddr.core import Publisher, Subscriber
from netaddr.ip import IPAddress, IPNetwork, IPRange, cidr_abbrev_to_verbose
-
from netaddr.compat import _dict_items, _callable
+
#: Topic based lookup dictionary for IANA information.
IANA_INFO = {
'IPv4': {},
'IPv6': {},
+ 'IPv6_unicast': {},
'multicast': {},
}
@@ -221,6 +220,42 @@ class IPv6Parser(XMLRecordParser):
return record
+class IPv6UnicastParser(XMLRecordParser):
+ """
+ A XMLRecordParser that understands how to parse and retrieve data records
+ from the IANA IPv6 unicast address assignments file.
+
+ It can be found online here :-
+
+ - http://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xml
+ """
+ def __init__(self, fh, **kwargs):
+ """
+ Constructor.
+
+ fh - a valid, open file handle to an IANA IPv6 address space file.
+
+ kwargs - additional parser options.
+ """
+ super(IPv6UnicastParser, self).__init__(fh)
+
+ def process_record(self, rec):
+ """
+ Callback method invoked for every record.
+
+ See base class method for more details.
+ """
+ record = {
+ 'status': str(rec.get('status', '')).strip(),
+ 'description': str(rec.get('description', '')).strip(),
+ 'prefix': str(rec.get('prefix', '')).strip(),
+ 'date': str(rec.get('date', '')).strip(),
+ 'whois': str(rec.get('whois', '')).strip(),
+ }
+
+ return record
+
+
class MulticastParser(XMLRecordParser):
"""
A XMLRecordParser that knows how to process the IANA IPv4 multicast address
@@ -305,6 +340,9 @@ class DictUpdater(Subscriber):
elif self.topic == 'IPv6':
cidr = IPNetwork(cidr_abbrev_to_verbose(data_id))
self.dct[cidr] = data
+ elif self.topic == 'IPv6_unicast':
+ cidr = IPNetwork(data_id)
+ self.dct[cidr] = data
elif self.topic == 'multicast':
iprange = None
if '-' in data_id:
@@ -334,6 +372,10 @@ def load_info():
ipv6.attach(DictUpdater(IANA_INFO['IPv6'], 'IPv6', 'prefix'))
ipv6.parse()
+ ipv6ua = IPv6UnicastParser(open(_path.join(PATH, 'ipv6-unicast-address-assignments.xml')))
+ ipv6ua.attach(DictUpdater(IANA_INFO['IPv6_unicast'], 'IPv6_unicast', 'prefix'))
+ ipv6ua.parse()
+
mcast = MulticastParser(open(_path.join(PATH, 'multicast-addresses.xml')))
mcast.attach(DictUpdater(IANA_INFO['multicast'], 'multicast', 'address'))
mcast.parse()
@@ -356,77 +398,46 @@ def pprint_info(fh=None):
fh.write('%-45r' % (iprange) + details + "\n")
-def query(ip_addr):
- """
- Returns informational data specific to this IP address.
- """
- info = {}
+def _within_bounds(ip, ip_range):
+ # Boundary checking for multiple IP classes.
+ if hasattr(ip_range, 'first'):
+ # IP network or IP range.
+ return ip in ip_range
+ elif hasattr(ip_range, 'value'):
+ # IP address.
+ return ip == ip_range
+
+ raise Exception('Unsupported IP range or address: %r!' % ip_range)
- def within_bounds(ip, ip_range):
- # Boundary checking for multiple IP classes.
- if hasattr(ip_range, 'first'):
- # IP network or IP range.
- return ip in ip_range
- elif hasattr(ip_range, 'value'):
- # IP address.
- return ip == ip_range
- raise Exception('Unsupported IP range or address: %r!' % ip_range)
+def query(ip_addr):
+ """Returns informational data specific to this IP address."""
+ info = {}
if ip_addr.version == 4:
for cidr, record in _dict_items(IANA_INFO['IPv4']):
- if within_bounds(ip_addr, cidr):
+ if _within_bounds(ip_addr, cidr):
info.setdefault('IPv4', [])
info['IPv4'].append(record)
if ip_addr.is_multicast():
for iprange, record in _dict_items(IANA_INFO['multicast']):
- if within_bounds(ip_addr, iprange):
+ if _within_bounds(ip_addr, iprange):
info.setdefault('Multicast', [])
info['Multicast'].append(record)
elif ip_addr.version == 6:
for cidr, record in _dict_items(IANA_INFO['IPv6']):
- if within_bounds(ip_addr, cidr):
+ if _within_bounds(ip_addr, cidr):
info.setdefault('IPv6', [])
info['IPv6'].append(record)
- return info
+ for cidr, record in _dict_items(IANA_INFO['IPv6_unicast']):
+ if _within_bounds(ip_addr, cidr):
+ info.setdefault('IPv6_unicast', [])
+ info['IPv6_unicast'].append(record)
-
-def get_latest_files():
- """Download the latest files from IANA"""
- if _sys.version_info[0] == 3:
- # Python 3.x
- from urllib.request import Request, urlopen
- else:
- # Python 2.x
- from urllib2 import Request, urlopen
-
- urls = [
- 'http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xml',
- 'http://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xml',
- 'http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml',
- ]
-
- for url in urls:
- _sys.stdout.write('downloading latest copy of %s\n' % url)
- request = Request(url)
- response = urlopen(request)
- save_path = _path.dirname(__file__)
- basename = _os.path.basename(response.geturl().rstrip('/'))
- filename = _path.join(save_path, basename)
- fh = open(filename, 'wb')
- fh.write(response.read())
- fh.close()
-
- # Make sure the line endings are consistent across platforms.
- dos2unix(filename)
-
-
-if __name__ == '__main__':
- # Generate indices when module is executed as a script.
- get_latest_files()
+ return info
# On module import, read IANA data files and populate lookups dict.
load_info()
diff --git a/netaddr/ip/ipv6-unicast-address-assignments.xml b/netaddr/ip/ipv6-unicast-address-assignments.xml
new file mode 100644
index 0000000..4f62e9d
--- /dev/null
+++ b/netaddr/ip/ipv6-unicast-address-assignments.xml
@@ -0,0 +1,446 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<?xml-stylesheet type="text/xsl" href="ipv6-unicast-address-assignments.xsl"?>
+<?oxygen RNGSchema="ipv6-unicast-address-assignments.rng" type="xml"?>
+<registry xmlns="http://www.iana.org/assignments" id="ipv6-unicast-address-assignments">
+ <title>IPv6 Global Unicast Address Assignments</title>
+ <category>Internet Protocol version 6 (IPv6) Global Unicast Allocations</category>
+ <updated>2015-08-10</updated>
+ <xref type="rfc" data="rfc7249"/>
+ <registration_rule>Allocations to RIRs are made in line with the Global Policy published at <xref type="uri" data="http://www.icann.org/en/resources/policy/global-addressing"/>.
+All other assignments require IETF Review.</registration_rule>
+ <description>The allocation of Internet Protocol version 6 (IPv6) unicast address space is listed
+here. References to the various other registries detailing the use of the IPv6 address
+space can be found in the <xref type="registry" data="ipv6-address-space">IPv6 Address Space registry</xref>.</description>
+ <note>The assignable Global Unicast Address space is defined in <xref type="rfc" data="rfc4291"/> as being the address
+block defined by the prefix 2000::/3. All address space in this block not listed in the
+table below is reserved by IANA for future allocation.</note>
+ <record date="1999-07-01">
+ <prefix>2001:0000::/23</prefix>
+ <description>IANA</description>
+ <whois>whois.iana.org</whois>
+ <status>ALLOCATED</status>
+ <notes>2001:0000::/23 is reserved for IETF Protocol Assignments <xref type="rfc" data="rfc2928"/>.
+2001:0000::/32 is reserved for TEREDO <xref type="rfc" data="rfc4380"/>.
+2001:0002::/48 is reserved for Benchmarking <xref type="rfc" data="rfc5180"/>.
+2001:10::/28 is reserved for ORCHID <xref type="rfc" data="rfc4843"/>.
+For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
+ </record>
+ <record date="1999-07-01">
+ <prefix>2001:0000::/23</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="1999-07-01">
+ <prefix>2001:0400::/23</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="1999-07-01">
+ <prefix>2001:0600::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2002-05-02">
+ <prefix>2001:0800::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2002-11-02">
+ <prefix>2001:0a00::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2002-05-02">
+ <prefix>2001:0c00::/23</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes>2001:db8::/32 reserved for Documentation <xref type="rfc" data="rfc3849"/>.
+For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
+ </record>
+ <record date="2003-01-01">
+ <prefix>2001:0e00::/23</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2002-11-01">
+ <prefix>2001:1200::/23</prefix>
+ <description>LACNIC</description>
+ <whois>whois.lacnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.lacnic.net/rdap/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2003-02-01">
+ <prefix>2001:1400::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2003-07-01">
+ <prefix>2001:1600::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2003-04-01">
+ <prefix>2001:1800::/23</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-01-01">
+ <prefix>2001:1a00::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-05-04">
+ <prefix>2001:1c00::/22</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-05-04">
+ <prefix>2001:2000::/20</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-05-04">
+ <prefix>2001:3000::/21</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-05-04">
+ <prefix>2001:3800::/22</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record>
+ <prefix>2001:3c00::/22</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes>2001:3c00::/22 is reserved for possible future allocation to the RIPE NCC.</notes>
+ </record>
+ <record date="2004-06-11">
+ <prefix>2001:4000::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-06-01">
+ <prefix>2001:4200::/23</prefix>
+ <description>AFRINIC</description>
+ <whois>whois.afrinic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.afrinic.net/rdap/</server>
+ <server>http://rdap.afrinic.net/rdap/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-06-11">
+ <prefix>2001:4400::/23</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-08-17">
+ <prefix>2001:4600::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-08-24">
+ <prefix>2001:4800::/23</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-10-15">
+ <prefix>2001:4a00::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-12-17">
+ <prefix>2001:4c00::/23</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-09-10">
+ <prefix>2001:5000::/20</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-11-30">
+ <prefix>2001:8000::/19</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2004-11-30">
+ <prefix>2001:a000::/20</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2006-03-08">
+ <prefix>2001:b000::/20</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2001-02-01">
+ <prefix>2002:0000::/16</prefix>
+ <description>6to4</description>
+ <whois/>
+ <status>ALLOCATED</status>
+ <notes>2002::/16 is reserved for 6to4 <xref type="rfc" data="rfc3056"/>.
+For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
+ </record>
+ <record date="2005-01-12">
+ <prefix>2003:0000::/18</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2006-10-03">
+ <prefix>2400:0000::/12</prefix>
+ <description>APNIC</description>
+ <whois>whois.apnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.apnic.net/</server>
+ </rdap>
+ <notes>2400:0000::/19 was allocated on 2005-05-20. 2400:2000::/19 was allocated on 2005-07-08. 2400:4000::/21 was
+allocated on 2005-08-08. 2404:0000::/23 was allocated on 2006-01-19. The more recent allocation (2006-10-03)
+incorporates all these previous allocations.</notes>
+ </record>
+ <record date="2006-10-03">
+ <prefix>2600:0000::/12</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes>2600:0000::/22, 2604:0000::/22, 2608:0000::/22 and 260c:0000::/22 were allocated on 2005-04-19. The more
+recent allocation (2006-10-03) incorporates all these previous allocations.</notes>
+ </record>
+ <record date="2005-11-17">
+ <prefix>2610:0000::/23</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2006-09-12">
+ <prefix>2620:0000::/23</prefix>
+ <description>ARIN</description>
+ <whois>whois.arin.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.arin.net/registry</server>
+ <server>http://rdap.arin.net/registry</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="2006-10-03">
+ <prefix>2800:0000::/12</prefix>
+ <description>LACNIC</description>
+ <whois>whois.lacnic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.lacnic.net/rdap/</server>
+ </rdap>
+ <notes>2800:0000::/23 was allocated on 2005-11-17. The more recent allocation (2006-10-03) incorporates the
+previous allocation.</notes>
+ </record>
+ <record date="2006-10-03">
+ <prefix>2a00:0000::/12</prefix>
+ <description>RIPE NCC</description>
+ <whois>whois.ripe.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.db.ripe.net/</server>
+ </rdap>
+ <notes>2a00:0000::/21 was originally allocated on 2005-04-19. 2a01:0000::/23 was allocated on 2005-07-14.
+2a01:0000::/16 (incorporating the 2a01:0000::/23) was allocated on 2005-12-15. The more recent allocation
+(2006-10-03) incorporates these previous allocations.</notes>
+ </record>
+ <record date="2006-10-03">
+ <prefix>2c00:0000::/12</prefix>
+ <description>AFRINIC</description>
+ <whois>whois.afrinic.net</whois>
+ <status>ALLOCATED</status>
+ <rdap>
+ <server>https://rdap.afrinic.net/rdap/</server>
+ <server>http://rdap.afrinic.net/rdap/</server>
+ </rdap>
+ <notes/>
+ </record>
+ <record date="1999-07-01">
+ <prefix>2d00:0000::/8</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes/>
+ </record>
+ <record date="1999-07-01">
+ <prefix>2e00:0000::/7</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes/>
+ </record>
+ <record date="1999-07-01">
+ <prefix>3000:0000::/4</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes/>
+ </record>
+ <record date="2008-04">
+ <prefix>3ffe::/16</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes>3ffe:831f::/32 was used for Teredo in some old but widely distributed networking stacks. This usage is
+deprecated in favor of 2001::/32, which was allocated for the purpose in <xref type="rfc" data="rfc4380"/>.
+3ffe::/16 and 5f00::/8 were used for the 6bone but were returned. <xref type="rfc" data="rfc5156"/></notes>
+ </record>
+ <record date="2008-04">
+ <prefix>5f00::/8</prefix>
+ <description>IANA</description>
+ <whois/>
+ <status>RESERVED</status>
+ <notes>3ffe::/16 and 5f00::/8 were used for the 6bone but were returned. <xref type="rfc" data="rfc5156"/></notes>
+ </record>
+ <people/>
+</registry>
diff --git a/netaddr/strategy/eui48.py b/netaddr/strategy/eui48.py
index c960dd6..68c8232 100644
--- a/netaddr/strategy/eui48.py
+++ b/netaddr/strategy/eui48.py
@@ -19,6 +19,7 @@ except ImportError:
AF_LINK = 48
from netaddr.core import AddrFormatError
+from netaddr.compat import _is_str
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,
@@ -26,8 +27,6 @@ from netaddr.strategy import (
valid_bin as _valid_bin, int_to_bin as _int_to_bin,
bin_to_int as _bin_to_int)
-from netaddr.compat import _is_str
-
#: The width (in bits) of this address type.
width = 48
diff --git a/netaddr/strategy/eui64.py b/netaddr/strategy/eui64.py
index 0100a18..01c98ea 100644
--- a/netaddr/strategy/eui64.py
+++ b/netaddr/strategy/eui64.py
@@ -9,11 +9,7 @@ IEEE 64-bit EUI (Extended Unique Indentifier) logic.
import struct as _struct
import re as _re
-# This is a fake constant that doesn't really exist. Here for completeness.
-AF_EUI64 = 64
-
from netaddr.core import AddrFormatError
-from netaddr.compat import _is_str
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,
@@ -21,17 +17,12 @@ from netaddr.strategy import (
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 = 64
-#: The individual word size (in bits) of this address type.
-word_size = 8
-
-#: The format string to be used when converting words to string values.
-word_fmt = '%.2X'
+# This is a fake constant that doesn't really exist. Here for completeness.
+AF_EUI64 = 64
-#: The separator character used between each word.
-word_sep = '-'
+#: The width (in bits) of this address type.
+width = 64
#: The AF_* constant value of this address type.
family = AF_EUI64
@@ -42,24 +33,88 @@ family_name = 'EUI-64'
#: The version of this address type.
version = 64
-#: 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
+#-----------------------------------------------------------------------------
+# Dialect classes.
+#-----------------------------------------------------------------------------
+
+class eui64_base(object):
+ """A standard IEEE EUI-64 dialect class."""
+ #: The individual word size (in bits) of this address type.
+ word_size = 8
+
+ #: 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
+
+ #: The separator character used between each word.
+ word_sep = '-'
+
+ #: The format string to be used when converting words to string values.
+ word_fmt = '%.2X'
-#: The maximum integer value for an individual word in this address type.
-max_word = 2 ** word_size - 1
+ #: The number base to be used when interpreting word values as integers.
+ word_base = 16
-#: Compiled regular expression for detecting value EUI-64 identifiers.
-RE_EUI64_FORMATS = [
- _re.compile('^' + ':'.join(['([0-9A-F]{1,2})'] * 8) + '$', _re.IGNORECASE),
- _re.compile('^' + '-'.join(['([0-9A-F]{1,2})'] * 8) + '$', _re.IGNORECASE),
- _re.compile('^(' + '[0-9A-F]' * 16 + ')$', _re.IGNORECASE),
-]
+
+class eui64_unix(eui64_base):
+ """A UNIX-style MAC address dialect class."""
+ word_size = 8
+ num_words = width // word_size
+ word_sep = ':'
+ word_fmt = '%x'
+ word_base = 16
+
+
+class eui64_unix_expanded(eui64_unix):
+ """A UNIX-style MAC address dialect class with leading zeroes."""
+ word_fmt = '%.2x'
+
+
+class eui64_cisco(eui64_base):
+ """A Cisco 'triple hextet' MAC address dialect class."""
+ word_size = 16
+ num_words = width // word_size
+ word_sep = '.'
+ word_fmt = '%.4x'
+ word_base = 16
+
+
+class eui64_bare(eui64_base):
+ """A bare (no delimiters) MAC address dialect class."""
+ word_size = 64
+ num_words = width // word_size
+ word_sep = ''
+ word_fmt = '%.16X'
+ word_base = 16
+
+
+#: The default dialect to be used when not specified by the user.
+
+DEFAULT_EUI64_DIALECT = eui64_base
+
+#-----------------------------------------------------------------------------
+#: Regular expressions to match all supported MAC address formats.
+RE_EUI64_FORMATS = (
+ # 2 bytes x 8 (UNIX, Windows, EUI-64)
+ '^' + ':'.join(['([0-9A-F]{1,2})'] * 8) + '$',
+ '^' + '-'.join(['([0-9A-F]{1,2})'] * 8) + '$',
+
+ # 4 bytes x 4 (Cisco like)
+ '^' + ':'.join(['([0-9A-F]{1,4})'] * 4) + '$',
+ '^' + '-'.join(['([0-9A-F]{1,4})'] * 4) + '$',
+ '^' + '\.'.join(['([0-9A-F]{1,4})'] * 4) + '$',
+
+ # 16 bytes (bare, no delimiters)
+ '^(' + ''.join(['[0-9A-F]'] * 16) + ')$',
+)
+# For efficiency, each string regexp converted in place to its compiled
+# counterpart.
+RE_EUI64_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_EUI64_FORMATS]
def _get_match_result(address, formats):
@@ -89,7 +144,7 @@ def str_to_int(addr):
:param addr: An IEEE EUI-64 indentifier in string form.
:return: An unsigned integer that is equivalent to value represented
- by EUI-64 string identifier.
+ by EUI-64 string address formatted according to the dialect
"""
words = []
@@ -100,13 +155,25 @@ def str_to_int(addr):
except TypeError:
raise AddrFormatError('invalid IEEE EUI-64 identifier: %r!' % addr)
- if _is_str(words):
- return int(words, 16)
- if len(words) != num_words:
+ if isinstance(words, tuple):
+ pass
+ else:
+ words = (words,)
+
+ if len(words) == 8:
+ # 2 bytes x 8 (UNIX, Windows, EUI-48)
+ int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
+ elif len(words) == 4:
+ # 4 bytes x 4 (Cisco like)
+ int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
+ elif len(words) == 1:
+ # 16 bytes (bare, no delimiters)
+ int_val = int('%016x' % int(words[0], 16), 16)
+ else:
raise AddrFormatError(
'bad word count for EUI-64 identifier: %r!' % addr)
- return int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
+ return int_val
def int_to_str(int_val, dialect=None):
@@ -114,13 +181,14 @@ def int_to_str(int_val, dialect=None):
:param int_val: An unsigned integer.
:param dialect: (optional) a Python class defining formatting options
- (Please Note - not currently in use).
:return: An IEEE EUI-64 identifier that is equivalent to unsigned integer.
"""
- words = int_to_words(int_val)
- tokens = [word_fmt % i for i in words]
- addr = word_sep.join(tokens)
+ if dialect is None:
+ dialect = eui64_base
+ words = int_to_words(int_val, dialect)
+ tokens = [dialect.word_fmt % i for i in words]
+ addr = dialect.word_sep.join(tokens)
return addr
@@ -155,30 +223,45 @@ def packed_to_int(packed_int):
def valid_words(words, dialect=None):
- return _valid_words(words, word_size, num_words)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _valid_words(words, dialect.word_size, dialect.num_words)
def int_to_words(int_val, dialect=None):
- return _int_to_words(int_val, word_size, num_words)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _int_to_words(int_val, dialect.word_size, dialect.num_words)
def words_to_int(words, dialect=None):
- return _words_to_int(words, word_size, num_words)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _words_to_int(words, dialect.word_size, dialect.num_words)
def valid_bits(bits, dialect=None):
- return _valid_bits(bits, width, word_sep)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _valid_bits(bits, width, dialect.word_sep)
def bits_to_int(bits, dialect=None):
- return _bits_to_int(bits, width, word_sep)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _bits_to_int(bits, width, dialect.word_sep)
def int_to_bits(int_val, dialect=None):
- return _int_to_bits(int_val, word_size, num_words, word_sep)
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
+ return _int_to_bits(
+ int_val, dialect.word_size, dialect.num_words, dialect.word_sep)
-def valid_bin(bin_val):
+def valid_bin(bin_val, dialect=None):
+ if dialect is None:
+ dialect = DEFAULT_EUI64_DIALECT
return _valid_bin(bin_val, width)
diff --git a/netaddr/tests/eui/test_eui.py b/netaddr/tests/eui/test_eui.py
index 48ef7d2..c806028 100644
--- a/netaddr/tests/eui/test_eui.py
+++ b/netaddr/tests/eui/test_eui.py
@@ -4,7 +4,9 @@ import random
import pytest
-from netaddr import EUI, mac_unix, mac_unix_expanded, mac_cisco, mac_bare, mac_pgsql, OUI, IAB, IPAddress
+from netaddr import (EUI, mac_unix, mac_unix_expanded, mac_cisco,
+ mac_bare, mac_pgsql, eui64_unix, eui64_unix_expanded,
+ eui64_cisco, eui64_bare, OUI, IAB, IPAddress)
def test_mac_address_properties():
@@ -99,6 +101,39 @@ def test_eui_custom_dialect():
assert str(mac) == '00:1B:77:49:54:FD'
+def test_eui64_dialects():
+ mac = EUI('00-1B-77-49-54-FD-12-34')
+ assert str(mac) == '00-1B-77-49-54-FD-12-34'
+
+ mac = EUI('00-1B-77-49-54-FD-12-34', dialect=eui64_unix)
+ assert str(mac) == '0:1b:77:49:54:fd:12:34'
+
+ mac = EUI('00-1B-77-49-54-FD-12-34', dialect=eui64_unix_expanded)
+ assert str(mac) == '00:1b:77:49:54:fd:12:34'
+
+ mac = EUI('00-1B-77-49-54-FD-12-34', dialect=eui64_cisco)
+ assert str(mac) == '001b.7749.54fd.1234'
+
+ mac = EUI('00-1B-77-49-54-FD-12-34', dialect=eui64_bare)
+ assert str(mac) == '001B774954FD1234'
+
+
+def test_eui64_dialect_property_assignment():
+ mac = EUI('00-1B-77-49-54-FD-12-34')
+ assert str(mac) == '00-1B-77-49-54-FD-12-34'
+
+ mac.dialect = eui64_cisco
+ assert str(mac) == '001b.7749.54fd.1234'
+
+
+def test_eui64_custom_dialect():
+ class eui64_custom(eui64_unix):
+ word_fmt = '%.2X'
+
+ mac = EUI('00-1B-77-49-54-FD-12-34', dialect=eui64_custom)
+ assert str(mac) == '00:1B:77:49:54:FD:12:34'
+
+
def test_eui_oui_information():
mac = EUI('00-1B-77-49-54-FD')
oui = mac.oui
diff --git a/netaddr/tests/ip/test_ip_categories.py b/netaddr/tests/ip/test_ip_categories.py
index 89204fb..3aa0652 100644
--- a/netaddr/tests/ip/test_ip_categories.py
+++ b/netaddr/tests/ip/test_ip_categories.py
@@ -16,10 +16,22 @@ def test_is_private():
assert IPAddress('10.0.0.1').is_private()
assert IPAddress('192.168.0.1').is_private()
assert IPAddress('fc00::1').is_private()
+ assert IPAddress('198.18.0.0').is_private()
+ assert IPAddress('198.19.255.255').is_private()
def test_is_reserved():
assert IPAddress('253.0.0.1').is_reserved()
+ assert IPAddress('192.0.2.0').is_reserved()
+ assert IPAddress('192.0.2.255').is_reserved()
+ assert IPAddress('127.0.0.0').is_reserved()
+ assert IPAddress('127.255.255.255').is_reserved()
+ assert IPAddress('192.88.99.0').is_reserved()
+ assert IPAddress('192.88.99.255').is_reserved()
+ assert IPAddress('0.0.0.0').is_reserved()
+ assert IPAddress('0.255.255.255').is_reserved()
+ assert IPAddress('233.252.0.0').is_reserved()
+ assert IPAddress('233.252.0.255').is_reserved()
def test_is_public():
diff --git a/netaddr/tests/ip/test_ip_ranges.py b/netaddr/tests/ip/test_ip_ranges.py
index b5cfd7f..cebe2be 100644
--- a/netaddr/tests/ip/test_ip_ranges.py
+++ b/netaddr/tests/ip/test_ip_ranges.py
@@ -229,7 +229,7 @@ def test_iprange_info_and_properties():
'whois': 'whois.arin.net'}]
}
- assert iprange.is_private()
+ assert iprange.is_reserved()
assert iprange.version == 4
diff --git a/netaddr/tests/ip/test_ip_v6.py b/netaddr/tests/ip/test_ip_v6.py
index 2744844..fb19f19 100644
--- a/netaddr/tests/ip/test_ip_v6.py
+++ b/netaddr/tests/ip/test_ip_v6.py
@@ -118,3 +118,16 @@ def test_ipnetwork_pickling_v6():
assert cidr2.value == 281473902969344
assert cidr2.prefixlen == 120
assert cidr2.version == 6
+
+
+def test_ipv6_unicast_address_allocation_info():
+ ip = IPNetwork('2001:1200::/23')
+
+ assert ip.info.IPv6[0].allocation == 'Global Unicast'
+ assert ip.info.IPv6[0].prefix == '2000::/3'
+ assert ip.info.IPv6[0].reference == 'rfc4291'
+
+ assert ip.info.IPv6_unicast[0].prefix == '2001:1200::/23'
+ assert ip.info.IPv6_unicast[0].description == 'LACNIC'
+ assert ip.info.IPv6_unicast[0].whois == 'whois.lacnic.net'
+ assert ip.info.IPv6_unicast[0].status == 'ALLOCATED'
diff --git a/netaddr/tests/test_netaddr.py b/netaddr/tests/test_netaddr.py
new file mode 100644
index 0000000..ee7ecd8
--- /dev/null
+++ b/netaddr/tests/test_netaddr.py
@@ -0,0 +1,11 @@
+from netaddr import valid_mac, valid_eui64
+
+
+def test_valid_mac():
+ assert valid_mac('00-B0-D0-86-BB-F7')
+ assert not valid_mac('00-1B-77-49-54-FD-12-34')
+
+
+def test_valid_eui64():
+ assert valid_eui64('00-1B-77-49-54-FD-12-34')
+ assert not valid_eui64('00-B0-D0-86-BB-F7')
diff --git a/release.py b/release.py
index 1875e82..926a7fe 100644
--- a/release.py
+++ b/release.py
@@ -10,7 +10,7 @@ name = 'netaddr'
version = netaddr.__version__
-description = 'Pythonic manipulation of IPv4, IPv6, CIDR, EUI and MAC network addresses'
+description = 'A Python library for representing and manipulating network addresses'
keywords = [
'Networking', 'Systems Administration', 'IANA', 'IEEE', 'CIDR', 'IP',
@@ -36,9 +36,7 @@ packages = [
# Required by distutils only.
package_data = {
'netaddr.ip': [
- 'ipv4-address-space.xml',
- 'ipv6-address-space.xml',
- 'multicast-addresses.xml'
+ '*.xml',
],
'netaddr.eui': [
'*.txt',
@@ -53,10 +51,7 @@ license = 'BSD License'
#------------------------------------------------------------------------
# NB - keep this text around 74 characters wide so it is viewable
# in various fixed window sizes.
-long_description = """
-A Python library for representing and manipulating network addresses.
-
-Provides support for:
+long_description = """Provides provides support for:
Layer 3 addresses
diff --git a/tutorials/2.x/ip/tutorial.txt b/tutorials/2.x/ip/tutorial.txt
index 858a445..2c577c8 100644
--- a/tutorials/2.x/ip/tutorial.txt
+++ b/tutorials/2.x/ip/tutorial.txt
@@ -61,7 +61,7 @@ Representing networks and subnets
>>> ip.ip
IPAddress('192.0.2.1')
>>> ip.network, ip.broadcast
-(IPAddress('192.0.2.1'), IPAddress('192.0.2.1'))
+(IPAddress('192.0.2.1'), None)
>>> ip.netmask, ip.hostmask
(IPAddress('255.255.255.255'), IPAddress('0.0.0.0'))
>>> ip.size