fix vendor
This commit is contained in:
parent
731ddbc133
commit
a373c65a73
@ -3,7 +3,7 @@
|
|||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/vendor/lib/python2.7/site-packages" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/vendor" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
<excludeFolder url="file://$MODULE_DIR$/vendor" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
|
2
main.py
2
main.py
@ -9,7 +9,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
parent_dir = os.path.abspath(os.path.dirname(__file__))
|
parent_dir = os.path.abspath(os.path.dirname(__file__))
|
||||||
vendor_dir = os.path.join(parent_dir, 'vendor/lib/python2.7/site-packages')
|
vendor_dir = os.path.join(parent_dir, 'vendor')
|
||||||
sys.path.append(vendor_dir)
|
sys.path.append(vendor_dir)
|
||||||
|
|
||||||
from netaddr import IPNetwork
|
from netaddr import IPNetwork
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
- David P. D. Moss (author, maintainer) drkjam@gmail.com
|
|
||||||
- Stefan Nordhausen (maintainer) stefan.nordhausen@immobilienscout24.de
|
|
||||||
- Jakub Stasiak (maintainer) jakub@stasiak.at
|
|
||||||
|
|
||||||
Released under the BSD License (see :doc:`license` for details).
|
|
@ -1 +0,0 @@
|
|||||||
pip
|
|
@ -1,36 +0,0 @@
|
|||||||
Here are the licenses applicable to the use of the netaddr library.
|
|
||||||
|
|
||||||
-------
|
|
||||||
netaddr
|
|
||||||
-------
|
|
||||||
|
|
||||||
COPYRIGHT AND LICENSE
|
|
||||||
|
|
||||||
Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of David P. D. Moss nor the names of contributors
|
|
||||||
may be used to endorse or promote products derived from this
|
|
||||||
software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,118 +0,0 @@
|
|||||||
Metadata-Version: 2.1
|
|
||||||
Name: netaddr
|
|
||||||
Version: 0.8.0
|
|
||||||
Summary: A network address manipulation library for Python
|
|
||||||
Home-page: https://github.com/drkjam/netaddr/
|
|
||||||
Author: David P. D. Moss, Stefan Nordhausen et al
|
|
||||||
Author-email: drkjam@gmail.com
|
|
||||||
License: BSD License
|
|
||||||
Download-URL: https://pypi.org/project/netaddr/
|
|
||||||
Keywords: Networking,Systems Administration,IANA,IEEE,CIDR,IP,IPv4,IPv6,CIDR,EUI,MAC,MAC-48,EUI-48,EUI-64
|
|
||||||
Platform: OS Independent
|
|
||||||
Classifier: Development Status :: 5 - Production/Stable
|
|
||||||
Classifier: Environment :: Console
|
|
||||||
Classifier: Intended Audience :: Developers
|
|
||||||
Classifier: Intended Audience :: Education
|
|
||||||
Classifier: Intended Audience :: Information Technology
|
|
||||||
Classifier: Intended Audience :: Science/Research
|
|
||||||
Classifier: Intended Audience :: System Administrators
|
|
||||||
Classifier: Intended Audience :: Telecommunications Industry
|
|
||||||
Classifier: License :: OSI Approved :: BSD License
|
|
||||||
Classifier: License :: OSI Approved :: MIT License
|
|
||||||
Classifier: Natural Language :: English
|
|
||||||
Classifier: Operating System :: OS Independent
|
|
||||||
Classifier: Programming Language :: Python
|
|
||||||
Classifier: Programming Language :: Python :: 2
|
|
||||||
Classifier: Programming Language :: Python :: 2.7
|
|
||||||
Classifier: Programming Language :: Python :: 3
|
|
||||||
Classifier: Programming Language :: Python :: 3.5
|
|
||||||
Classifier: Programming Language :: Python :: 3.6
|
|
||||||
Classifier: Programming Language :: Python :: 3.7
|
|
||||||
Classifier: Programming Language :: Python :: 3.8
|
|
||||||
Classifier: Topic :: Communications
|
|
||||||
Classifier: Topic :: Documentation
|
|
||||||
Classifier: Topic :: Education
|
|
||||||
Classifier: Topic :: Education :: Testing
|
|
||||||
Classifier: Topic :: Home Automation
|
|
||||||
Classifier: Topic :: Internet
|
|
||||||
Classifier: Topic :: Internet :: Log Analysis
|
|
||||||
Classifier: Topic :: Internet :: Name Service (DNS)
|
|
||||||
Classifier: Topic :: Internet :: Proxy Servers
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
||||||
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
||||||
Classifier: Topic :: Security
|
|
||||||
Classifier: Topic :: Software Development
|
|
||||||
Classifier: Topic :: Software Development :: Libraries
|
|
||||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
||||||
Classifier: Topic :: Software Development :: Quality Assurance
|
|
||||||
Classifier: Topic :: Software Development :: Testing
|
|
||||||
Classifier: Topic :: Software Development :: Testing :: Traffic Generation
|
|
||||||
Classifier: Topic :: System :: Benchmark
|
|
||||||
Classifier: Topic :: System :: Clustering
|
|
||||||
Classifier: Topic :: System :: Distributed Computing
|
|
||||||
Classifier: Topic :: System :: Installation/Setup
|
|
||||||
Classifier: Topic :: System :: Logging
|
|
||||||
Classifier: Topic :: System :: Monitoring
|
|
||||||
Classifier: Topic :: System :: Networking
|
|
||||||
Classifier: Topic :: System :: Networking :: Firewalls
|
|
||||||
Classifier: Topic :: System :: Networking :: Monitoring
|
|
||||||
Classifier: Topic :: System :: Networking :: Time Synchronization
|
|
||||||
Classifier: Topic :: System :: Recovery Tools
|
|
||||||
Classifier: Topic :: System :: Shells
|
|
||||||
Classifier: Topic :: System :: Software Distribution
|
|
||||||
Classifier: Topic :: System :: Systems Administration
|
|
||||||
Classifier: Topic :: System :: System Shells
|
|
||||||
Classifier: Topic :: Text Processing
|
|
||||||
Classifier: Topic :: Text Processing :: Filters
|
|
||||||
Classifier: Topic :: Utilities
|
|
||||||
Requires-Dist: importlib-resources ; python_version < "3.7"
|
|
||||||
|
|
||||||
netaddr
|
|
||||||
=======
|
|
||||||
|
|
||||||
A system-independent network address manipulation library for Python 2.7 and 3.5+.
|
|
||||||
(Python 2.7 and 3.5 support is deprecated).
|
|
||||||
|
|
||||||
.. image:: https://codecov.io/gh/netaddr/netaddr/branch/master/graph/badge.svg
|
|
||||||
:target: https://codecov.io/gh/netaddr/netaddr
|
|
||||||
.. image:: https://github.com/netaddr/netaddr/workflows/CI/badge.svg
|
|
||||||
:target: https://github.com/netaddr/netaddr/actions?query=workflow%3ACI+branch%3Amaster
|
|
||||||
.. image:: https://img.shields.io/pypi/v/netaddr.svg
|
|
||||||
:target: https://pypi.org/project/netaddr/
|
|
||||||
.. image:: https://img.shields.io/pypi/pyversions/netaddr.svg
|
|
||||||
:target: pypi.python.org/pypi/netaddr
|
|
||||||
|
|
||||||
Provides support for:
|
|
||||||
|
|
||||||
Layer 3 addresses
|
|
||||||
|
|
||||||
- IPv4 and IPv6 addresses, subnets, masks, prefixes
|
|
||||||
- iterating, slicing, sorting, summarizing and classifying IP networks
|
|
||||||
- dealing with various ranges formats (CIDR, arbitrary ranges and
|
|
||||||
globs, nmap)
|
|
||||||
- set based operations (unions, intersections etc) over IP addresses
|
|
||||||
and subnets
|
|
||||||
- parsing a large variety of different formats and notations
|
|
||||||
- looking up IANA IP block information
|
|
||||||
- generating DNS reverse lookups
|
|
||||||
- supernetting and subnetting
|
|
||||||
|
|
||||||
Layer 2 addresses
|
|
||||||
|
|
||||||
- representation and manipulation MAC addresses and EUI-64 identifiers
|
|
||||||
- looking up IEEE organisational information (OUI, IAB)
|
|
||||||
- generating derived IPv6 addresses
|
|
||||||
|
|
||||||
Starting with Python 3.3 there's an `ipaddress <https://docs.python.org/3/library/ipaddress.html>`_
|
|
||||||
module in the Python standard library which provides layer 3 address manipulation
|
|
||||||
capabilities overlapping ``netaddr``.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Latest documentation https://netaddr.readthedocs.io/en/latest/
|
|
||||||
|
|
||||||
Share and enjoy!
|
|
||||||
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
|||||||
../../../bin/netaddr,sha256=Vur2dSZN9Xcjeo6hltTm7xF_0_kCv-sihPegDrXZm5c,258
|
|
||||||
netaddr-0.8.0.dist-info/AUTHORS,sha256=ukJIe5KKm4I3UsXdPRTflQj-wISd8--amGiTe8PAVco,241
|
|
||||||
netaddr-0.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
|
||||||
netaddr-0.8.0.dist-info/LICENSE,sha256=DlPeYlR3h0YvQe77XO4xoU9-p2e6A2LG-TBPF0JIbUc,1606
|
|
||||||
netaddr-0.8.0.dist-info/METADATA,sha256=0kO1sE_BkLIoTmbDur34u3t45VoiqdBIG9LqX7fV168,4884
|
|
||||||
netaddr-0.8.0.dist-info/RECORD,,
|
|
||||||
netaddr-0.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
||||||
netaddr-0.8.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
|
|
||||||
netaddr-0.8.0.dist-info/entry_points.txt,sha256=TGfqa95bQEnE9BUe3rrr0Hq1Y1rw49ZVkVDjKg7nE90,46
|
|
||||||
netaddr-0.8.0.dist-info/top_level.txt,sha256=GlouRmUZSQCg0kloRWKY6qxTHAsQo_rE8QvlxYBHwOE,8
|
|
||||||
netaddr/__init__.py,sha256=de86OhloTul8BJpkkwh1yn4iK-tEVRRGm_kXIfNXcE8,1849
|
|
||||||
netaddr/__init__.pyc,,
|
|
||||||
netaddr/cli.py,sha256=_r5UPGR2hAZhyD4d_KbmHQDBz_eRoWFlqFJ3xtcLxAc,1278
|
|
||||||
netaddr/cli.pyc,,
|
|
||||||
netaddr/compat.py,sha256=o4F9ROM_oAz4tIj8qeGB6viWBaXVxyOEtTPy6J4svA0,2209
|
|
||||||
netaddr/compat.pyc,,
|
|
||||||
netaddr/contrib/__init__.py,sha256=tHDAWK5T0l3IxMjlnlXRZmBVEWe_JnYu6GmdSeP8slQ,567
|
|
||||||
netaddr/contrib/__init__.pyc,,
|
|
||||||
netaddr/contrib/subnet_splitter.py,sha256=FloAxzvcMMXXHaesGJiyJWug1R8Wrxh-00Cvv6UfDAY,1769
|
|
||||||
netaddr/contrib/subnet_splitter.pyc,,
|
|
||||||
netaddr/core.py,sha256=AhQhwutql11wSUWN5_ip6cM9gHjP_zr3c7I3BIrCwIw,5898
|
|
||||||
netaddr/core.pyc,,
|
|
||||||
netaddr/eui/__init__.py,sha256=tET2m6vht-x8WUStqv7wOfVEPtPfYPZBppcAm8-5np8,25688
|
|
||||||
netaddr/eui/__init__.pyc,,
|
|
||||||
netaddr/eui/iab.idx,sha256=sQ-fBZjeCGVbYT4jYwPMzMsktJ70b8kQJhr58NVpK1g,95464
|
|
||||||
netaddr/eui/iab.txt,sha256=gXX96cz8zxlCvWd7SNwRiwmf6OLsbnvOVcDoNGNUMLQ,2453223
|
|
||||||
netaddr/eui/ieee.py,sha256=f0SxL3s6jxXysovoCw948vFzW_qDY8Q8M7p0v1FXZ6U,9303
|
|
||||||
netaddr/eui/ieee.pyc,,
|
|
||||||
netaddr/eui/oui.idx,sha256=hE0dZwt-G72WK569n7AphTpwyUNlsPdsRJ90r8SUPNI,523486
|
|
||||||
netaddr/eui/oui.txt,sha256=w6qHufJSUn1ZQEx2TcqW11gP39McK5c6NTumiFIYK0E,4458411
|
|
||||||
netaddr/fbsocket.py,sha256=L2yZtJVBhAR-Hxfx5pROfJiE2V9XkjLtYf6ONElUJnE,8247
|
|
||||||
netaddr/fbsocket.pyc,,
|
|
||||||
netaddr/ip/__init__.py,sha256=rzSNhw4H_ElQSD8-Gmm3bqbMqxGB4NdV2WijqksEs1Y,68492
|
|
||||||
netaddr/ip/__init__.pyc,,
|
|
||||||
netaddr/ip/glob.py,sha256=tlxePyqAA_JrNGj2A8_9vRgzbMIxJXScs7A0Sx1xNhI,10474
|
|
||||||
netaddr/ip/glob.pyc,,
|
|
||||||
netaddr/ip/iana.py,sha256=Cdzo4XuKq6cKJB6JxrM9SevKz9afMHrLOseEkdFQghw,14052
|
|
||||||
netaddr/ip/iana.pyc,,
|
|
||||||
netaddr/ip/ipv4-address-space.xml,sha256=QkOuS1T5_UYzzCehV-c715ukZkipxSersFJnAZXwd0E,75998
|
|
||||||
netaddr/ip/ipv6-address-space.xml,sha256=U14bpxkCfEc5JCOogbw33uLCVbzZd49WmgXxD89Atxk,9581
|
|
||||||
netaddr/ip/ipv6-unicast-address-assignments.xml,sha256=NTc9d0XDdSIxzmeoB37mfNY46r2Mig6aOCJh0k8Ydiw,14852
|
|
||||||
netaddr/ip/multicast-addresses.xml,sha256=uV2J89XT2rfvb9WopNUkETY5MlsB5a8Ct9mZOKCY1qI,153025
|
|
||||||
netaddr/ip/nmap.py,sha256=6OEyTSrxUUNvEZdhX0I27c9aV7GPKf1oJMT50toS_ro,4078
|
|
||||||
netaddr/ip/nmap.pyc,,
|
|
||||||
netaddr/ip/rfc1924.py,sha256=GCLZKg6VUhBuqZlN6JHPP-M0VaaEVysoyhx4AeZTNUM,1750
|
|
||||||
netaddr/ip/rfc1924.pyc,,
|
|
||||||
netaddr/ip/sets.py,sha256=SG_aO9KJ13Td3YPqsFvlmRDceMwblDWcSTER_wly54Q,26586
|
|
||||||
netaddr/ip/sets.pyc,,
|
|
||||||
netaddr/strategy/__init__.py,sha256=QI-vJBK4wCJGp-c6iQSkbRDE4wqy1YW44ioFa2pCJ3U,7479
|
|
||||||
netaddr/strategy/__init__.pyc,,
|
|
||||||
netaddr/strategy/eui48.py,sha256=w-ClUG8ie34GsVfNY8vHVXiTWU303v_RPjP76s_HynE,8640
|
|
||||||
netaddr/strategy/eui48.pyc,,
|
|
||||||
netaddr/strategy/eui64.py,sha256=nipcYqN5XYWu7L8gEkbeVdfqBU5CJ592uz3yFSIlD_k,7711
|
|
||||||
netaddr/strategy/eui64.pyc,,
|
|
||||||
netaddr/strategy/ipv4.py,sha256=T-dK0926vh-FWso2M7GLNTliKSteLzbZfEH37tjupHc,8095
|
|
||||||
netaddr/strategy/ipv4.pyc,,
|
|
||||||
netaddr/strategy/ipv6.py,sha256=OuwhcXbtReuI6KYtO6Nsc9kdEjThVqmY4XmvnTKBunU,7632
|
|
||||||
netaddr/strategy/ipv6.pyc,,
|
|
@ -1,6 +0,0 @@
|
|||||||
Wheel-Version: 1.0
|
|
||||||
Generator: bdist_wheel (0.34.2)
|
|
||||||
Root-Is-Purelib: true
|
|
||||||
Tag: py2-none-any
|
|
||||||
Tag: py3-none-any
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
[console_scripts]
|
|
||||||
netaddr = netaddr.cli:main
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
netaddr
|
|
@ -1,48 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""A Python library for manipulating IP and EUI network addresses."""
|
|
||||||
|
|
||||||
#: Version info (major, minor, maintenance, status)
|
|
||||||
__version__ = '0.8.0'
|
|
||||||
VERSION = tuple(int(part) for part in __version__.split('.'))
|
|
||||||
STATUS = ''
|
|
||||||
|
|
||||||
import sys as _sys
|
|
||||||
|
|
||||||
if _sys.version_info[0:2] < (2, 4):
|
|
||||||
raise RuntimeError('Python 2.4.x or higher is required!')
|
|
||||||
|
|
||||||
from netaddr.core import (AddrConversionError, AddrFormatError,
|
|
||||||
NotRegisteredError, ZEROFILL, Z, INET_PTON, P, NOHOST, N)
|
|
||||||
|
|
||||||
from netaddr.ip import (IPAddress, IPNetwork, IPRange, all_matching_cidrs,
|
|
||||||
cidr_abbrev_to_verbose, cidr_exclude, cidr_merge, iprange_to_cidrs,
|
|
||||||
iter_iprange, iter_unique_ips, largest_matching_cidr,
|
|
||||||
smallest_matching_cidr, spanning_cidr)
|
|
||||||
|
|
||||||
from netaddr.ip.sets import IPSet
|
|
||||||
|
|
||||||
from netaddr.ip.glob import (IPGlob, cidr_to_glob, glob_to_cidrs,
|
|
||||||
glob_to_iprange, glob_to_iptuple, iprange_to_globs, valid_glob)
|
|
||||||
|
|
||||||
from netaddr.ip.nmap import valid_nmap_range, iter_nmap_range
|
|
||||||
|
|
||||||
from netaddr.ip.rfc1924 import base85_to_ipv6, ipv6_to_base85
|
|
||||||
|
|
||||||
from netaddr.eui import EUI, IAB, OUI
|
|
||||||
|
|
||||||
from netaddr.strategy.ipv4 import valid_str as valid_ipv4
|
|
||||||
|
|
||||||
from netaddr.strategy.ipv6 import (valid_str as valid_ipv6, ipv6_compact,
|
|
||||||
ipv6_full, ipv6_verbose)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
from netaddr.contrib.subnet_splitter import SubnetSplitter
|
|
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""an interactive shell for the netaddr library"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import netaddr
|
|
||||||
from netaddr import *
|
|
||||||
|
|
||||||
# aliases to save some typing ...
|
|
||||||
from netaddr import IPAddress as IP, IPNetwork as CIDR
|
|
||||||
from netaddr import EUI as MAC
|
|
||||||
|
|
||||||
def main():
|
|
||||||
argv = sys.argv[1:]
|
|
||||||
|
|
||||||
banner = "\nnetaddr shell %s - %s\n" % (netaddr.__version__, __doc__)
|
|
||||||
exit_msg = "\nShare and enjoy!"
|
|
||||||
rc_override = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
try:
|
|
||||||
# ipython >= 0.11
|
|
||||||
from IPython.terminal.embed import InteractiveShellEmbed
|
|
||||||
ipshell = InteractiveShellEmbed(banner1=banner, exit_msg=exit_msg)
|
|
||||||
except ImportError:
|
|
||||||
# ipython < 0.11
|
|
||||||
from IPython.Shell import IPShellEmbed
|
|
||||||
ipshell = IPShellEmbed(argv, banner, exit_msg, rc_override)
|
|
||||||
except ImportError:
|
|
||||||
sys.stderr.write('IPython (http://ipython.scipy.org/) not found!\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
ipshell()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,93 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Compatibility wrappers providing uniform behaviour for Python code required to
|
|
||||||
run under both Python 2.x and 3.x.
|
|
||||||
|
|
||||||
All operations emulate 2.x behaviour where applicable.
|
|
||||||
"""
|
|
||||||
import sys as _sys
|
|
||||||
|
|
||||||
if _sys.version_info[0] == 3:
|
|
||||||
# Python 3.x specific logic.
|
|
||||||
_sys_maxint = _sys.maxsize
|
|
||||||
|
|
||||||
_int_type = int
|
|
||||||
|
|
||||||
_str_type = str
|
|
||||||
|
|
||||||
_bytes_type = lambda x: bytes(x, 'UTF-8')
|
|
||||||
|
|
||||||
_is_str = lambda x: isinstance(x, (str, type(''.encode())))
|
|
||||||
|
|
||||||
_is_int = lambda x: isinstance(x, int)
|
|
||||||
|
|
||||||
_callable = lambda x: hasattr(x, '__call__')
|
|
||||||
|
|
||||||
_dict_keys = lambda x: list(x.keys())
|
|
||||||
|
|
||||||
_dict_items = lambda x: list(x.items())
|
|
||||||
|
|
||||||
_iter_dict_keys = lambda x: x.keys()
|
|
||||||
|
|
||||||
def _bytes_join(*args):
|
|
||||||
return ''.encode().join(*args)
|
|
||||||
|
|
||||||
def _zip(*args):
|
|
||||||
return list(zip(*args))
|
|
||||||
|
|
||||||
def _range(*args, **kwargs):
|
|
||||||
return list(range(*args, **kwargs))
|
|
||||||
|
|
||||||
_iter_range = range
|
|
||||||
|
|
||||||
def _iter_next(x):
|
|
||||||
return next(x)
|
|
||||||
|
|
||||||
elif _sys.version_info[0:2] > [2, 3]:
|
|
||||||
# Python 2.4 or higher.
|
|
||||||
_sys_maxint = _sys.maxint
|
|
||||||
|
|
||||||
_int_type = (int, long)
|
|
||||||
|
|
||||||
_str_type = basestring
|
|
||||||
|
|
||||||
_bytes_type = str
|
|
||||||
|
|
||||||
_is_str = lambda x: isinstance(x, basestring)
|
|
||||||
|
|
||||||
_is_int = lambda x: isinstance(x, (int, long))
|
|
||||||
|
|
||||||
_callable = lambda x: callable(x)
|
|
||||||
|
|
||||||
_dict_keys = lambda x: x.keys()
|
|
||||||
|
|
||||||
_dict_items = lambda x: x.items()
|
|
||||||
|
|
||||||
_iter_dict_keys = lambda x: iter(x.keys())
|
|
||||||
|
|
||||||
def _bytes_join(*args):
|
|
||||||
return ''.join(*args)
|
|
||||||
|
|
||||||
def _zip(*args):
|
|
||||||
return zip(*args)
|
|
||||||
|
|
||||||
def _range(*args, **kwargs):
|
|
||||||
return range(*args, **kwargs)
|
|
||||||
|
|
||||||
_iter_range = xrange
|
|
||||||
|
|
||||||
def _iter_next(x):
|
|
||||||
return x.next()
|
|
||||||
else:
|
|
||||||
# Unsupported versions.
|
|
||||||
raise RuntimeError(
|
|
||||||
'this module only supports Python 2.4.x or higher (including 3.x)!')
|
|
||||||
|
|
||||||
try:
|
|
||||||
from importlib import resources as _importlib_resources
|
|
||||||
except ImportError:
|
|
||||||
import importlib_resources as _importlib_resources
|
|
@ -1,12 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
The netaddr.contrib namespace for non-core code contributed by users.
|
|
||||||
|
|
||||||
It is a testing ground for new ideas. Depending on the interest in
|
|
||||||
functionality found here, code may find its way into the core in various
|
|
||||||
ways, either as is or as additions to existing APIs.
|
|
||||||
"""
|
|
@ -1,46 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
from netaddr.ip import IPNetwork, cidr_exclude, cidr_merge
|
|
||||||
|
|
||||||
|
|
||||||
class SubnetSplitter(object):
|
|
||||||
"""
|
|
||||||
A handy utility class that takes a single (large) subnet and allows
|
|
||||||
smaller subnet within its range to be extracted by CIDR prefix. Any
|
|
||||||
leaving address space is available for subsequent extractions until
|
|
||||||
all space is exhausted.
|
|
||||||
"""
|
|
||||||
def __init__(self, base_cidr):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param base_cidr: an IPv4 or IPv6 address with a CIDR prefix.
|
|
||||||
(see IPNetwork.__init__ for full details).
|
|
||||||
"""
|
|
||||||
self._subnets = set([IPNetwork(base_cidr)])
|
|
||||||
|
|
||||||
def extract_subnet(self, prefix, count=None):
|
|
||||||
"""Extract 1 or more subnets of size specified by CIDR prefix."""
|
|
||||||
for cidr in self.available_subnets():
|
|
||||||
subnets = list(cidr.subnet(prefix, count=count))
|
|
||||||
if not subnets:
|
|
||||||
continue
|
|
||||||
self.remove_subnet(cidr)
|
|
||||||
self._subnets = self._subnets.union(
|
|
||||||
set(
|
|
||||||
cidr_exclude(cidr, cidr_merge(subnets)[0])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return subnets
|
|
||||||
return []
|
|
||||||
|
|
||||||
def available_subnets(self):
|
|
||||||
"""Returns a list of the currently available subnets."""
|
|
||||||
return sorted(self._subnets, key=lambda x: x.prefixlen, reverse=True)
|
|
||||||
|
|
||||||
def remove_subnet(self, ip_network):
|
|
||||||
"""Remove a specified IPNetwork from available address space."""
|
|
||||||
self._subnets.remove(ip_network)
|
|
206
vendor/lib/python2.7/site-packages/netaddr/core.py
vendored
206
vendor/lib/python2.7/site-packages/netaddr/core.py
vendored
@ -1,206 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""Common code shared between various netaddr sub modules"""
|
|
||||||
|
|
||||||
import sys as _sys
|
|
||||||
import pprint as _pprint
|
|
||||||
|
|
||||||
from netaddr.compat import _callable, _iter_dict_keys
|
|
||||||
|
|
||||||
#: True if platform is natively big endian, False otherwise.
|
|
||||||
BIG_ENDIAN_PLATFORM = _sys.byteorder == 'big'
|
|
||||||
|
|
||||||
#: Use inet_pton() semantics instead of inet_aton() when parsing IPv4.
|
|
||||||
P = INET_PTON = 1
|
|
||||||
|
|
||||||
#: Remove any preceding zeros from IPv4 address octets before parsing.
|
|
||||||
Z = ZEROFILL = 2
|
|
||||||
|
|
||||||
#: Remove any host bits found to the right of an applied CIDR prefix.
|
|
||||||
N = NOHOST = 4
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Custom exceptions.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
class AddrFormatError(Exception):
|
|
||||||
"""
|
|
||||||
An Exception indicating a network address is not correctly formatted.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AddrConversionError(Exception):
|
|
||||||
"""
|
|
||||||
An Exception indicating a failure to convert between address types or
|
|
||||||
notations.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NotRegisteredError(Exception):
|
|
||||||
"""
|
|
||||||
An Exception indicating that an OUI or IAB was not found in the IEEE
|
|
||||||
Registry.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
a = 42
|
|
||||||
a.bit_length()
|
|
||||||
# No exception, must be Python 2.7 or 3.1+ -> can use bit_length()
|
|
||||||
def num_bits(int_val):
|
|
||||||
"""
|
|
||||||
:param int_val: an unsigned integer.
|
|
||||||
|
|
||||||
:return: the minimum number of bits needed to represent value provided.
|
|
||||||
"""
|
|
||||||
return int_val.bit_length()
|
|
||||||
except AttributeError:
|
|
||||||
# a.bit_length() excepted, must be an older Python version.
|
|
||||||
def num_bits(int_val):
|
|
||||||
"""
|
|
||||||
:param int_val: an unsigned integer.
|
|
||||||
|
|
||||||
:return: the minimum number of bits needed to represent value provided.
|
|
||||||
"""
|
|
||||||
numbits = 0
|
|
||||||
while int_val:
|
|
||||||
numbits += 1
|
|
||||||
int_val >>= 1
|
|
||||||
return numbits
|
|
||||||
|
|
||||||
|
|
||||||
class Subscriber(object):
|
|
||||||
"""
|
|
||||||
An abstract class defining the interface expected by a Publisher.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
"""
|
|
||||||
A callback method used by a Publisher to notify this Subscriber about
|
|
||||||
updates.
|
|
||||||
|
|
||||||
:param data: a Python object containing data provided by Publisher.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError('cannot invoke virtual method!')
|
|
||||||
|
|
||||||
|
|
||||||
class PrettyPrinter(Subscriber):
|
|
||||||
"""
|
|
||||||
A concrete Subscriber that employs the pprint in the standard library to
|
|
||||||
format all data from updates received, writing them to a file-like
|
|
||||||
object.
|
|
||||||
|
|
||||||
Useful as a debugging aid.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, fh=_sys.stdout, write_eol=True):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param fh: a file-like object to write updates to.
|
|
||||||
Default: sys.stdout.
|
|
||||||
|
|
||||||
|
|
||||||
:param write_eol: if ``True`` this object will write newlines to
|
|
||||||
output, if ``False`` it will not.
|
|
||||||
"""
|
|
||||||
self.fh = fh
|
|
||||||
self.write_eol = write_eol
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
"""
|
|
||||||
A callback method used by a Publisher to notify this Subscriber about
|
|
||||||
updates.
|
|
||||||
|
|
||||||
:param data: a Python object containing data provided by Publisher.
|
|
||||||
"""
|
|
||||||
self.fh.write(_pprint.pformat(data))
|
|
||||||
if self.write_eol:
|
|
||||||
self.fh.write("\n")
|
|
||||||
|
|
||||||
|
|
||||||
class Publisher(object):
|
|
||||||
"""
|
|
||||||
A 'push' Publisher that maintains a list of Subscriber objects notifying
|
|
||||||
them of state changes by passing them update data when it encounter events
|
|
||||||
of interest.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Constructor"""
|
|
||||||
self.subscribers = []
|
|
||||||
|
|
||||||
def attach(self, subscriber):
|
|
||||||
"""
|
|
||||||
Add a new subscriber.
|
|
||||||
|
|
||||||
:param subscriber: a new object that implements the Subscriber object
|
|
||||||
interface.
|
|
||||||
"""
|
|
||||||
if hasattr(subscriber, 'update') and _callable(eval('subscriber.update')):
|
|
||||||
if subscriber not in self.subscribers:
|
|
||||||
self.subscribers.append(subscriber)
|
|
||||||
else:
|
|
||||||
raise TypeError('%r does not support required interface!' % subscriber)
|
|
||||||
|
|
||||||
def detach(self, subscriber):
|
|
||||||
"""
|
|
||||||
Remove an existing subscriber.
|
|
||||||
|
|
||||||
:param subscriber: a new object that implements the Subscriber object
|
|
||||||
interface.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
self.subscribers.remove(subscriber)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def notify(self, data):
|
|
||||||
"""
|
|
||||||
Send update data to to all registered Subscribers.
|
|
||||||
|
|
||||||
:param data: the data to be passed to each registered Subscriber.
|
|
||||||
"""
|
|
||||||
for subscriber in self.subscribers:
|
|
||||||
subscriber.update(data)
|
|
||||||
|
|
||||||
|
|
||||||
class DictDotLookup(object):
|
|
||||||
"""
|
|
||||||
Creates objects that behave much like a dictionaries, but allow nested
|
|
||||||
key access using object '.' (dot) lookups.
|
|
||||||
|
|
||||||
Recipe 576586: Dot-style nested lookups over dictionary based data
|
|
||||||
structures - http://code.activestate.com/recipes/576586/
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, d):
|
|
||||||
for k in d:
|
|
||||||
if isinstance(d[k], dict):
|
|
||||||
self.__dict__[k] = DictDotLookup(d[k])
|
|
||||||
elif isinstance(d[k], (list, tuple)):
|
|
||||||
l = []
|
|
||||||
for v in d[k]:
|
|
||||||
if isinstance(v, dict):
|
|
||||||
l.append(DictDotLookup(v))
|
|
||||||
else:
|
|
||||||
l.append(v)
|
|
||||||
self.__dict__[k] = l
|
|
||||||
else:
|
|
||||||
self.__dict__[k] = d[k]
|
|
||||||
|
|
||||||
def __getitem__(self, name):
|
|
||||||
if name in self.__dict__:
|
|
||||||
return self.__dict__[name]
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return _iter_dict_keys(self.__dict__)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return _pprint.pformat(self.__dict__)
|
|
@ -1,749 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Classes and functions for dealing with MAC addresses, EUI-48, EUI-64, OUI, IAB
|
|
||||||
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 _importlib_resources, _is_int, _is_str
|
|
||||||
|
|
||||||
|
|
||||||
class BaseIdentifier(object):
|
|
||||||
"""Base class for all IEEE identifiers."""
|
|
||||||
__slots__ = ('_value', '__weakref__')
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._value = None
|
|
||||||
|
|
||||||
def __int__(self):
|
|
||||||
""":return: integer value of this identifier"""
|
|
||||||
return self._value
|
|
||||||
|
|
||||||
def __long__(self):
|
|
||||||
""":return: integer value of this identifier"""
|
|
||||||
return self._value
|
|
||||||
|
|
||||||
def __oct__(self):
|
|
||||||
""":return: octal string representation of this identifier."""
|
|
||||||
# Python 2.x only.
|
|
||||||
if self._value == 0:
|
|
||||||
return '0'
|
|
||||||
return '0%o' % self._value
|
|
||||||
|
|
||||||
def __hex__(self):
|
|
||||||
""":return: hexadecimal string representation of this identifier."""
|
|
||||||
# Python 2.x only.
|
|
||||||
return '0x%x' % self._value
|
|
||||||
|
|
||||||
def __index__(self):
|
|
||||||
"""
|
|
||||||
:return: return the integer value of this identifier when passed to
|
|
||||||
hex(), oct() or bin().
|
|
||||||
"""
|
|
||||||
# Python 3.x only.
|
|
||||||
return self._value
|
|
||||||
|
|
||||||
|
|
||||||
class OUI(BaseIdentifier):
|
|
||||||
"""
|
|
||||||
An individual IEEE OUI (Organisationally Unique Identifier).
|
|
||||||
|
|
||||||
For online details see - http://standards.ieee.org/regauth/oui/
|
|
||||||
|
|
||||||
"""
|
|
||||||
__slots__ = ('records',)
|
|
||||||
|
|
||||||
def __init__(self, oui):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
:param oui: an OUI string ``XX-XX-XX`` or an unsigned integer. \
|
|
||||||
Also accepts and parses full MAC/EUI-48 address strings (but not \
|
|
||||||
MAC/EUI-48 integers)!
|
|
||||||
"""
|
|
||||||
super(OUI, self).__init__()
|
|
||||||
|
|
||||||
# Lazy loading of IEEE data structures.
|
|
||||||
from netaddr.eui import ieee
|
|
||||||
|
|
||||||
self.records = []
|
|
||||||
|
|
||||||
if isinstance(oui, str):
|
|
||||||
#TODO: Improve string parsing here.
|
|
||||||
#TODO: Accept full MAC/EUI-48 addressses as well as XX-XX-XX
|
|
||||||
#TODO: and just take /16 (see IAB for details)
|
|
||||||
self._value = int(oui.replace('-', ''), 16)
|
|
||||||
elif _is_int(oui):
|
|
||||||
if 0 <= oui <= 0xffffff:
|
|
||||||
self._value = oui
|
|
||||||
else:
|
|
||||||
raise ValueError('OUI int outside expected range: %r' % (oui,))
|
|
||||||
else:
|
|
||||||
raise TypeError('unexpected OUI format: %r' % (oui,))
|
|
||||||
|
|
||||||
# Discover offsets.
|
|
||||||
if self._value in ieee.OUI_INDEX:
|
|
||||||
fh = _importlib_resources.open_binary(__package__, 'oui.txt')
|
|
||||||
for (offset, size) in ieee.OUI_INDEX[self._value]:
|
|
||||||
fh.seek(offset)
|
|
||||||
data = fh.read(size).decode('UTF-8')
|
|
||||||
self._parse_data(data, offset, size)
|
|
||||||
fh.close()
|
|
||||||
else:
|
|
||||||
raise NotRegisteredError('OUI %r not registered!' % (oui,))
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if not isinstance(other, OUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return self._value == other._value
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
if not isinstance(other, OUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return self._value != other._value
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
""":returns: Pickled state of an `OUI` object."""
|
|
||||||
return self._value, self.records
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
""":param state: data used to unpickle a pickled `OUI` object."""
|
|
||||||
self._value, self.records = state
|
|
||||||
|
|
||||||
def _parse_data(self, data, offset, size):
|
|
||||||
"""Returns a dict record from raw OUI record data"""
|
|
||||||
record = {
|
|
||||||
'idx': 0,
|
|
||||||
'oui': '',
|
|
||||||
'org': '',
|
|
||||||
'address': [],
|
|
||||||
'offset': offset,
|
|
||||||
'size': size,
|
|
||||||
}
|
|
||||||
|
|
||||||
for line in data.split("\n"):
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if '(hex)' in line:
|
|
||||||
record['idx'] = self._value
|
|
||||||
record['org'] = line.split(None, 2)[2]
|
|
||||||
record['oui'] = str(self)
|
|
||||||
elif '(base 16)' in line:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
record['address'].append(line)
|
|
||||||
|
|
||||||
self.records.append(record)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def reg_count(self):
|
|
||||||
"""Number of registered organisations with this OUI"""
|
|
||||||
return len(self.records)
|
|
||||||
|
|
||||||
def registration(self, index=0):
|
|
||||||
"""
|
|
||||||
The IEEE registration details for this OUI.
|
|
||||||
|
|
||||||
:param index: the index of record (may contain multiple registrations)
|
|
||||||
(Default: 0 - first registration)
|
|
||||||
|
|
||||||
:return: Objectified Python data structure containing registration
|
|
||||||
details.
|
|
||||||
"""
|
|
||||||
return DictDotLookup(self.records[index])
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
""":return: string representation of this OUI"""
|
|
||||||
int_val = self._value
|
|
||||||
return "%02X-%02X-%02X" % (
|
|
||||||
(int_val >> 16) & 0xff,
|
|
||||||
(int_val >> 8) & 0xff,
|
|
||||||
int_val & 0xff)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""":return: executable Python string to recreate equivalent object."""
|
|
||||||
return "OUI('%s')" % self
|
|
||||||
|
|
||||||
|
|
||||||
class IAB(BaseIdentifier):
|
|
||||||
IAB_EUI_VALUES = (0x0050c2, 0x40d855)
|
|
||||||
|
|
||||||
"""
|
|
||||||
An individual IEEE IAB (Individual Address Block) identifier.
|
|
||||||
|
|
||||||
For online details see - http://standards.ieee.org/regauth/oui/
|
|
||||||
|
|
||||||
"""
|
|
||||||
__slots__ = ('record',)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def split_iab_mac(cls, eui_int, strict=False):
|
|
||||||
"""
|
|
||||||
:param eui_int: a MAC IAB as an unsigned integer.
|
|
||||||
|
|
||||||
:param strict: If True, raises a ValueError if the last 12 bits of
|
|
||||||
IAB MAC/EUI-48 address are non-zero, ignores them otherwise.
|
|
||||||
(Default: False)
|
|
||||||
"""
|
|
||||||
if (eui_int >> 12) in cls.IAB_EUI_VALUES:
|
|
||||||
return eui_int, 0
|
|
||||||
|
|
||||||
user_mask = 2 ** 12 - 1
|
|
||||||
iab_mask = (2 ** 48 - 1) ^ user_mask
|
|
||||||
iab_bits = eui_int >> 12
|
|
||||||
user_bits = (eui_int | iab_mask) - iab_mask
|
|
||||||
|
|
||||||
if (iab_bits >> 12) in cls.IAB_EUI_VALUES:
|
|
||||||
if strict and user_bits != 0:
|
|
||||||
raise ValueError('%r is not a strict IAB!' % hex(user_bits))
|
|
||||||
else:
|
|
||||||
raise ValueError('%r is not an IAB address!' % hex(eui_int))
|
|
||||||
|
|
||||||
return iab_bits, user_bits
|
|
||||||
|
|
||||||
def __init__(self, iab, strict=False):
|
|
||||||
"""
|
|
||||||
Constructor
|
|
||||||
|
|
||||||
:param iab: an IAB string ``00-50-C2-XX-X0-00`` or an unsigned \
|
|
||||||
integer. This address looks like an EUI-48 but it should not \
|
|
||||||
have any non-zero bits in the last 3 bytes.
|
|
||||||
|
|
||||||
:param strict: If True, raises a ValueError if the last 12 bits \
|
|
||||||
of IAB MAC/EUI-48 address are non-zero, ignores them otherwise. \
|
|
||||||
(Default: False)
|
|
||||||
"""
|
|
||||||
super(IAB, self).__init__()
|
|
||||||
|
|
||||||
# Lazy loading of IEEE data structures.
|
|
||||||
from netaddr.eui import ieee
|
|
||||||
|
|
||||||
self.record = {
|
|
||||||
'idx': 0,
|
|
||||||
'iab': '',
|
|
||||||
'org': '',
|
|
||||||
'address': [],
|
|
||||||
'offset': 0,
|
|
||||||
'size': 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
if isinstance(iab, str):
|
|
||||||
#TODO: Improve string parsing here.
|
|
||||||
#TODO: '00-50-C2' is actually invalid.
|
|
||||||
#TODO: Should be '00-50-C2-00-00-00' (i.e. a full MAC/EUI-48)
|
|
||||||
int_val = int(iab.replace('-', ''), 16)
|
|
||||||
iab_int, user_int = self.split_iab_mac(int_val, strict=strict)
|
|
||||||
self._value = iab_int
|
|
||||||
elif _is_int(iab):
|
|
||||||
iab_int, user_int = self.split_iab_mac(iab, strict=strict)
|
|
||||||
self._value = iab_int
|
|
||||||
else:
|
|
||||||
raise TypeError('unexpected IAB format: %r!' % (iab,))
|
|
||||||
|
|
||||||
# Discover offsets.
|
|
||||||
if self._value in ieee.IAB_INDEX:
|
|
||||||
fh = _importlib_resources.open_binary(__package__, 'iab.txt')
|
|
||||||
(offset, size) = ieee.IAB_INDEX[self._value][0]
|
|
||||||
self.record['offset'] = offset
|
|
||||||
self.record['size'] = size
|
|
||||||
fh.seek(offset)
|
|
||||||
data = fh.read(size).decode('UTF-8')
|
|
||||||
self._parse_data(data, offset, size)
|
|
||||||
fh.close()
|
|
||||||
else:
|
|
||||||
raise NotRegisteredError('IAB %r not unregistered!' % (iab,))
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if not isinstance(other, IAB):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return self._value == other._value
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
if not isinstance(other, IAB):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return self._value != other._value
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
""":returns: Pickled state of an `IAB` object."""
|
|
||||||
return self._value, self.record
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
""":param state: data used to unpickle a pickled `IAB` object."""
|
|
||||||
self._value, self.record = state
|
|
||||||
|
|
||||||
def _parse_data(self, data, offset, size):
|
|
||||||
"""Returns a dict record from raw IAB record data"""
|
|
||||||
for line in data.split("\n"):
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if '(hex)' in line:
|
|
||||||
self.record['idx'] = self._value
|
|
||||||
self.record['org'] = line.split(None, 2)[2]
|
|
||||||
self.record['iab'] = str(self)
|
|
||||||
elif '(base 16)' in line:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
self.record['address'].append(line)
|
|
||||||
|
|
||||||
def registration(self):
|
|
||||||
"""The IEEE registration details for this IAB"""
|
|
||||||
return DictDotLookup(self.record)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
""":return: string representation of this IAB"""
|
|
||||||
int_val = self._value << 4
|
|
||||||
|
|
||||||
return "%02X-%02X-%02X-%02X-%02X-00" % (
|
|
||||||
(int_val >> 32) & 0xff,
|
|
||||||
(int_val >> 24) & 0xff,
|
|
||||||
(int_val >> 16) & 0xff,
|
|
||||||
(int_val >> 8) & 0xff,
|
|
||||||
int_val & 0xff)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""":return: executable Python string to recreate equivalent object."""
|
|
||||||
return "IAB('%s')" % self
|
|
||||||
|
|
||||||
|
|
||||||
class EUI(BaseIdentifier):
|
|
||||||
"""
|
|
||||||
An IEEE EUI (Extended Unique Identifier).
|
|
||||||
|
|
||||||
Both EUI-48 (used for layer 2 MAC addresses) and EUI-64 are supported.
|
|
||||||
|
|
||||||
Input parsing for EUI-48 addresses is flexible, supporting many MAC
|
|
||||||
variants.
|
|
||||||
|
|
||||||
"""
|
|
||||||
__slots__ = ('_module', '_dialect')
|
|
||||||
|
|
||||||
def __init__(self, addr, version=None, dialect=None):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param addr: an EUI-48 (MAC) or EUI-64 address in string format or \
|
|
||||||
an unsigned integer. May also be another EUI object (copy \
|
|
||||||
construction).
|
|
||||||
|
|
||||||
:param version: (optional) the explicit EUI address version, either \
|
|
||||||
48 or 64. Mainly used to distinguish EUI-48 and EUI-64 identifiers \
|
|
||||||
specified as integers which may be numerically equivalent.
|
|
||||||
|
|
||||||
:param dialect: (optional) the mac_* dialect to be used to configure \
|
|
||||||
the formatting of EUI-48 (MAC) addresses.
|
|
||||||
"""
|
|
||||||
super(EUI, self).__init__()
|
|
||||||
|
|
||||||
self._module = None
|
|
||||||
|
|
||||||
if isinstance(addr, EUI):
|
|
||||||
# Copy constructor.
|
|
||||||
if version is not None and version != addr._module.version:
|
|
||||||
raise ValueError('cannot switch EUI versions using '
|
|
||||||
'copy constructor!')
|
|
||||||
self._module = addr._module
|
|
||||||
self._value = addr._value
|
|
||||||
self.dialect = addr.dialect
|
|
||||||
return
|
|
||||||
|
|
||||||
if version is not None:
|
|
||||||
if version == 48:
|
|
||||||
self._module = _eui48
|
|
||||||
elif version == 64:
|
|
||||||
self._module = _eui64
|
|
||||||
else:
|
|
||||||
raise ValueError('unsupported EUI version %r' % version)
|
|
||||||
else:
|
|
||||||
# Choose a default version when addr is an integer and version is
|
|
||||||
# not specified.
|
|
||||||
if _is_int(addr):
|
|
||||||
if 0 <= addr <= 0xffffffffffff:
|
|
||||||
self._module = _eui48
|
|
||||||
elif 0xffffffffffff < addr <= 0xffffffffffffffff:
|
|
||||||
self._module = _eui64
|
|
||||||
|
|
||||||
self.value = addr
|
|
||||||
|
|
||||||
# Choose a dialect for MAC formatting.
|
|
||||||
self.dialect = dialect
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
""":returns: Pickled state of an `EUI` object."""
|
|
||||||
return self._value, self._module.version, self.dialect
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
"""
|
|
||||||
:param state: data used to unpickle a pickled `EUI` object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
value, version, dialect = state
|
|
||||||
|
|
||||||
self._value = value
|
|
||||||
|
|
||||||
if version == 48:
|
|
||||||
self._module = _eui48
|
|
||||||
elif version == 64:
|
|
||||||
self._module = _eui64
|
|
||||||
else:
|
|
||||||
raise ValueError('unpickling failed for object state: %s' \
|
|
||||||
% (state,))
|
|
||||||
|
|
||||||
self.dialect = dialect
|
|
||||||
|
|
||||||
def _get_value(self):
|
|
||||||
return self._value
|
|
||||||
|
|
||||||
def _set_value(self, value):
|
|
||||||
if self._module is None:
|
|
||||||
# EUI version is implicit, detect it from value.
|
|
||||||
for module in (_eui48, _eui64):
|
|
||||||
try:
|
|
||||||
self._value = module.str_to_int(value)
|
|
||||||
self._module = module
|
|
||||||
break
|
|
||||||
except AddrFormatError:
|
|
||||||
try:
|
|
||||||
if 0 <= int(value) <= module.max_int:
|
|
||||||
self._value = int(value)
|
|
||||||
self._module = module
|
|
||||||
break
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if self._module is None:
|
|
||||||
raise AddrFormatError('failed to detect EUI version: %r'
|
|
||||||
% (value,))
|
|
||||||
else:
|
|
||||||
# EUI version is explicit.
|
|
||||||
if _is_str(value):
|
|
||||||
try:
|
|
||||||
self._value = self._module.str_to_int(value)
|
|
||||||
except AddrFormatError:
|
|
||||||
raise AddrFormatError('address %r is not an EUIv%d'
|
|
||||||
% (value, self._module.version))
|
|
||||||
else:
|
|
||||||
if 0 <= int(value) <= self._module.max_int:
|
|
||||||
self._value = int(value)
|
|
||||||
else:
|
|
||||||
raise AddrFormatError('bad address format: %r' % (value,))
|
|
||||||
|
|
||||||
value = property(_get_value, _set_value, None,
|
|
||||||
'a positive integer representing the value of this EUI indentifier.')
|
|
||||||
|
|
||||||
def _get_dialect(self):
|
|
||||||
return self._dialect
|
|
||||||
|
|
||||||
def _validate_dialect(self, value):
|
|
||||||
if value is None:
|
|
||||||
if self._module is _eui64:
|
|
||||||
return eui64_base
|
|
||||||
else:
|
|
||||||
return mac_eui48
|
|
||||||
else:
|
|
||||||
if hasattr(value, 'word_size') and hasattr(value, 'word_fmt'):
|
|
||||||
return value
|
|
||||||
else:
|
|
||||||
raise TypeError('custom dialects should subclass mac_eui48!')
|
|
||||||
|
|
||||||
def _set_dialect(self, value):
|
|
||||||
self._dialect = self._validate_dialect(value)
|
|
||||||
|
|
||||||
dialect = property(_get_dialect, _set_dialect, None,
|
|
||||||
"a Python class providing support for the interpretation of "
|
|
||||||
"various MAC\n address formats.")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def oui(self):
|
|
||||||
"""The OUI (Organisationally Unique Identifier) for this EUI."""
|
|
||||||
if self._module == _eui48:
|
|
||||||
return OUI(self.value >> 24)
|
|
||||||
elif self._module == _eui64:
|
|
||||||
return OUI(self.value >> 40)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def ei(self):
|
|
||||||
"""The EI (Extension Identifier) for this EUI"""
|
|
||||||
if self._module == _eui48:
|
|
||||||
return '%02X-%02X-%02X' % tuple(self[3:6])
|
|
||||||
elif self._module == _eui64:
|
|
||||||
return '%02X-%02X-%02X-%02X-%02X' % tuple(self[3:8])
|
|
||||||
|
|
||||||
def is_iab(self):
|
|
||||||
""":return: True if this EUI is an IAB address, False otherwise"""
|
|
||||||
return (self._value >> 24) in IAB.IAB_EUI_VALUES
|
|
||||||
|
|
||||||
@property
|
|
||||||
def iab(self):
|
|
||||||
"""
|
|
||||||
If is_iab() is True, the IAB (Individual Address Block) is returned,
|
|
||||||
``None`` otherwise.
|
|
||||||
"""
|
|
||||||
if self.is_iab():
|
|
||||||
return IAB(self._value >> 12)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def version(self):
|
|
||||||
"""The EUI version represented by this EUI object."""
|
|
||||||
return self._module.version
|
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
|
||||||
"""
|
|
||||||
:return: The integer value of the word referenced by index (both \
|
|
||||||
positive and negative). Raises ``IndexError`` if index is out \
|
|
||||||
of bounds. Also supports Python list slices for accessing \
|
|
||||||
word groups.
|
|
||||||
"""
|
|
||||||
if _is_int(idx):
|
|
||||||
# Indexing, including negative indexing goodness.
|
|
||||||
num_words = self._dialect.num_words
|
|
||||||
if not (-num_words) <= idx <= (num_words - 1):
|
|
||||||
raise IndexError('index out range for address type!')
|
|
||||||
return self._module.int_to_words(self._value, self._dialect)[idx]
|
|
||||||
elif isinstance(idx, slice):
|
|
||||||
words = self._module.int_to_words(self._value, self._dialect)
|
|
||||||
return [words[i] for i in range(*idx.indices(len(words)))]
|
|
||||||
else:
|
|
||||||
raise TypeError('unsupported type %r!' % (idx,))
|
|
||||||
|
|
||||||
def __setitem__(self, idx, value):
|
|
||||||
"""Set the value of the word referenced by index in this address"""
|
|
||||||
if isinstance(idx, slice):
|
|
||||||
# TODO - settable slices.
|
|
||||||
raise NotImplementedError('settable slices are not supported!')
|
|
||||||
|
|
||||||
if not _is_int(idx):
|
|
||||||
raise TypeError('index not an integer!')
|
|
||||||
|
|
||||||
if not 0 <= idx <= (self._dialect.num_words - 1):
|
|
||||||
raise IndexError('index %d outside address type boundary!' % (idx,))
|
|
||||||
|
|
||||||
if not _is_int(value):
|
|
||||||
raise TypeError('value not an integer!')
|
|
||||||
|
|
||||||
if not 0 <= value <= self._dialect.max_word:
|
|
||||||
raise IndexError('value %d outside word size maximum of %d bits!'
|
|
||||||
% (value, self._dialect.word_size))
|
|
||||||
|
|
||||||
words = list(self._module.int_to_words(self._value, self._dialect))
|
|
||||||
words[idx] = value
|
|
||||||
self._value = self._module.words_to_int(words)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
""":return: hash of this EUI object suitable for dict keys, sets etc"""
|
|
||||||
return hash((self.version, self._value))
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically the same as other, \
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) == (other.version, other._value)
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically the same as other, \
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) != (other.version, other._value)
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically lower in value than \
|
|
||||||
other, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) < (other.version, other._value)
|
|
||||||
|
|
||||||
def __le__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically lower or equal in \
|
|
||||||
value to other, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) <= (other.version, other._value)
|
|
||||||
|
|
||||||
def __gt__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically greater in value \
|
|
||||||
than other, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) > (other.version, other._value)
|
|
||||||
|
|
||||||
def __ge__(self, other):
|
|
||||||
"""
|
|
||||||
:return: ``True`` if this EUI object is numerically greater or equal \
|
|
||||||
in value to other, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not isinstance(other, EUI):
|
|
||||||
try:
|
|
||||||
other = self.__class__(other)
|
|
||||||
except Exception:
|
|
||||||
return NotImplemented
|
|
||||||
return (self.version, self._value) >= (other.version, other._value)
|
|
||||||
|
|
||||||
def bits(self, word_sep=None):
|
|
||||||
"""
|
|
||||||
:param word_sep: (optional) the separator to insert between words. \
|
|
||||||
Default: None - use default separator for address type.
|
|
||||||
|
|
||||||
:return: human-readable binary digit string of this address.
|
|
||||||
"""
|
|
||||||
return self._module.int_to_bits(self._value, word_sep)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def packed(self):
|
|
||||||
"""The value of this EUI address as a packed binary string."""
|
|
||||||
return self._module.int_to_packed(self._value)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def words(self):
|
|
||||||
"""A list of unsigned integer octets found in this EUI address."""
|
|
||||||
return self._module.int_to_words(self._value)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bin(self):
|
|
||||||
"""
|
|
||||||
The value of this EUI adddress in standard Python binary
|
|
||||||
representational form (0bxxx). A back port of the format provided by
|
|
||||||
the builtin bin() function found in Python 2.6.x and higher.
|
|
||||||
"""
|
|
||||||
return self._module.int_to_bin(self._value)
|
|
||||||
|
|
||||||
def eui64(self):
|
|
||||||
"""
|
|
||||||
- If this object represents an EUI-48 it is converted to EUI-64 \
|
|
||||||
as per the standard.
|
|
||||||
- If this object is already an EUI-64, a new, numerically \
|
|
||||||
equivalent object is returned instead.
|
|
||||||
|
|
||||||
:return: The value of this EUI object as a new 64-bit EUI object.
|
|
||||||
"""
|
|
||||||
if self.version == 48:
|
|
||||||
# Convert 11:22:33:44:55:66 into 11:22:33:FF:FE:44:55:66.
|
|
||||||
first_three = self._value >> 24
|
|
||||||
last_three = self._value & 0xffffff
|
|
||||||
new_value = (first_three << 40) | 0xfffe000000 | last_three
|
|
||||||
else:
|
|
||||||
# is already a EUI64
|
|
||||||
new_value = self._value
|
|
||||||
return self.__class__(new_value, version=64)
|
|
||||||
|
|
||||||
def modified_eui64(self):
|
|
||||||
"""
|
|
||||||
- create a new EUI object with a modified EUI-64 as described in RFC 4291 section 2.5.1
|
|
||||||
|
|
||||||
:return: a new and modified 64-bit EUI object.
|
|
||||||
"""
|
|
||||||
# Modified EUI-64 format interface identifiers are formed by inverting
|
|
||||||
# the "u" bit (universal/local bit in IEEE EUI-64 terminology) when
|
|
||||||
# forming the interface identifier from IEEE EUI-64 identifiers. In
|
|
||||||
# the resulting Modified EUI-64 format, the "u" bit is set to one (1)
|
|
||||||
# to indicate universal scope, and it is set to zero (0) to indicate
|
|
||||||
# local scope.
|
|
||||||
eui64 = self.eui64()
|
|
||||||
eui64._value ^= 0x00000000000000000200000000000000
|
|
||||||
return eui64
|
|
||||||
|
|
||||||
def ipv6(self, prefix):
|
|
||||||
"""
|
|
||||||
.. note:: This poses security risks in certain scenarios. \
|
|
||||||
Please read RFC 4941 for details. Reference: RFCs 4291 and 4941.
|
|
||||||
|
|
||||||
:param prefix: ipv6 prefix
|
|
||||||
|
|
||||||
:return: new IPv6 `IPAddress` object based on this `EUI` \
|
|
||||||
using the technique described in RFC 4291.
|
|
||||||
"""
|
|
||||||
int_val = int(prefix) + int(self.modified_eui64())
|
|
||||||
return IPAddress(int_val, version=6)
|
|
||||||
|
|
||||||
def ipv6_link_local(self):
|
|
||||||
"""
|
|
||||||
.. note:: This poses security risks in certain scenarios. \
|
|
||||||
Please read RFC 4941 for details. Reference: RFCs 4291 and 4941.
|
|
||||||
|
|
||||||
:return: new link local IPv6 `IPAddress` object based on this `EUI` \
|
|
||||||
using the technique described in RFC 4291.
|
|
||||||
"""
|
|
||||||
return self.ipv6(0xfe800000000000000000000000000000)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def info(self):
|
|
||||||
"""
|
|
||||||
A record dict containing IEEE registration details for this EUI
|
|
||||||
(MAC-48) if available, None otherwise.
|
|
||||||
"""
|
|
||||||
data = {'OUI': self.oui.registration()}
|
|
||||||
if self.is_iab():
|
|
||||||
data['IAB'] = self.iab.registration()
|
|
||||||
|
|
||||||
return DictDotLookup(data)
|
|
||||||
|
|
||||||
def format(self, dialect=None):
|
|
||||||
"""
|
|
||||||
Format the EUI into the representational format according to the given
|
|
||||||
dialect
|
|
||||||
|
|
||||||
:param dialect: the mac_* dialect defining the formatting of EUI-48 \
|
|
||||||
(MAC) addresses.
|
|
||||||
|
|
||||||
:return: EUI in representational format according to the given dialect
|
|
||||||
"""
|
|
||||||
validated_dialect = self._validate_dialect(dialect)
|
|
||||||
return self._module.int_to_str(self._value, validated_dialect)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
""":return: EUI in representational format"""
|
|
||||||
return self._module.int_to_str(self._value, self._dialect)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""":return: executable Python string to recreate equivalent object."""
|
|
||||||
return "EUI('%s')" % self
|
|
||||||
|
|
4575
vendor/lib/python2.7/site-packages/netaddr/eui/iab.idx
vendored
4575
vendor/lib/python2.7/site-packages/netaddr/eui/iab.idx
vendored
File diff suppressed because it is too large
Load Diff
27381
vendor/lib/python2.7/site-packages/netaddr/eui/iab.txt
vendored
27381
vendor/lib/python2.7/site-packages/netaddr/eui/iab.txt
vendored
File diff suppressed because it is too large
Load Diff
@ -1,293 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# DISCLAIMER
|
|
||||||
#
|
|
||||||
# netaddr is not sponsored nor endorsed by the IEEE.
|
|
||||||
#
|
|
||||||
# Use of data from the IEEE (Institute of Electrical and Electronics
|
|
||||||
# Engineers) is subject to copyright. See the following URL for
|
|
||||||
# details :-
|
|
||||||
#
|
|
||||||
# - http://www.ieee.org/web/publications/rights/legal.html
|
|
||||||
#
|
|
||||||
# IEEE data files included with netaddr are not modified in any way but are
|
|
||||||
# parsed and made available to end users through an API. There is no
|
|
||||||
# guarantee that referenced files are not out of date.
|
|
||||||
#
|
|
||||||
# See README file and source code for URLs to latest copies of the relevant
|
|
||||||
# files.
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Provides access to public OUI and IAB registration data published by the IEEE.
|
|
||||||
|
|
||||||
More details can be found at the following URLs :-
|
|
||||||
|
|
||||||
- IEEE Home Page - http://www.ieee.org/
|
|
||||||
- Registration Authority Home Page - http://standards.ieee.org/regauth/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os.path as _path
|
|
||||||
import csv as _csv
|
|
||||||
|
|
||||||
from netaddr.compat import _bytes_type, _importlib_resources
|
|
||||||
from netaddr.core import Subscriber, Publisher
|
|
||||||
|
|
||||||
|
|
||||||
#: OUI index lookup dictionary.
|
|
||||||
OUI_INDEX = {}
|
|
||||||
|
|
||||||
#: IAB index lookup dictionary.
|
|
||||||
IAB_INDEX = {}
|
|
||||||
|
|
||||||
|
|
||||||
class FileIndexer(Subscriber):
|
|
||||||
"""
|
|
||||||
A concrete Subscriber that receives OUI record offset information that is
|
|
||||||
written to an index data file as a set of comma separated records.
|
|
||||||
"""
|
|
||||||
def __init__(self, index_file):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param index_file: a file-like object or name of index file where
|
|
||||||
index records will be written.
|
|
||||||
"""
|
|
||||||
if hasattr(index_file, 'readline') and hasattr(index_file, 'tell'):
|
|
||||||
self.fh = index_file
|
|
||||||
else:
|
|
||||||
self.fh = open(index_file, 'w')
|
|
||||||
|
|
||||||
self.writer = _csv.writer(self.fh, lineterminator="\n")
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
"""
|
|
||||||
Receives and writes index data to a CSV data file.
|
|
||||||
|
|
||||||
:param data: record containing offset record information.
|
|
||||||
"""
|
|
||||||
self.writer.writerow(data)
|
|
||||||
|
|
||||||
|
|
||||||
class OUIIndexParser(Publisher):
|
|
||||||
"""
|
|
||||||
A concrete Publisher that parses OUI (Organisationally Unique Identifier)
|
|
||||||
records from IEEE text-based registration files
|
|
||||||
|
|
||||||
It notifies registered Subscribers as each record is encountered, passing
|
|
||||||
on the record's position relative to the start of the file (offset) and
|
|
||||||
the size of the record (in bytes).
|
|
||||||
|
|
||||||
The file processed by this parser is available online from this URL :-
|
|
||||||
|
|
||||||
- http://standards.ieee.org/regauth/oui/oui.txt
|
|
||||||
|
|
||||||
This is a sample of the record structure expected::
|
|
||||||
|
|
||||||
00-CA-FE (hex) ACME CORPORATION
|
|
||||||
00CAFE (base 16) ACME CORPORATION
|
|
||||||
1 MAIN STREET
|
|
||||||
SPRINGFIELD
|
|
||||||
UNITED STATES
|
|
||||||
"""
|
|
||||||
def __init__(self, ieee_file):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param ieee_file: a file-like object or name of file containing OUI
|
|
||||||
records. When using a file-like object always open it in binary
|
|
||||||
mode otherwise offsets will probably misbehave.
|
|
||||||
"""
|
|
||||||
super(OUIIndexParser, self).__init__()
|
|
||||||
|
|
||||||
if hasattr(ieee_file, 'readline') and hasattr(ieee_file, 'tell'):
|
|
||||||
self.fh = ieee_file
|
|
||||||
else:
|
|
||||||
self.fh = open(ieee_file, 'rb')
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
"""
|
|
||||||
Starts the parsing process which detects records and notifies
|
|
||||||
registered subscribers as it finds each OUI record.
|
|
||||||
"""
|
|
||||||
skip_header = True
|
|
||||||
record = None
|
|
||||||
size = 0
|
|
||||||
|
|
||||||
marker = _bytes_type('(hex)')
|
|
||||||
hyphen = _bytes_type('-')
|
|
||||||
empty_string = _bytes_type('')
|
|
||||||
|
|
||||||
while True:
|
|
||||||
line = self.fh.readline()
|
|
||||||
|
|
||||||
if not line:
|
|
||||||
break # EOF, we're done
|
|
||||||
|
|
||||||
if skip_header and marker in line:
|
|
||||||
skip_header = False
|
|
||||||
|
|
||||||
if skip_header:
|
|
||||||
# ignoring header section
|
|
||||||
continue
|
|
||||||
|
|
||||||
if marker in line:
|
|
||||||
# record start
|
|
||||||
if record is not None:
|
|
||||||
# a complete record.
|
|
||||||
record.append(size)
|
|
||||||
self.notify(record)
|
|
||||||
|
|
||||||
size = len(line)
|
|
||||||
offset = (self.fh.tell() - len(line))
|
|
||||||
oui = line.split()[0]
|
|
||||||
index = int(oui.replace(hyphen, empty_string), 16)
|
|
||||||
record = [index, offset]
|
|
||||||
else:
|
|
||||||
# within record
|
|
||||||
size += len(line)
|
|
||||||
|
|
||||||
# process final record on loop exit
|
|
||||||
record.append(size)
|
|
||||||
self.notify(record)
|
|
||||||
|
|
||||||
|
|
||||||
class IABIndexParser(Publisher):
|
|
||||||
"""
|
|
||||||
A concrete Publisher that parses IAB (Individual Address Block) records
|
|
||||||
from IEEE text-based registration files
|
|
||||||
|
|
||||||
It notifies registered Subscribers as each record is encountered, passing
|
|
||||||
on the record's position relative to the start of the file (offset) and
|
|
||||||
the size of the record (in bytes).
|
|
||||||
|
|
||||||
The file processed by this parser is available online from this URL :-
|
|
||||||
|
|
||||||
- http://standards.ieee.org/regauth/oui/iab.txt
|
|
||||||
|
|
||||||
This is a sample of the record structure expected::
|
|
||||||
|
|
||||||
00-50-C2 (hex) ACME CORPORATION
|
|
||||||
ABC000-ABCFFF (base 16) ACME CORPORATION
|
|
||||||
1 MAIN STREET
|
|
||||||
SPRINGFIELD
|
|
||||||
UNITED STATES
|
|
||||||
"""
|
|
||||||
def __init__(self, ieee_file):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param ieee_file: a file-like object or name of file containing IAB
|
|
||||||
records. When using a file-like object always open it in binary
|
|
||||||
mode otherwise offsets will probably misbehave.
|
|
||||||
"""
|
|
||||||
super(IABIndexParser, self).__init__()
|
|
||||||
|
|
||||||
if hasattr(ieee_file, 'readline') and hasattr(ieee_file, 'tell'):
|
|
||||||
self.fh = ieee_file
|
|
||||||
else:
|
|
||||||
self.fh = open(ieee_file, 'rb')
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
"""
|
|
||||||
Starts the parsing process which detects records and notifies
|
|
||||||
registered subscribers as it finds each IAB record.
|
|
||||||
"""
|
|
||||||
skip_header = True
|
|
||||||
record = None
|
|
||||||
size = 0
|
|
||||||
|
|
||||||
hex_marker = _bytes_type('(hex)')
|
|
||||||
base16_marker = _bytes_type('(base 16)')
|
|
||||||
hyphen = _bytes_type('-')
|
|
||||||
empty_string = _bytes_type('')
|
|
||||||
|
|
||||||
while True:
|
|
||||||
line = self.fh.readline()
|
|
||||||
|
|
||||||
if not line:
|
|
||||||
break # EOF, we're done
|
|
||||||
|
|
||||||
if skip_header and hex_marker in line:
|
|
||||||
skip_header = False
|
|
||||||
|
|
||||||
if skip_header:
|
|
||||||
# ignoring header section
|
|
||||||
continue
|
|
||||||
|
|
||||||
if hex_marker in line:
|
|
||||||
# record start
|
|
||||||
if record is not None:
|
|
||||||
record.append(size)
|
|
||||||
self.notify(record)
|
|
||||||
|
|
||||||
offset = (self.fh.tell() - len(line))
|
|
||||||
iab_prefix = line.split()[0]
|
|
||||||
index = iab_prefix
|
|
||||||
record = [index, offset]
|
|
||||||
size = len(line)
|
|
||||||
elif base16_marker in line:
|
|
||||||
# within record
|
|
||||||
size += len(line)
|
|
||||||
prefix = record[0].replace(hyphen, empty_string)
|
|
||||||
suffix = line.split()[0]
|
|
||||||
suffix = suffix.split(hyphen)[0]
|
|
||||||
record[0] = (int(prefix + suffix, 16)) >> 12
|
|
||||||
else:
|
|
||||||
# within record
|
|
||||||
size += len(line)
|
|
||||||
|
|
||||||
# process final record on loop exit
|
|
||||||
record.append(size)
|
|
||||||
self.notify(record)
|
|
||||||
|
|
||||||
|
|
||||||
def create_index_from_registry(registry_fh, index_path, parser):
|
|
||||||
"""Generate an index files from the IEEE registry file."""
|
|
||||||
oui_parser = parser(registry_fh)
|
|
||||||
oui_parser.attach(FileIndexer(index_path))
|
|
||||||
oui_parser.parse()
|
|
||||||
|
|
||||||
|
|
||||||
def create_indices():
|
|
||||||
"""Create indices for OUI and IAB file based lookups"""
|
|
||||||
create_index_from_registry(
|
|
||||||
_path.join(_path.dirname(__file__), 'oui.txt'),
|
|
||||||
_path.join(_path.dirname(__file__), 'oui.idx'),
|
|
||||||
OUIIndexParser,
|
|
||||||
)
|
|
||||||
create_index_from_registry(
|
|
||||||
_path.join(_path.dirname(__file__), 'iab.txt'),
|
|
||||||
_path.join(_path.dirname(__file__), 'iab.idx'),
|
|
||||||
IABIndexParser,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def load_index(index, fp):
|
|
||||||
"""Load index from file into index data structure."""
|
|
||||||
try:
|
|
||||||
for row in _csv.reader([x.decode('UTF-8') for x in fp]):
|
|
||||||
(key, offset, size) = [int(_) for _ in row]
|
|
||||||
index.setdefault(key, [])
|
|
||||||
index[key].append((offset, size))
|
|
||||||
finally:
|
|
||||||
fp.close()
|
|
||||||
|
|
||||||
|
|
||||||
def load_indices():
|
|
||||||
"""Load OUI and IAB lookup indices into memory"""
|
|
||||||
load_index(OUI_INDEX, _importlib_resources.open_binary(__package__, 'oui.idx'))
|
|
||||||
load_index(IAB_INDEX, _importlib_resources.open_binary(__package__, 'iab.idx'))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Generate indices when module is executed as a script.
|
|
||||||
create_indices()
|
|
||||||
else:
|
|
||||||
# On module load read indices in memory to enable lookups.
|
|
||||||
load_indices()
|
|
28112
vendor/lib/python2.7/site-packages/netaddr/eui/oui.idx
vendored
28112
vendor/lib/python2.7/site-packages/netaddr/eui/oui.idx
vendored
File diff suppressed because it is too large
Load Diff
168378
vendor/lib/python2.7/site-packages/netaddr/eui/oui.txt
vendored
168378
vendor/lib/python2.7/site-packages/netaddr/eui/oui.txt
vendored
File diff suppressed because it is too large
Load Diff
@ -1,246 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""Fallback routines for Python's standard library socket module"""
|
|
||||||
|
|
||||||
from struct import unpack as _unpack, pack as _pack
|
|
||||||
|
|
||||||
from netaddr.compat import _bytes_join, _is_str
|
|
||||||
|
|
||||||
AF_INET = 2
|
|
||||||
AF_INET6 = 10
|
|
||||||
|
|
||||||
|
|
||||||
def inet_ntoa(packed_ip):
|
|
||||||
"""
|
|
||||||
Convert an IP address from 32-bit packed binary format to string format.
|
|
||||||
"""
|
|
||||||
if not _is_str(packed_ip):
|
|
||||||
raise TypeError('string type expected, not %s' % type(packed_ip))
|
|
||||||
|
|
||||||
if len(packed_ip) != 4:
|
|
||||||
raise ValueError('invalid length of packed IP address string')
|
|
||||||
|
|
||||||
return '%d.%d.%d.%d' % _unpack('4B', packed_ip)
|
|
||||||
|
|
||||||
|
|
||||||
def _compact_ipv6_tokens(tokens):
|
|
||||||
new_tokens = []
|
|
||||||
|
|
||||||
positions = []
|
|
||||||
start_index = None
|
|
||||||
num_tokens = 0
|
|
||||||
|
|
||||||
# Discover all runs of zeros.
|
|
||||||
for idx, token in enumerate(tokens):
|
|
||||||
if token == '0':
|
|
||||||
if start_index is None:
|
|
||||||
start_index = idx
|
|
||||||
num_tokens += 1
|
|
||||||
else:
|
|
||||||
if num_tokens > 1:
|
|
||||||
positions.append((num_tokens, start_index))
|
|
||||||
start_index = None
|
|
||||||
num_tokens = 0
|
|
||||||
|
|
||||||
new_tokens.append(token)
|
|
||||||
|
|
||||||
# Store any position not saved before loop exit.
|
|
||||||
if num_tokens > 1:
|
|
||||||
positions.append((num_tokens, start_index))
|
|
||||||
|
|
||||||
# Replace first longest run with an empty string.
|
|
||||||
if len(positions) != 0:
|
|
||||||
# Locate longest, left-most run of zeros.
|
|
||||||
positions.sort(key=lambda x: x[1])
|
|
||||||
best_position = positions[0]
|
|
||||||
for position in positions:
|
|
||||||
if position[0] > best_position[0]:
|
|
||||||
best_position = position
|
|
||||||
# Replace chosen zero run.
|
|
||||||
(length, start_idx) = best_position
|
|
||||||
new_tokens = new_tokens[0:start_idx] + [''] + new_tokens[start_idx + length:]
|
|
||||||
|
|
||||||
# Add start and end blanks so join creates '::'.
|
|
||||||
if new_tokens[0] == '':
|
|
||||||
new_tokens.insert(0, '')
|
|
||||||
|
|
||||||
if new_tokens[-1] == '':
|
|
||||||
new_tokens.append('')
|
|
||||||
|
|
||||||
return new_tokens
|
|
||||||
|
|
||||||
|
|
||||||
def inet_ntop(af, packed_ip):
|
|
||||||
"""Convert an packed IP address of the given family to string format."""
|
|
||||||
if af == AF_INET:
|
|
||||||
# IPv4.
|
|
||||||
return inet_ntoa(packed_ip)
|
|
||||||
elif af == AF_INET6:
|
|
||||||
# IPv6.
|
|
||||||
if len(packed_ip) != 16 or not _is_str(packed_ip):
|
|
||||||
raise ValueError('invalid length of packed IP address string')
|
|
||||||
|
|
||||||
tokens = ['%x' % i for i in _unpack('>8H', packed_ip)]
|
|
||||||
|
|
||||||
# Convert packed address to an integer value.
|
|
||||||
words = list(_unpack('>8H', packed_ip))
|
|
||||||
int_val = 0
|
|
||||||
for i, num in enumerate(reversed(words)):
|
|
||||||
word = num
|
|
||||||
word = word << 16 * i
|
|
||||||
int_val = int_val | word
|
|
||||||
|
|
||||||
if 0xffff < int_val <= 0xffffffff or int_val >> 32 == 0xffff:
|
|
||||||
# IPv4 compatible / mapped IPv6.
|
|
||||||
packed_ipv4 = _pack('>2H', *[int(i, 16) for i in tokens[-2:]])
|
|
||||||
ipv4_str = inet_ntoa(packed_ipv4)
|
|
||||||
tokens = tokens[0:-2] + [ipv4_str]
|
|
||||||
|
|
||||||
return ':'.join(_compact_ipv6_tokens(tokens))
|
|
||||||
else:
|
|
||||||
raise ValueError('unknown address family %d' % af)
|
|
||||||
|
|
||||||
|
|
||||||
def _inet_pton_af_inet(ip_string):
|
|
||||||
"""
|
|
||||||
Convert an IP address in string format (123.45.67.89) to the 32-bit packed
|
|
||||||
binary format used in low-level network functions. Differs from inet_aton
|
|
||||||
by only support decimal octets. Using octal or hexadecimal values will
|
|
||||||
raise a ValueError exception.
|
|
||||||
"""
|
|
||||||
#TODO: optimise this ... use inet_aton with mods if available ...
|
|
||||||
if _is_str(ip_string):
|
|
||||||
invalid_addr = ValueError('illegal IP address string %r' % ip_string)
|
|
||||||
# Support for hexadecimal and octal octets.
|
|
||||||
tokens = ip_string.split('.')
|
|
||||||
|
|
||||||
# Pack octets.
|
|
||||||
if len(tokens) == 4:
|
|
||||||
words = []
|
|
||||||
for token in tokens:
|
|
||||||
if token.startswith('0x') or (token.startswith('0') and len(token) > 1):
|
|
||||||
raise invalid_addr
|
|
||||||
try:
|
|
||||||
octet = int(token)
|
|
||||||
except ValueError:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
if (octet >> 8) != 0:
|
|
||||||
raise invalid_addr
|
|
||||||
words.append(_pack('B', octet))
|
|
||||||
return _bytes_join(words)
|
|
||||||
else:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
raise ValueError('argument should be a string, not %s' % type(ip_string))
|
|
||||||
|
|
||||||
|
|
||||||
def inet_pton(af, ip_string):
|
|
||||||
"""
|
|
||||||
Convert an IP address from string format to a packed string suitable for
|
|
||||||
use with low-level network functions.
|
|
||||||
"""
|
|
||||||
if af == AF_INET:
|
|
||||||
# IPv4.
|
|
||||||
return _inet_pton_af_inet(ip_string)
|
|
||||||
elif af == AF_INET6:
|
|
||||||
invalid_addr = ValueError('illegal IP address string %r' % ip_string)
|
|
||||||
# IPv6.
|
|
||||||
values = []
|
|
||||||
|
|
||||||
if not _is_str(ip_string):
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
if 'x' in ip_string:
|
|
||||||
# Don't accept hextets with the 0x prefix.
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
if '::' in ip_string:
|
|
||||||
if ip_string == '::':
|
|
||||||
# Unspecified address.
|
|
||||||
return '\x00'.encode() * 16
|
|
||||||
# IPv6 compact mode.
|
|
||||||
try:
|
|
||||||
prefix, suffix = ip_string.split('::')
|
|
||||||
except ValueError:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
l_prefix = []
|
|
||||||
l_suffix = []
|
|
||||||
|
|
||||||
if prefix != '':
|
|
||||||
l_prefix = prefix.split(':')
|
|
||||||
|
|
||||||
if suffix != '':
|
|
||||||
l_suffix = suffix.split(':')
|
|
||||||
|
|
||||||
# IPv6 compact IPv4 compatibility mode.
|
|
||||||
if len(l_suffix) and '.' in l_suffix[-1]:
|
|
||||||
ipv4_str = _inet_pton_af_inet(l_suffix.pop())
|
|
||||||
l_suffix.append('%x' % _unpack('>H', ipv4_str[0:2])[0])
|
|
||||||
l_suffix.append('%x' % _unpack('>H', ipv4_str[2:4])[0])
|
|
||||||
|
|
||||||
token_count = len(l_prefix) + len(l_suffix)
|
|
||||||
|
|
||||||
if not 0 <= token_count <= 8 - 1:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
gap_size = 8 - ( len(l_prefix) + len(l_suffix) )
|
|
||||||
|
|
||||||
values = (
|
|
||||||
[_pack('>H', int(i, 16)) for i in l_prefix] +
|
|
||||||
['\x00\x00'.encode() for i in range(gap_size)] +
|
|
||||||
[_pack('>H', int(i, 16)) for i in l_suffix]
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
for token in l_prefix + l_suffix:
|
|
||||||
word = int(token, 16)
|
|
||||||
if not 0 <= word <= 0xffff:
|
|
||||||
raise invalid_addr
|
|
||||||
except ValueError:
|
|
||||||
raise invalid_addr
|
|
||||||
else:
|
|
||||||
# IPv6 verbose mode.
|
|
||||||
if ':' in ip_string:
|
|
||||||
tokens = ip_string.split(':')
|
|
||||||
|
|
||||||
if '.' in ip_string:
|
|
||||||
ipv6_prefix = tokens[:-1]
|
|
||||||
if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
if ipv6_prefix[-1].lower() not in ('0', 'ffff'):
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
# IPv6 verbose IPv4 compatibility mode.
|
|
||||||
if len(tokens) != 7:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
ipv4_str = _inet_pton_af_inet(tokens.pop())
|
|
||||||
tokens.append('%x' % _unpack('>H', ipv4_str[0:2])[0])
|
|
||||||
tokens.append('%x' % _unpack('>H', ipv4_str[2:4])[0])
|
|
||||||
|
|
||||||
values = [_pack('>H', int(i, 16)) for i in tokens]
|
|
||||||
else:
|
|
||||||
# IPv6 verbose mode.
|
|
||||||
if len(tokens) != 8:
|
|
||||||
raise invalid_addr
|
|
||||||
try:
|
|
||||||
tokens = [int(token, 16) for token in tokens]
|
|
||||||
for token in tokens:
|
|
||||||
if not 0 <= token <= 0xffff:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
values = [_pack('>H', i) for i in tokens]
|
|
||||||
else:
|
|
||||||
raise invalid_addr
|
|
||||||
|
|
||||||
return _bytes_join(values)
|
|
||||||
else:
|
|
||||||
raise ValueError('Unknown address family %d' % af)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,312 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Routines and classes for supporting and expressing IP address ranges using a
|
|
||||||
glob style syntax.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from netaddr.core import AddrFormatError, AddrConversionError
|
|
||||||
from netaddr.ip import IPRange, IPAddress, IPNetwork, iprange_to_cidrs
|
|
||||||
from netaddr.compat import _is_str
|
|
||||||
|
|
||||||
|
|
||||||
def valid_glob(ipglob):
|
|
||||||
"""
|
|
||||||
:param ipglob: An IP address range in a glob-style format.
|
|
||||||
|
|
||||||
:return: ``True`` if IP range glob is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
#TODO: Add support for abbreviated ipglobs.
|
|
||||||
#TODO: e.g. 192.0.*.* == 192.0.*
|
|
||||||
#TODO: *.*.*.* == *
|
|
||||||
#TODO: Add strict flag to enable verbose ipglob checking.
|
|
||||||
if not _is_str(ipglob):
|
|
||||||
return False
|
|
||||||
|
|
||||||
seen_hyphen = False
|
|
||||||
seen_asterisk = False
|
|
||||||
|
|
||||||
octets = ipglob.split('.')
|
|
||||||
|
|
||||||
if len(octets) != 4:
|
|
||||||
return False
|
|
||||||
|
|
||||||
for octet in octets:
|
|
||||||
if '-' in octet:
|
|
||||||
if seen_hyphen:
|
|
||||||
return False
|
|
||||||
seen_hyphen = True
|
|
||||||
if seen_asterisk:
|
|
||||||
# Asterisks cannot precede hyphenated octets.
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
(octet1, octet2) = [int(i) for i in octet.split('-')]
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
if octet1 >= octet2:
|
|
||||||
return False
|
|
||||||
if not 0 <= octet1 <= 254:
|
|
||||||
return False
|
|
||||||
if not 1 <= octet2 <= 255:
|
|
||||||
return False
|
|
||||||
elif octet == '*':
|
|
||||||
seen_asterisk = True
|
|
||||||
else:
|
|
||||||
if seen_hyphen is True:
|
|
||||||
return False
|
|
||||||
if seen_asterisk is True:
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
if not 0 <= int(octet) <= 255:
|
|
||||||
return False
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def glob_to_iptuple(ipglob):
|
|
||||||
"""
|
|
||||||
A function that accepts a glob-style IP range and returns the component
|
|
||||||
lower and upper bound IP address.
|
|
||||||
|
|
||||||
:param ipglob: an IP address range in a glob-style format.
|
|
||||||
|
|
||||||
:return: a tuple contain lower and upper bound IP objects.
|
|
||||||
"""
|
|
||||||
if not valid_glob(ipglob):
|
|
||||||
raise AddrFormatError('not a recognised IP glob range: %r!' % (ipglob,))
|
|
||||||
|
|
||||||
start_tokens = []
|
|
||||||
end_tokens = []
|
|
||||||
|
|
||||||
for octet in ipglob.split('.'):
|
|
||||||
if '-' in octet:
|
|
||||||
tokens = octet.split('-')
|
|
||||||
start_tokens.append(tokens[0])
|
|
||||||
end_tokens.append(tokens[1])
|
|
||||||
elif octet == '*':
|
|
||||||
start_tokens.append('0')
|
|
||||||
end_tokens.append('255')
|
|
||||||
else:
|
|
||||||
start_tokens.append(octet)
|
|
||||||
end_tokens.append(octet)
|
|
||||||
|
|
||||||
return IPAddress('.'.join(start_tokens)), IPAddress('.'.join(end_tokens))
|
|
||||||
|
|
||||||
|
|
||||||
def glob_to_iprange(ipglob):
|
|
||||||
"""
|
|
||||||
A function that accepts a glob-style IP range and returns the equivalent
|
|
||||||
IP range.
|
|
||||||
|
|
||||||
:param ipglob: an IP address range in a glob-style format.
|
|
||||||
|
|
||||||
:return: an IPRange object.
|
|
||||||
"""
|
|
||||||
if not valid_glob(ipglob):
|
|
||||||
raise AddrFormatError('not a recognised IP glob range: %r!' % (ipglob,))
|
|
||||||
|
|
||||||
start_tokens = []
|
|
||||||
end_tokens = []
|
|
||||||
|
|
||||||
for octet in ipglob.split('.'):
|
|
||||||
if '-' in octet:
|
|
||||||
tokens = octet.split('-')
|
|
||||||
start_tokens.append(tokens[0])
|
|
||||||
end_tokens.append(tokens[1])
|
|
||||||
elif octet == '*':
|
|
||||||
start_tokens.append('0')
|
|
||||||
end_tokens.append('255')
|
|
||||||
else:
|
|
||||||
start_tokens.append(octet)
|
|
||||||
end_tokens.append(octet)
|
|
||||||
|
|
||||||
return IPRange('.'.join(start_tokens), '.'.join(end_tokens))
|
|
||||||
|
|
||||||
|
|
||||||
def iprange_to_globs(start, end):
|
|
||||||
"""
|
|
||||||
A function that accepts an arbitrary start and end IP address or subnet
|
|
||||||
and returns one or more glob-style IP ranges.
|
|
||||||
|
|
||||||
:param start: the start IP address or subnet.
|
|
||||||
|
|
||||||
:param end: the end IP address or subnet.
|
|
||||||
|
|
||||||
:return: a list containing one or more IP globs.
|
|
||||||
"""
|
|
||||||
start = IPAddress(start)
|
|
||||||
end = IPAddress(end)
|
|
||||||
|
|
||||||
if start.version != 4 and end.version != 4:
|
|
||||||
raise AddrConversionError('IP glob ranges only support IPv4!')
|
|
||||||
|
|
||||||
def _iprange_to_glob(lb, ub):
|
|
||||||
# Internal function to process individual IP globs.
|
|
||||||
t1 = [int(_) for _ in str(lb).split('.')]
|
|
||||||
t2 = [int(_) for _ in str(ub).split('.')]
|
|
||||||
|
|
||||||
tokens = []
|
|
||||||
|
|
||||||
seen_hyphen = False
|
|
||||||
seen_asterisk = False
|
|
||||||
|
|
||||||
for i in range(4):
|
|
||||||
if t1[i] == t2[i]:
|
|
||||||
# A normal octet.
|
|
||||||
tokens.append(str(t1[i]))
|
|
||||||
elif (t1[i] == 0) and (t2[i] == 255):
|
|
||||||
# An asterisk octet.
|
|
||||||
tokens.append('*')
|
|
||||||
seen_asterisk = True
|
|
||||||
else:
|
|
||||||
# Create a hyphenated octet - only one allowed per IP glob.
|
|
||||||
if not seen_asterisk:
|
|
||||||
if not seen_hyphen:
|
|
||||||
tokens.append('%s-%s' % (t1[i], t2[i]))
|
|
||||||
seen_hyphen = True
|
|
||||||
else:
|
|
||||||
raise AddrConversionError(
|
|
||||||
'only 1 hyphenated octet per IP glob allowed!')
|
|
||||||
else:
|
|
||||||
raise AddrConversionError(
|
|
||||||
"asterisks are not allowed before hyphenated octets!")
|
|
||||||
|
|
||||||
return '.'.join(tokens)
|
|
||||||
|
|
||||||
globs = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
# IP range can be represented by a single glob.
|
|
||||||
ipglob = _iprange_to_glob(start, end)
|
|
||||||
if not valid_glob(ipglob):
|
|
||||||
#TODO: this is a workaround, it is produces non-optimal but valid
|
|
||||||
#TODO: glob conversions. Fix inner function so that is always
|
|
||||||
#TODO: produces a valid glob.
|
|
||||||
raise AddrConversionError('invalid ip glob created')
|
|
||||||
globs.append(ipglob)
|
|
||||||
except AddrConversionError:
|
|
||||||
# Break IP range up into CIDRs before conversion to globs.
|
|
||||||
#
|
|
||||||
#TODO: this is still not completely optimised but is good enough
|
|
||||||
#TODO: for the moment.
|
|
||||||
#
|
|
||||||
for cidr in iprange_to_cidrs(start, end):
|
|
||||||
ipglob = _iprange_to_glob(cidr[0], cidr[-1])
|
|
||||||
globs.append(ipglob)
|
|
||||||
|
|
||||||
return globs
|
|
||||||
|
|
||||||
|
|
||||||
def glob_to_cidrs(ipglob):
|
|
||||||
"""
|
|
||||||
A function that accepts a glob-style IP range and returns a list of one
|
|
||||||
or more IP CIDRs that exactly matches it.
|
|
||||||
|
|
||||||
:param ipglob: an IP address range in a glob-style format.
|
|
||||||
|
|
||||||
:return: a list of one or more IP objects.
|
|
||||||
"""
|
|
||||||
return iprange_to_cidrs(*glob_to_iptuple(ipglob))
|
|
||||||
|
|
||||||
|
|
||||||
def cidr_to_glob(cidr):
|
|
||||||
"""
|
|
||||||
A function that accepts an IP subnet in a glob-style format and returns
|
|
||||||
a list of CIDR subnets that exactly matches the specified glob.
|
|
||||||
|
|
||||||
:param cidr: an IP object CIDR subnet.
|
|
||||||
|
|
||||||
:return: a list of one or more IP addresses and subnets.
|
|
||||||
"""
|
|
||||||
ip = IPNetwork(cidr)
|
|
||||||
globs = iprange_to_globs(ip[0], ip[-1])
|
|
||||||
if len(globs) != 1:
|
|
||||||
# There should only ever be a one to one mapping between a CIDR and
|
|
||||||
# an IP glob range.
|
|
||||||
raise AddrConversionError('bad CIDR to IP glob conversion!')
|
|
||||||
return globs[0]
|
|
||||||
|
|
||||||
|
|
||||||
class IPGlob(IPRange):
|
|
||||||
"""
|
|
||||||
Represents an IP address range using a glob-style syntax ``x.x.x-y.*``
|
|
||||||
|
|
||||||
Individual octets can be represented using the following shortcuts :
|
|
||||||
|
|
||||||
1. ``*`` - the asterisk octet (represents values ``0`` through ``255``)
|
|
||||||
2. ``x-y`` - the hyphenated octet (represents values ``x`` through ``y``)
|
|
||||||
|
|
||||||
A few basic rules also apply :
|
|
||||||
|
|
||||||
1. ``x`` must always be less than ``y``, therefore :
|
|
||||||
|
|
||||||
- ``x`` can only be ``0`` through ``254``
|
|
||||||
- ``y`` can only be ``1`` through ``255``
|
|
||||||
|
|
||||||
2. only one hyphenated octet per IP glob is allowed
|
|
||||||
3. only asterisks are permitted after a hyphenated octet
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| IP glob | Description |
|
|
||||||
+==================+==============================+
|
|
||||||
| ``192.0.2.1`` | a single address |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| ``192.0.2.0-31`` | 32 addresses |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| ``192.0.2.*`` | 256 addresses |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| ``192.0.2-3.*`` | 512 addresses |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| ``192.0-1.*.*`` | 131,072 addresses |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
| ``*.*.*.*`` | the whole IPv4 address space |
|
|
||||||
+------------------+------------------------------+
|
|
||||||
|
|
||||||
.. note :: \
|
|
||||||
IP glob ranges are not directly equivalent to CIDR blocks. \
|
|
||||||
They can represent address ranges that do not fall on strict bit mask \
|
|
||||||
boundaries. They are suitable for use in configuration files, being \
|
|
||||||
more obvious and readable than their CIDR counterparts, especially for \
|
|
||||||
admins and end users with little or no networking knowledge or \
|
|
||||||
experience. All CIDR addresses can always be represented as IP globs \
|
|
||||||
but the reverse is not always true.
|
|
||||||
"""
|
|
||||||
__slots__ = ('_glob',)
|
|
||||||
|
|
||||||
def __init__(self, ipglob):
|
|
||||||
(start, end) = glob_to_iptuple(ipglob)
|
|
||||||
super(IPGlob, self).__init__(start, end)
|
|
||||||
self.glob = iprange_to_globs(self._start, self._end)[0]
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
""":return: Pickled state of an `IPGlob` object."""
|
|
||||||
return super(IPGlob, self).__getstate__()
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
""":param state: data used to unpickle a pickled `IPGlob` object."""
|
|
||||||
super(IPGlob, self).__setstate__(state)
|
|
||||||
self.glob = iprange_to_globs(self._start, self._end)[0]
|
|
||||||
|
|
||||||
def _get_glob(self):
|
|
||||||
return self._glob
|
|
||||||
|
|
||||||
def _set_glob(self, ipglob):
|
|
||||||
(self._start, self._end) = glob_to_iptuple(ipglob)
|
|
||||||
self._glob = iprange_to_globs(self._start, self._end)[0]
|
|
||||||
|
|
||||||
glob = property(_get_glob, _set_glob, None,
|
|
||||||
'an arbitrary IP address range in glob format.')
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
""":return: IP glob in common representational format."""
|
|
||||||
return "%s" % self.glob
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""":return: Python statement to create an equivalent object"""
|
|
||||||
return "%s('%s')" % (self.__class__.__name__, self.glob)
|
|
@ -1,448 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# DISCLAIMER
|
|
||||||
#
|
|
||||||
# netaddr is not sponsored nor endorsed by IANA.
|
|
||||||
#
|
|
||||||
# Use of data from IANA (Internet Assigned Numbers Authority) is subject to
|
|
||||||
# copyright and is provided with prior written permission.
|
|
||||||
#
|
|
||||||
# IANA data files included with netaddr are not modified in any way but are
|
|
||||||
# parsed and made available to end users through an API.
|
|
||||||
#
|
|
||||||
# See README file and source code for URLs to latest copies of the relevant
|
|
||||||
# files.
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Routines for accessing data published by IANA (Internet Assigned Numbers
|
|
||||||
Authority).
|
|
||||||
|
|
||||||
More details can be found at the following URLs :-
|
|
||||||
|
|
||||||
- IANA Home Page - http://www.iana.org/
|
|
||||||
- IEEE Protocols Information Home Page - http://www.iana.org/protocols/
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys as _sys
|
|
||||||
from xml.sax import make_parser, handler
|
|
||||||
|
|
||||||
from netaddr.core import Publisher, Subscriber
|
|
||||||
from netaddr.ip import IPAddress, IPNetwork, IPRange, cidr_abbrev_to_verbose
|
|
||||||
from netaddr.compat import _dict_items, _callable, _importlib_resources
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#: Topic based lookup dictionary for IANA information.
|
|
||||||
IANA_INFO = {
|
|
||||||
'IPv4': {},
|
|
||||||
'IPv6': {},
|
|
||||||
'IPv6_unicast': {},
|
|
||||||
'multicast': {},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class SaxRecordParser(handler.ContentHandler):
|
|
||||||
def __init__(self, callback=None):
|
|
||||||
self._level = 0
|
|
||||||
self._is_active = False
|
|
||||||
self._record = None
|
|
||||||
self._tag_level = None
|
|
||||||
self._tag_payload = None
|
|
||||||
self._tag_feeding = None
|
|
||||||
self._callback = callback
|
|
||||||
|
|
||||||
def startElement(self, name, attrs):
|
|
||||||
self._level += 1
|
|
||||||
|
|
||||||
if self._is_active is False:
|
|
||||||
if name == 'record':
|
|
||||||
self._is_active = True
|
|
||||||
self._tag_level = self._level
|
|
||||||
self._record = {}
|
|
||||||
if 'date' in attrs:
|
|
||||||
self._record['date'] = attrs['date']
|
|
||||||
elif self._level == self._tag_level + 1:
|
|
||||||
if name == 'xref':
|
|
||||||
if 'type' in attrs and 'data' in attrs:
|
|
||||||
l = self._record.setdefault(attrs['type'], [])
|
|
||||||
l.append(attrs['data'])
|
|
||||||
else:
|
|
||||||
self._tag_payload = []
|
|
||||||
self._tag_feeding = True
|
|
||||||
else:
|
|
||||||
self._tag_feeding = False
|
|
||||||
|
|
||||||
def endElement(self, name):
|
|
||||||
if self._is_active is True:
|
|
||||||
if name == 'record' and self._tag_level == self._level:
|
|
||||||
self._is_active = False
|
|
||||||
self._tag_level = None
|
|
||||||
if _callable(self._callback):
|
|
||||||
self._callback(self._record)
|
|
||||||
self._record = None
|
|
||||||
elif self._level == self._tag_level + 1:
|
|
||||||
if name != 'xref':
|
|
||||||
self._record[name] = ''.join(self._tag_payload)
|
|
||||||
self._tag_payload = None
|
|
||||||
self._tag_feeding = False
|
|
||||||
|
|
||||||
self._level -= 1
|
|
||||||
|
|
||||||
def characters(self, content):
|
|
||||||
if self._tag_feeding is True:
|
|
||||||
self._tag_payload.append(content)
|
|
||||||
|
|
||||||
|
|
||||||
class XMLRecordParser(Publisher):
|
|
||||||
"""
|
|
||||||
A configurable Parser that understands how to parse XML based records.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, fh, **kwargs):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
fh - a valid, open file handle to XML based record data.
|
|
||||||
"""
|
|
||||||
super(XMLRecordParser, self).__init__()
|
|
||||||
|
|
||||||
self.xmlparser = make_parser()
|
|
||||||
self.xmlparser.setContentHandler(SaxRecordParser(self.consume_record))
|
|
||||||
|
|
||||||
self.fh = fh
|
|
||||||
|
|
||||||
self.__dict__.update(kwargs)
|
|
||||||
|
|
||||||
def process_record(self, rec):
|
|
||||||
"""
|
|
||||||
This is the callback method invoked for every record. It is usually
|
|
||||||
over-ridden by base classes to provide specific record-based logic.
|
|
||||||
|
|
||||||
Any record can be vetoed (not passed to registered Subscriber objects)
|
|
||||||
by simply returning None.
|
|
||||||
"""
|
|
||||||
return rec
|
|
||||||
|
|
||||||
def consume_record(self, rec):
|
|
||||||
record = self.process_record(rec)
|
|
||||||
if record is not None:
|
|
||||||
self.notify(record)
|
|
||||||
|
|
||||||
def parse(self):
|
|
||||||
"""
|
|
||||||
Parse and normalises records, notifying registered subscribers with
|
|
||||||
record data as it is encountered.
|
|
||||||
"""
|
|
||||||
self.xmlparser.parse(self.fh)
|
|
||||||
|
|
||||||
|
|
||||||
class IPv4Parser(XMLRecordParser):
|
|
||||||
"""
|
|
||||||
A XMLRecordParser that understands how to parse and retrieve data records
|
|
||||||
from the IANA IPv4 address space file.
|
|
||||||
|
|
||||||
It can be found online here :-
|
|
||||||
|
|
||||||
- http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xml
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, fh, **kwargs):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
fh - a valid, open file handle to an IANA IPv4 address space file.
|
|
||||||
|
|
||||||
kwargs - additional parser options.
|
|
||||||
"""
|
|
||||||
super(IPv4Parser, self).__init__(fh)
|
|
||||||
|
|
||||||
def process_record(self, rec):
|
|
||||||
"""
|
|
||||||
Callback method invoked for every record.
|
|
||||||
|
|
||||||
See base class method for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
record = {}
|
|
||||||
for key in ('prefix', 'designation', 'date', 'whois', 'status'):
|
|
||||||
record[key] = str(rec.get(key, '')).strip()
|
|
||||||
|
|
||||||
# Strip leading zeros from octet.
|
|
||||||
if '/' in record['prefix']:
|
|
||||||
(octet, prefix) = record['prefix'].split('/')
|
|
||||||
record['prefix'] = '%d/%d' % (int(octet), int(prefix))
|
|
||||||
|
|
||||||
record['status'] = record['status'].capitalize()
|
|
||||||
|
|
||||||
return record
|
|
||||||
|
|
||||||
|
|
||||||
class IPv6Parser(XMLRecordParser):
|
|
||||||
"""
|
|
||||||
A XMLRecordParser that understands how to parse and retrieve data records
|
|
||||||
from the IANA IPv6 address space file.
|
|
||||||
|
|
||||||
It can be found online here :-
|
|
||||||
|
|
||||||
- http://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.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(IPv6Parser, self).__init__(fh)
|
|
||||||
|
|
||||||
def process_record(self, rec):
|
|
||||||
"""
|
|
||||||
Callback method invoked for every record.
|
|
||||||
|
|
||||||
See base class method for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
record = {
|
|
||||||
'prefix': str(rec.get('prefix', '')).strip(),
|
|
||||||
'allocation': str(rec.get('description', '')).strip(),
|
|
||||||
# HACK: -1 instead of 0 is a hacky hack to get 4291 instead of 3513 from
|
|
||||||
#
|
|
||||||
# <xref type="rfc" data="rfc3513"/> was later obsoleted by <xref type="rfc" data="rfc4291"/>
|
|
||||||
#
|
|
||||||
# I imagine there's no way to solve this in a general way, maybe we should start returning a list
|
|
||||||
# of RFC-s here?
|
|
||||||
'reference': str(rec.get('rfc', [''])[-1]).strip(),
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
allocation file.
|
|
||||||
|
|
||||||
It can be found online here :-
|
|
||||||
|
|
||||||
- http://www.iana.org/assignments/multicast-addresses/multicast-addresses.xml
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, fh, **kwargs):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
fh - a valid, open file handle to an IANA IPv4 multicast address
|
|
||||||
allocation file.
|
|
||||||
|
|
||||||
kwargs - additional parser options.
|
|
||||||
"""
|
|
||||||
super(MulticastParser, self).__init__(fh)
|
|
||||||
|
|
||||||
def normalise_addr(self, addr):
|
|
||||||
"""
|
|
||||||
Removes variations from address entries found in this particular file.
|
|
||||||
"""
|
|
||||||
if '-' in addr:
|
|
||||||
(a1, a2) = addr.split('-')
|
|
||||||
o1 = a1.strip().split('.')
|
|
||||||
o2 = a2.strip().split('.')
|
|
||||||
return '%s-%s' % ('.'.join([str(int(i)) for i in o1]),
|
|
||||||
'.'.join([str(int(i)) for i in o2]))
|
|
||||||
else:
|
|
||||||
o1 = addr.strip().split('.')
|
|
||||||
return '.'.join([str(int(i)) for i in o1])
|
|
||||||
|
|
||||||
def process_record(self, rec):
|
|
||||||
"""
|
|
||||||
Callback method invoked for every record.
|
|
||||||
|
|
||||||
See base class method for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if 'addr' in rec:
|
|
||||||
record = {
|
|
||||||
'address': self.normalise_addr(str(rec['addr'])),
|
|
||||||
'descr': str(rec.get('description', '')),
|
|
||||||
}
|
|
||||||
return record
|
|
||||||
|
|
||||||
|
|
||||||
class DictUpdater(Subscriber):
|
|
||||||
"""
|
|
||||||
Concrete Subscriber that inserts records received from a Publisher into a
|
|
||||||
dictionary.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, dct, topic, unique_key):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
dct - lookup dict or dict like object to insert records into.
|
|
||||||
|
|
||||||
topic - high-level category name of data to be processed.
|
|
||||||
|
|
||||||
unique_key - key name in data dict that uniquely identifies it.
|
|
||||||
"""
|
|
||||||
self.dct = dct
|
|
||||||
self.topic = topic
|
|
||||||
self.unique_key = unique_key
|
|
||||||
|
|
||||||
def update(self, data):
|
|
||||||
"""
|
|
||||||
Callback function used by Publisher to notify this Subscriber about
|
|
||||||
an update. Stores topic based information into dictionary passed to
|
|
||||||
constructor.
|
|
||||||
"""
|
|
||||||
data_id = data[self.unique_key]
|
|
||||||
|
|
||||||
if self.topic == 'IPv4':
|
|
||||||
cidr = IPNetwork(cidr_abbrev_to_verbose(data_id))
|
|
||||||
self.dct[cidr] = data
|
|
||||||
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:
|
|
||||||
# See if we can manage a single CIDR.
|
|
||||||
(first, last) = data_id.split('-')
|
|
||||||
iprange = IPRange(first, last)
|
|
||||||
cidrs = iprange.cidrs()
|
|
||||||
if len(cidrs) == 1:
|
|
||||||
iprange = cidrs[0]
|
|
||||||
else:
|
|
||||||
iprange = IPAddress(data_id)
|
|
||||||
self.dct[iprange] = data
|
|
||||||
|
|
||||||
|
|
||||||
def load_info():
|
|
||||||
"""
|
|
||||||
Parse and load internal IANA data lookups with the latest information from
|
|
||||||
data files.
|
|
||||||
"""
|
|
||||||
ipv4 = IPv4Parser(_importlib_resources.open_binary(__package__, 'ipv4-address-space.xml'))
|
|
||||||
ipv4.attach(DictUpdater(IANA_INFO['IPv4'], 'IPv4', 'prefix'))
|
|
||||||
ipv4.parse()
|
|
||||||
|
|
||||||
ipv6 = IPv6Parser(_importlib_resources.open_binary(__package__, 'ipv6-address-space.xml'))
|
|
||||||
ipv6.attach(DictUpdater(IANA_INFO['IPv6'], 'IPv6', 'prefix'))
|
|
||||||
ipv6.parse()
|
|
||||||
|
|
||||||
ipv6ua = IPv6UnicastParser(
|
|
||||||
_importlib_resources.open_binary(__package__, 'ipv6-unicast-address-assignments.xml'),
|
|
||||||
)
|
|
||||||
ipv6ua.attach(DictUpdater(IANA_INFO['IPv6_unicast'], 'IPv6_unicast', 'prefix'))
|
|
||||||
ipv6ua.parse()
|
|
||||||
|
|
||||||
mcast = MulticastParser(_importlib_resources.open_binary(__package__, 'multicast-addresses.xml'))
|
|
||||||
mcast.attach(DictUpdater(IANA_INFO['multicast'], 'multicast', 'address'))
|
|
||||||
mcast.parse()
|
|
||||||
|
|
||||||
|
|
||||||
def pprint_info(fh=None):
|
|
||||||
"""
|
|
||||||
Pretty prints IANA information to filehandle.
|
|
||||||
"""
|
|
||||||
if fh is None:
|
|
||||||
fh = _sys.stdout
|
|
||||||
|
|
||||||
for category in sorted(IANA_INFO):
|
|
||||||
fh.write('-' * len(category) + "\n")
|
|
||||||
fh.write(category + "\n")
|
|
||||||
fh.write('-' * len(category) + "\n")
|
|
||||||
ipranges = IANA_INFO[category]
|
|
||||||
for iprange in sorted(ipranges):
|
|
||||||
details = ipranges[iprange]
|
|
||||||
fh.write('%-45r' % (iprange) + details + "\n")
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
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):
|
|
||||||
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):
|
|
||||||
info.setdefault('IPv6', [])
|
|
||||||
info['IPv6'].append(record)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
return info
|
|
||||||
|
|
||||||
# On module import, read IANA data files and populate lookups dict.
|
|
||||||
load_info()
|
|
File diff suppressed because it is too large
Load Diff
@ -1,198 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<?xml-stylesheet type="text/xsl" href="ipv6-address-space.xsl"?>
|
|
||||||
<?oxygen RNGSchema="ipv6-address-space.rng" type="xml"?>
|
|
||||||
<registry xmlns="http://www.iana.org/assignments" id="ipv6-address-space">
|
|
||||||
<title>Internet Protocol Version 6 Address Space</title>
|
|
||||||
<updated>2019-09-13</updated>
|
|
||||||
<note>The IPv6 address management function was formally delegated to
|
|
||||||
IANA in December 1995 <xref type="rfc" data="rfc1881"/>. The registration procedure
|
|
||||||
was confirmed with the IETF Chair in March 2010.
|
|
||||||
|
|
||||||
As stated in RFC3513, IANA should limit its allocation of IPv6-unicast
|
|
||||||
address space to the range of addresses that start with binary value 001.
|
|
||||||
The rest of the global unicast address space (approximately 85% of the IPv6
|
|
||||||
address space) is reserved for future definition and use, and is not to be
|
|
||||||
assigned by IANA at this time.
|
|
||||||
|
|
||||||
While <xref type="rfc" data="rfc3513"/> was obsoleted by <xref type="rfc" data="rfc4291"/>,
|
|
||||||
the guidiance provided to IANA did not change regarding the allocation of IPv6
|
|
||||||
unicast addresses.
|
|
||||||
|
|
||||||
</note>
|
|
||||||
<registry id="ipv6-address-space-1">
|
|
||||||
<registration_rule>IESG Approval</registration_rule>
|
|
||||||
<record>
|
|
||||||
<prefix>0000::/8</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes>
|
|
||||||
<xref type="note" data="1"/>
|
|
||||||
<xref type="note" data="2"/>
|
|
||||||
<xref type="note" data="3"/>
|
|
||||||
<xref type="note" data="4"/>
|
|
||||||
<xref type="note" data="5"/>
|
|
||||||
<xref type="note" data="6"/>
|
|
||||||
</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>0100::/8</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes>0100::/64 reserved for Discard-Only Address Block <xref type="rfc" data="rfc6666"/>.
|
|
||||||
Complete registration details are found in <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>0200::/7</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc4048"/>
|
|
||||||
<notes>Deprecated as of December 2004 <xref type="rfc" data="rfc4048"/>.
|
|
||||||
Formerly an OSI NSAP-mapped prefix set <xref type="rfc" data="rfc4548"/>.</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>0400::/6</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>0800::/5</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>1000::/4</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>2000::/3</prefix>
|
|
||||||
<description>Global Unicast</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes>The IPv6 Unicast space encompasses the entire IPv6 address range
|
|
||||||
with the exception of ff00::/8, per <xref type="rfc" data="rfc4291"/>. IANA unicast address
|
|
||||||
assignments are currently limited to the IPv6 unicast address
|
|
||||||
range of 2000::/3. IANA assignments from this block are registered
|
|
||||||
in <xref type="registry" data="ipv6-unicast-address-assignments"/>.
|
|
||||||
<xref type="note" data="7"/>
|
|
||||||
<xref type="note" data="8"/>
|
|
||||||
<xref type="note" data="9"/>
|
|
||||||
<xref type="note" data="10"/>
|
|
||||||
<xref type="note" data="11"/>
|
|
||||||
<xref type="note" data="12"/>
|
|
||||||
<xref type="note" data="13"/>
|
|
||||||
<xref type="note" data="14"/>
|
|
||||||
<xref type="note" data="15"/>
|
|
||||||
</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>4000::/3</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>6000::/3</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>8000::/3</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>a000::/3</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>c000::/3</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>e000::/4</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>f000::/5</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>f800::/6</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>fc00::/7</prefix>
|
|
||||||
<description>Unique Local Unicast</description>
|
|
||||||
<xref type="rfc" data="rfc4193"/>
|
|
||||||
<notes>For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>fe00::/9</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes/>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>fe80::/10</prefix>
|
|
||||||
<description>Link-Scoped Unicast</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes>Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>fec0::/10</prefix>
|
|
||||||
<description>Reserved by IETF</description>
|
|
||||||
<xref type="rfc" data="rfc3879"/>
|
|
||||||
<notes>Deprecated by <xref type="rfc" data="rfc3879"/> in September 2004. Formerly a Site-Local scoped address prefix.</notes>
|
|
||||||
</record>
|
|
||||||
<record>
|
|
||||||
<prefix>ff00::/8</prefix>
|
|
||||||
<description>Multicast</description>
|
|
||||||
<xref type="rfc" data="rfc3513"/><xref type="rfc" data="rfc4291"/>
|
|
||||||
<notes>IANA assignments from this block are registered in <xref type="registry" data="ipv6-multicast-addresses"/>.</notes>
|
|
||||||
</record>
|
|
||||||
<footnote anchor="1">::1/128 reserved for Loopback Address <xref type="rfc" data="rfc4291"/>.
|
|
||||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="2">::/128 reserved for Unspecified Address <xref type="rfc" data="rfc4291"/>.
|
|
||||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="3">::ffff:0:0/96 reserved for IPv4-mapped Address <xref type="rfc" data="rfc4291"/>.
|
|
||||||
Reserved by protocol. For authoritative registration, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="4">0::/96 deprecated by <xref type="rfc" data="rfc4291"/>. Formerly defined as the "IPv4-compatible IPv6 address" prefix.</footnote>
|
|
||||||
<footnote anchor="5">The "Well Known Prefix" 64:ff9b::/96 is used in an algorithmic mapping between IPv4 to IPv6 addresses <xref type="rfc" data="rfc6052"/>.</footnote>
|
|
||||||
<footnote anchor="6">64:ff9b:1::/48 reserved for Local-Use IPv4/IPv6 Translation <xref type="rfc" data="rfc8215"/>.</footnote>
|
|
||||||
<footnote anchor="7">2001:0::/23 reserved for IETF Protocol Assignments <xref type="rfc" data="rfc2928"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="8">2001:0::/32 reserved for TEREDO <xref type="rfc" data="rfc4380"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="9">2001:2::/48 reserved for Benchmarking <xref type="rfc" data="rfc5180"/><xref type="rfc-errata" data="1752"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="10">2001:3::/32 reserved for AMT <xref type="rfc" data="rfc7450"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="11">2001:4:112::/48 reserved for AS112-v6 <xref type="rfc" data="rfc7535"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="12">2001:10::/28 deprecated (formerly ORCHID) <xref type="rfc" data="rfc4843"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="13">2001:20::/28 reserved for ORCHIDv2 <xref type="rfc" data="rfc7343"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="14">2001:db8::/32 reserved for Documentation <xref type="rfc" data="rfc3849"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
<footnote anchor="15">2002::/16 reserved for 6to4 <xref type="rfc" data="rfc3056"/>.
|
|
||||||
For complete registration details, see <xref type="registry" data="iana-ipv6-special-registry"/>.</footnote>
|
|
||||||
|
|
||||||
|
|
||||||
<people/>
|
|
||||||
</registry>
|
|
||||||
</registry>
|
|
@ -1,435 +0,0 @@
|
|||||||
<?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>2019-11-06</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="rfc3513"/> as the address block
|
|
||||||
defined by the prefix 2000::/3. <xref type="rfc" data="rfc3513"/> was later obsoleted by <xref type="rfc" data="rfc4291"/>. 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:1::1/128 is reserved for Port Control Protocol Anycast <xref type="rfc" data="rfc7723"/>.
|
|
||||||
2001:2::/48 is reserved for Benchmarking <xref type="rfc" data="rfc5180"/><xref type="rfc-errata" data="1752"/>.
|
|
||||||
2001:3::/32 is reserved for AMT <xref type="rfc" data="rfc7450"/>.
|
|
||||||
2001:4:112::/48 is reserved for AS112-v6 <xref type="rfc" data="rfc7535"/>.
|
|
||||||
2001:10::/28 is deprecated (previously ORCHID) <xref type="rfc" data="rfc4843"/>.
|
|
||||||
2001:20::/28 is reserved for ORCHIDv2 <xref type="rfc" data="rfc7343"/>.
|
|
||||||
2001:db8::/32 is 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="1999-07-01">
|
|
||||||
<prefix>2001:0200::/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-11-02">
|
|
||||||
<prefix>2001:0800::/22</prefix>
|
|
||||||
<description>RIPE NCC</description>
|
|
||||||
<whois>whois.ripe.net</whois>
|
|
||||||
<status>ALLOCATED</status>
|
|
||||||
<rdap>
|
|
||||||
<server>https://rdap.db.ripe.net/</server>
|
|
||||||
</rdap>
|
|
||||||
<notes>2001:0800::/23 was allocated on 2002-05-02. The more recent
|
|
||||||
allocation (2002-11-02) incorporates the previous allocation.</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-07-01">
|
|
||||||
<prefix>2001:1400::/22</prefix>
|
|
||||||
<description>RIPE NCC</description>
|
|
||||||
<whois>whois.ripe.net</whois>
|
|
||||||
<status>ALLOCATED</status>
|
|
||||||
<rdap>
|
|
||||||
<server>https://rdap.db.ripe.net/</server>
|
|
||||||
</rdap>
|
|
||||||
<notes>2001:1400::/23 was allocated on 2003-02-01. The more recent
|
|
||||||
allocation (2003-07-01) incorporates the previous allocation.</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="2019-03-12">
|
|
||||||
<prefix>2001:2000::/19</prefix>
|
|
||||||
<description>RIPE NCC</description>
|
|
||||||
<whois>whois.ripe.net</whois>
|
|
||||||
<status>ALLOCATED</status>
|
|
||||||
<rdap>
|
|
||||||
<server>https://rdap.db.ripe.net/</server>
|
|
||||||
</rdap>
|
|
||||||
<notes>2001:2000::/20, 2001:3000::/21, and 2001:3800::/22
|
|
||||||
were allocated on 2004-05-04. The more recent allocation
|
|
||||||
(2019-03-12) incorporates all these previous allocations.</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="2019-11-06">
|
|
||||||
<prefix>2630: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/>
|
|
||||||
</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="2019-06-05">
|
|
||||||
<prefix>2a10: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/>
|
|
||||||
</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>
|
|
File diff suppressed because it is too large
Load Diff
@ -1,117 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Routines for dealing with nmap-style IPv4 address ranges.
|
|
||||||
|
|
||||||
Based on nmap's Target Specification :-
|
|
||||||
|
|
||||||
http://nmap.org/book/man-target-specification.html
|
|
||||||
"""
|
|
||||||
|
|
||||||
from netaddr.core import AddrFormatError
|
|
||||||
from netaddr.ip import IPAddress, IPNetwork
|
|
||||||
from netaddr.compat import _iter_range, _is_str, _iter_next
|
|
||||||
|
|
||||||
|
|
||||||
def _nmap_octet_target_values(spec):
|
|
||||||
# Generates sequence of values for an individual octet as defined in the
|
|
||||||
# nmap Target Specification.
|
|
||||||
values = set()
|
|
||||||
|
|
||||||
for element in spec.split(','):
|
|
||||||
if '-' in element:
|
|
||||||
left, right = element.split('-', 1)
|
|
||||||
if not left:
|
|
||||||
left = 0
|
|
||||||
if not right:
|
|
||||||
right = 255
|
|
||||||
low = int(left)
|
|
||||||
high = int(right)
|
|
||||||
if not ((0 <= low <= 255) and (0 <= high <= 255)):
|
|
||||||
raise ValueError('octet value overflow for spec %s!' % (spec,))
|
|
||||||
if low > high:
|
|
||||||
raise ValueError('left side of hyphen must be <= right %r' % (element,))
|
|
||||||
for octet in _iter_range(low, high + 1):
|
|
||||||
values.add(octet)
|
|
||||||
else:
|
|
||||||
octet = int(element)
|
|
||||||
if not (0 <= octet <= 255):
|
|
||||||
raise ValueError('octet value overflow for spec %s!' % (spec,))
|
|
||||||
values.add(octet)
|
|
||||||
|
|
||||||
return sorted(values)
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_nmap_octet_ranges(nmap_target_spec):
|
|
||||||
# Generate 4 lists containing all octets defined by a given nmap Target
|
|
||||||
# specification.
|
|
||||||
if not _is_str(nmap_target_spec):
|
|
||||||
raise TypeError('string expected, not %s' % type(nmap_target_spec))
|
|
||||||
|
|
||||||
if not nmap_target_spec:
|
|
||||||
raise ValueError('nmap target specification cannot be blank!')
|
|
||||||
|
|
||||||
tokens = nmap_target_spec.split('.')
|
|
||||||
|
|
||||||
if len(tokens) != 4:
|
|
||||||
raise AddrFormatError('invalid nmap range: %s' % (nmap_target_spec,))
|
|
||||||
|
|
||||||
return (_nmap_octet_target_values(tokens[0]),
|
|
||||||
_nmap_octet_target_values(tokens[1]),
|
|
||||||
_nmap_octet_target_values(tokens[2]),
|
|
||||||
_nmap_octet_target_values(tokens[3]))
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_nmap_target_spec(target_spec):
|
|
||||||
if '/' in target_spec:
|
|
||||||
_, prefix = target_spec.split('/', 1)
|
|
||||||
if not (0 < int(prefix) < 33):
|
|
||||||
raise AddrFormatError('CIDR prefix expected, not %s' % (prefix,))
|
|
||||||
net = IPNetwork(target_spec)
|
|
||||||
if net.version != 4:
|
|
||||||
raise AddrFormatError('CIDR only support for IPv4!')
|
|
||||||
for ip in net:
|
|
||||||
yield ip
|
|
||||||
elif ':' in target_spec:
|
|
||||||
# nmap only currently supports IPv6 addresses without prefixes.
|
|
||||||
yield IPAddress(target_spec)
|
|
||||||
else:
|
|
||||||
octet_ranges = _generate_nmap_octet_ranges(target_spec)
|
|
||||||
for w in octet_ranges[0]:
|
|
||||||
for x in octet_ranges[1]:
|
|
||||||
for y in octet_ranges[2]:
|
|
||||||
for z in octet_ranges[3]:
|
|
||||||
yield IPAddress("%d.%d.%d.%d" % (w, x, y, z), 4)
|
|
||||||
|
|
||||||
|
|
||||||
def valid_nmap_range(target_spec):
|
|
||||||
"""
|
|
||||||
:param target_spec: an nmap-style IP range target specification.
|
|
||||||
|
|
||||||
:return: ``True`` if IP range target spec is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
_iter_next(_parse_nmap_target_spec(target_spec))
|
|
||||||
return True
|
|
||||||
except (TypeError, ValueError, AddrFormatError):
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def iter_nmap_range(*nmap_target_spec):
|
|
||||||
"""
|
|
||||||
An generator that yields IPAddress objects from defined by nmap target
|
|
||||||
specifications.
|
|
||||||
|
|
||||||
See https://nmap.org/book/man-target-specification.html for details.
|
|
||||||
|
|
||||||
:param *nmap_target_spec: one or more nmap IP range target specification.
|
|
||||||
|
|
||||||
:return: an iterator producing IPAddress objects for each IP in the target spec(s).
|
|
||||||
"""
|
|
||||||
for target_spec in nmap_target_spec:
|
|
||||||
for addr in _parse_nmap_target_spec(target_spec):
|
|
||||||
yield addr
|
|
@ -1,61 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""A basic implementation of RFC 1924 ;-)"""
|
|
||||||
|
|
||||||
from netaddr.core import AddrFormatError
|
|
||||||
from netaddr.ip import IPAddress
|
|
||||||
|
|
||||||
from netaddr.compat import _zip
|
|
||||||
|
|
||||||
|
|
||||||
def chr_range(low, high):
|
|
||||||
"""Returns all characters between low and high chars."""
|
|
||||||
return [chr(i) for i in range(ord(low), ord(high) + 1)]
|
|
||||||
|
|
||||||
#: Base 85 integer index to character lookup table.
|
|
||||||
BASE_85 = (
|
|
||||||
chr_range('0', '9') + chr_range('A', 'Z') +
|
|
||||||
chr_range('a', 'z') +
|
|
||||||
['!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>',
|
|
||||||
'?', '@', '^', '_', '`', '{', '|', '}', '~']
|
|
||||||
)
|
|
||||||
|
|
||||||
#: Base 85 digit to integer lookup table.
|
|
||||||
BASE_85_DICT = dict(_zip(BASE_85, range(0, 86)))
|
|
||||||
|
|
||||||
|
|
||||||
def ipv6_to_base85(addr):
|
|
||||||
"""Convert a regular IPv6 address to base 85."""
|
|
||||||
ip = IPAddress(addr)
|
|
||||||
int_val = int(ip)
|
|
||||||
|
|
||||||
remainder = []
|
|
||||||
while int_val > 0:
|
|
||||||
remainder.append(int_val % 85)
|
|
||||||
int_val //= 85
|
|
||||||
|
|
||||||
encoded = ''.join([BASE_85[w] for w in reversed(remainder)])
|
|
||||||
leading_zeroes = (20 - len(encoded)) * "0"
|
|
||||||
return leading_zeroes + encoded
|
|
||||||
|
|
||||||
|
|
||||||
def base85_to_ipv6(addr):
|
|
||||||
"""
|
|
||||||
Convert a base 85 IPv6 address to its hexadecimal format.
|
|
||||||
"""
|
|
||||||
tokens = list(addr)
|
|
||||||
|
|
||||||
if len(tokens) != 20:
|
|
||||||
raise AddrFormatError('Invalid base 85 IPv6 address: %r' % (addr,))
|
|
||||||
|
|
||||||
result = 0
|
|
||||||
for i, num in enumerate(reversed(tokens)):
|
|
||||||
num = BASE_85_DICT[num]
|
|
||||||
result += (num * 85 ** i)
|
|
||||||
|
|
||||||
ip = IPAddress(result, 6)
|
|
||||||
|
|
||||||
return str(ip)
|
|
@ -1,748 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""Set based operations for IP addresses and subnets."""
|
|
||||||
|
|
||||||
import itertools as _itertools
|
|
||||||
|
|
||||||
from netaddr.ip import (IPNetwork, IPAddress, IPRange, cidr_merge,
|
|
||||||
cidr_exclude, iprange_to_cidrs)
|
|
||||||
|
|
||||||
from netaddr.compat import _sys_maxint, _dict_keys, _int_type
|
|
||||||
|
|
||||||
|
|
||||||
def _subtract(supernet, subnets, subnet_idx, ranges):
|
|
||||||
"""Calculate IPSet([supernet]) - IPSet(subnets).
|
|
||||||
|
|
||||||
Assumptions: subnets is sorted, subnet_idx points to the first
|
|
||||||
element in subnets that is a subnet of supernet.
|
|
||||||
|
|
||||||
Results are appended to the ranges parameter as tuples of in format
|
|
||||||
(version, first, last). Return value is the first subnet_idx that
|
|
||||||
does not point to a subnet of supernet (or len(subnets) if all
|
|
||||||
subsequents items are a subnet of supernet).
|
|
||||||
"""
|
|
||||||
version = supernet._module.version
|
|
||||||
subnet = subnets[subnet_idx]
|
|
||||||
if subnet.first > supernet.first:
|
|
||||||
ranges.append((version, supernet.first, subnet.first - 1))
|
|
||||||
|
|
||||||
subnet_idx += 1
|
|
||||||
prev_subnet = subnet
|
|
||||||
while subnet_idx < len(subnets):
|
|
||||||
cur_subnet = subnets[subnet_idx]
|
|
||||||
|
|
||||||
if cur_subnet not in supernet:
|
|
||||||
break
|
|
||||||
if prev_subnet.last + 1 == cur_subnet.first:
|
|
||||||
# two adjacent, non-mergable IPNetworks
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
ranges.append((version, prev_subnet.last + 1, cur_subnet.first - 1))
|
|
||||||
|
|
||||||
subnet_idx += 1
|
|
||||||
prev_subnet = cur_subnet
|
|
||||||
|
|
||||||
first = prev_subnet.last + 1
|
|
||||||
last = supernet.last
|
|
||||||
if first <= last:
|
|
||||||
ranges.append((version, first, last))
|
|
||||||
|
|
||||||
return subnet_idx
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_merged_ranges(sorted_ranges):
|
|
||||||
"""Iterate over sorted_ranges, merging where possible
|
|
||||||
|
|
||||||
Sorted ranges must be a sorted iterable of (version, first, last) tuples.
|
|
||||||
Merging occurs for pairs like [(4, 10, 42), (4, 43, 100)] which is merged
|
|
||||||
into (4, 10, 100), and leads to return value
|
|
||||||
( IPAddress(10, 4), IPAddress(100, 4) ), which is suitable input for the
|
|
||||||
iprange_to_cidrs function.
|
|
||||||
"""
|
|
||||||
if not sorted_ranges:
|
|
||||||
return
|
|
||||||
|
|
||||||
current_version, current_start, current_stop = sorted_ranges[0]
|
|
||||||
|
|
||||||
for next_version, next_start, next_stop in sorted_ranges[1:]:
|
|
||||||
if next_start == current_stop + 1 and next_version == current_version:
|
|
||||||
# Can be merged.
|
|
||||||
current_stop = next_stop
|
|
||||||
continue
|
|
||||||
# Cannot be merged.
|
|
||||||
yield (IPAddress(current_start, current_version),
|
|
||||||
IPAddress(current_stop, current_version))
|
|
||||||
current_start = next_start
|
|
||||||
current_stop = next_stop
|
|
||||||
current_version = next_version
|
|
||||||
yield (IPAddress(current_start, current_version),
|
|
||||||
IPAddress(current_stop, current_version))
|
|
||||||
|
|
||||||
|
|
||||||
class IPSet(object):
|
|
||||||
"""
|
|
||||||
Represents an unordered collection (set) of unique IP addresses and
|
|
||||||
subnets.
|
|
||||||
|
|
||||||
"""
|
|
||||||
__slots__ = ('_cidrs', '__weakref__')
|
|
||||||
|
|
||||||
def __init__(self, iterable=None, flags=0):
|
|
||||||
"""
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
:param iterable: (optional) an iterable containing IP addresses,
|
|
||||||
subnets or ranges.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation
|
|
||||||
of the addr value. See the netaddr.core namespace documentation
|
|
||||||
for supported constant values.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if isinstance(iterable, IPNetwork):
|
|
||||||
self._cidrs = {iterable.cidr: True}
|
|
||||||
elif isinstance(iterable, IPRange):
|
|
||||||
self._cidrs = dict.fromkeys(
|
|
||||||
iprange_to_cidrs(iterable[0], iterable[-1]), True)
|
|
||||||
elif isinstance(iterable, IPSet):
|
|
||||||
self._cidrs = dict.fromkeys(iterable.iter_cidrs(), True)
|
|
||||||
else:
|
|
||||||
self._cidrs = {}
|
|
||||||
if iterable is not None:
|
|
||||||
mergeable = []
|
|
||||||
for addr in iterable:
|
|
||||||
if isinstance(addr, _int_type):
|
|
||||||
addr = IPAddress(addr, flags=flags)
|
|
||||||
mergeable.append(addr)
|
|
||||||
|
|
||||||
for cidr in cidr_merge(mergeable):
|
|
||||||
self._cidrs[cidr] = True
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
""":return: Pickled state of an ``IPSet`` object."""
|
|
||||||
return tuple([cidr.__getstate__() for cidr in self._cidrs])
|
|
||||||
|
|
||||||
def __setstate__(self, state):
|
|
||||||
"""
|
|
||||||
:param state: data used to unpickle a pickled ``IPSet`` object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self._cidrs = dict.fromkeys(
|
|
||||||
(IPNetwork((value, prefixlen), version=version)
|
|
||||||
for value, prefixlen, version in state),
|
|
||||||
True)
|
|
||||||
|
|
||||||
def _compact_single_network(self, added_network):
|
|
||||||
"""
|
|
||||||
Same as compact(), but assume that added_network is the only change and
|
|
||||||
that this IPSet was properly compacted before added_network was added.
|
|
||||||
This allows to perform compaction much faster. added_network must
|
|
||||||
already be present in self._cidrs.
|
|
||||||
"""
|
|
||||||
added_first = added_network.first
|
|
||||||
added_last = added_network.last
|
|
||||||
added_version = added_network.version
|
|
||||||
|
|
||||||
# Check for supernets and subnets of added_network.
|
|
||||||
if added_network._prefixlen == added_network._module.width:
|
|
||||||
# This is a single IP address, i.e. /32 for IPv4 or /128 for IPv6.
|
|
||||||
# It does not have any subnets, so we only need to check for its
|
|
||||||
# potential supernets.
|
|
||||||
for potential_supernet in added_network.supernet():
|
|
||||||
if potential_supernet in self._cidrs:
|
|
||||||
del self._cidrs[added_network]
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# IPNetworks from self._cidrs that are subnets of added_network.
|
|
||||||
to_remove = []
|
|
||||||
for cidr in self._cidrs:
|
|
||||||
if (cidr._module.version != added_version or cidr == added_network):
|
|
||||||
# We found added_network or some network of a different version.
|
|
||||||
continue
|
|
||||||
first = cidr.first
|
|
||||||
last = cidr.last
|
|
||||||
if first >= added_first and last <= added_last:
|
|
||||||
# cidr is a subnet of added_network. Remember to remove it.
|
|
||||||
to_remove.append(cidr)
|
|
||||||
elif first <= added_first and last >= added_last:
|
|
||||||
# cidr is a supernet of added_network. Remove added_network.
|
|
||||||
del self._cidrs[added_network]
|
|
||||||
# This IPSet was properly compacted before. Since added_network
|
|
||||||
# is removed now, it must again be properly compacted -> done.
|
|
||||||
assert (not to_remove)
|
|
||||||
return
|
|
||||||
for item in to_remove:
|
|
||||||
del self._cidrs[item]
|
|
||||||
|
|
||||||
# Check if added_network can be merged with another network.
|
|
||||||
|
|
||||||
# Note that merging can only happen between networks of the same
|
|
||||||
# prefixlen. This just leaves 2 candidates: The IPNetworks just before
|
|
||||||
# and just after the added_network.
|
|
||||||
# This can be reduced to 1 candidate: 10.0.0.0/24 and 10.0.1.0/24 can
|
|
||||||
# be merged into into 10.0.0.0/23. But 10.0.1.0/24 and 10.0.2.0/24
|
|
||||||
# cannot be merged. With only 1 candidate, we might as well make a
|
|
||||||
# dictionary lookup.
|
|
||||||
shift_width = added_network._module.width - added_network.prefixlen
|
|
||||||
while added_network.prefixlen != 0:
|
|
||||||
# figure out if the least significant bit of the network part is 0 or 1.
|
|
||||||
the_bit = (added_network._value >> shift_width) & 1
|
|
||||||
if the_bit:
|
|
||||||
candidate = added_network.previous()
|
|
||||||
else:
|
|
||||||
candidate = added_network.next()
|
|
||||||
|
|
||||||
if candidate not in self._cidrs:
|
|
||||||
# The only possible merge does not work -> merge done
|
|
||||||
return
|
|
||||||
# Remove added_network&candidate, add merged network.
|
|
||||||
del self._cidrs[candidate]
|
|
||||||
del self._cidrs[added_network]
|
|
||||||
added_network.prefixlen -= 1
|
|
||||||
# Be sure that we set the host bits to 0 when we move the prefixlen.
|
|
||||||
# Otherwise, adding 255.255.255.255/32 will result in a merged
|
|
||||||
# 255.255.255.255/24 network, but we want 255.255.255.0/24.
|
|
||||||
shift_width += 1
|
|
||||||
added_network._value = (added_network._value >> shift_width) << shift_width
|
|
||||||
self._cidrs[added_network] = True
|
|
||||||
|
|
||||||
def compact(self):
|
|
||||||
"""
|
|
||||||
Compact internal list of `IPNetwork` objects using a CIDR merge.
|
|
||||||
"""
|
|
||||||
cidrs = cidr_merge(self._cidrs)
|
|
||||||
self._cidrs = dict.fromkeys(cidrs, True)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
"""
|
|
||||||
Raises ``TypeError`` if this method is called.
|
|
||||||
|
|
||||||
.. note:: IPSet objects are not hashable and cannot be used as \
|
|
||||||
dictionary keys or as members of other sets. \
|
|
||||||
"""
|
|
||||||
raise TypeError('IP sets are unhashable!')
|
|
||||||
|
|
||||||
def __contains__(self, ip):
|
|
||||||
"""
|
|
||||||
:param ip: An IP address or subnet.
|
|
||||||
|
|
||||||
:return: ``True`` if IP address or subnet is a member of this IP set.
|
|
||||||
"""
|
|
||||||
# Iterating over self._cidrs is an O(n) operation: 1000 items in
|
|
||||||
# self._cidrs would mean 1000 loops. Iterating over all possible
|
|
||||||
# supernets loops at most 32 times for IPv4 or 128 times for IPv6,
|
|
||||||
# no matter how many CIDRs this object contains.
|
|
||||||
supernet = IPNetwork(ip)
|
|
||||||
if supernet in self._cidrs:
|
|
||||||
return True
|
|
||||||
while supernet._prefixlen:
|
|
||||||
supernet._prefixlen -= 1
|
|
||||||
if supernet in self._cidrs:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __nonzero__(self):
|
|
||||||
"""Return True if IPSet contains at least one IP, else False"""
|
|
||||||
return bool(self._cidrs)
|
|
||||||
|
|
||||||
__bool__ = __nonzero__ # Python 3.x.
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
"""
|
|
||||||
:return: an iterator over the IP addresses within this IP set.
|
|
||||||
"""
|
|
||||||
return _itertools.chain(*sorted(self._cidrs))
|
|
||||||
|
|
||||||
def iter_cidrs(self):
|
|
||||||
"""
|
|
||||||
:return: an iterator over individual IP subnets within this IP set.
|
|
||||||
"""
|
|
||||||
return sorted(self._cidrs)
|
|
||||||
|
|
||||||
def add(self, addr, flags=0):
|
|
||||||
"""
|
|
||||||
Adds an IP address or subnet or IPRange to this IP set. Has no effect if
|
|
||||||
it is already present.
|
|
||||||
|
|
||||||
Note that where possible the IP address or subnet is merged with other
|
|
||||||
members of the set to form more concise CIDR blocks.
|
|
||||||
|
|
||||||
:param addr: An IP address or subnet in either string or object form, or
|
|
||||||
an IPRange object.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation
|
|
||||||
of the addr value. See the netaddr.core namespace documentation
|
|
||||||
for supported constant values.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if isinstance(addr, IPRange):
|
|
||||||
new_cidrs = dict.fromkeys(
|
|
||||||
iprange_to_cidrs(addr[0], addr[-1]), True)
|
|
||||||
self._cidrs.update(new_cidrs)
|
|
||||||
self.compact()
|
|
||||||
return
|
|
||||||
if isinstance(addr, IPNetwork):
|
|
||||||
# Networks like 10.1.2.3/8 need to be normalized to 10.0.0.0/8
|
|
||||||
addr = addr.cidr
|
|
||||||
elif isinstance(addr, _int_type):
|
|
||||||
addr = IPNetwork(IPAddress(addr, flags=flags))
|
|
||||||
else:
|
|
||||||
addr = IPNetwork(addr)
|
|
||||||
|
|
||||||
self._cidrs[addr] = True
|
|
||||||
self._compact_single_network(addr)
|
|
||||||
|
|
||||||
def remove(self, addr, flags=0):
|
|
||||||
"""
|
|
||||||
Removes an IP address or subnet or IPRange from this IP set. Does
|
|
||||||
nothing if it is not already a member.
|
|
||||||
|
|
||||||
Note that this method behaves more like discard() found in regular
|
|
||||||
Python sets because it doesn't raise KeyError exceptions if the
|
|
||||||
IP address or subnet is question does not exist. It doesn't make sense
|
|
||||||
to fully emulate that behaviour here as IP sets contain groups of
|
|
||||||
individual IP addresses as individual set members using IPNetwork
|
|
||||||
objects.
|
|
||||||
|
|
||||||
:param addr: An IP address or subnet, or an IPRange.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation
|
|
||||||
of the addr value. See the netaddr.core namespace documentation
|
|
||||||
for supported constant values.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if isinstance(addr, IPRange):
|
|
||||||
cidrs = iprange_to_cidrs(addr[0], addr[-1])
|
|
||||||
for cidr in cidrs:
|
|
||||||
self.remove(cidr)
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(addr, _int_type):
|
|
||||||
addr = IPAddress(addr, flags=flags)
|
|
||||||
else:
|
|
||||||
addr = IPNetwork(addr)
|
|
||||||
|
|
||||||
# This add() is required for address blocks provided that are larger
|
|
||||||
# than blocks found within the set but have overlaps. e.g. :-
|
|
||||||
#
|
|
||||||
# >>> IPSet(['192.0.2.0/24']).remove('192.0.2.0/23')
|
|
||||||
# IPSet([])
|
|
||||||
#
|
|
||||||
self.add(addr)
|
|
||||||
|
|
||||||
remainder = None
|
|
||||||
matching_cidr = None
|
|
||||||
|
|
||||||
# Search for a matching CIDR and exclude IP from it.
|
|
||||||
for cidr in self._cidrs:
|
|
||||||
if addr in cidr:
|
|
||||||
remainder = cidr_exclude(cidr, addr)
|
|
||||||
matching_cidr = cidr
|
|
||||||
break
|
|
||||||
|
|
||||||
# Replace matching CIDR with remaining CIDR elements.
|
|
||||||
if remainder is not None:
|
|
||||||
del self._cidrs[matching_cidr]
|
|
||||||
for cidr in remainder:
|
|
||||||
self._cidrs[cidr] = True
|
|
||||||
# No call to self.compact() is needed. Removing an IPNetwork cannot
|
|
||||||
# create mergable networks.
|
|
||||||
|
|
||||||
def pop(self):
|
|
||||||
"""
|
|
||||||
Removes and returns an arbitrary IP address or subnet from this IP
|
|
||||||
set.
|
|
||||||
|
|
||||||
:return: An IP address or subnet.
|
|
||||||
"""
|
|
||||||
return self._cidrs.popitem()[0]
|
|
||||||
|
|
||||||
def isdisjoint(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: ``True`` if this IP set has no elements (IP addresses
|
|
||||||
or subnets) in common with other. Intersection *must* be an
|
|
||||||
empty set.
|
|
||||||
"""
|
|
||||||
result = self.intersection(other)
|
|
||||||
return not result
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
""":return: a shallow copy of this IP set."""
|
|
||||||
obj_copy = self.__class__()
|
|
||||||
obj_copy._cidrs.update(self._cidrs)
|
|
||||||
return obj_copy
|
|
||||||
|
|
||||||
def update(self, iterable, flags=0):
|
|
||||||
"""
|
|
||||||
Update the contents of this IP set with the union of itself and
|
|
||||||
other IP set.
|
|
||||||
|
|
||||||
:param iterable: an iterable containing IP addresses, subnets or ranges.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation
|
|
||||||
of the addr value. See the netaddr.core namespace documentation
|
|
||||||
for supported constant values.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if isinstance(iterable, IPSet):
|
|
||||||
self._cidrs = dict.fromkeys(
|
|
||||||
(ip for ip in cidr_merge(_dict_keys(self._cidrs)
|
|
||||||
+ _dict_keys(iterable._cidrs))), True)
|
|
||||||
return
|
|
||||||
elif isinstance(iterable, (IPNetwork, IPRange)):
|
|
||||||
self.add(iterable)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not hasattr(iterable, '__iter__'):
|
|
||||||
raise TypeError('an iterable was expected!')
|
|
||||||
# An iterable containing IP addresses or subnets.
|
|
||||||
mergeable = []
|
|
||||||
for addr in iterable:
|
|
||||||
if isinstance(addr, _int_type):
|
|
||||||
addr = IPAddress(addr, flags=flags)
|
|
||||||
mergeable.append(addr)
|
|
||||||
|
|
||||||
for cidr in cidr_merge(_dict_keys(self._cidrs) + mergeable):
|
|
||||||
self._cidrs[cidr] = True
|
|
||||||
|
|
||||||
self.compact()
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
"""Remove all IP addresses and subnets from this IP set."""
|
|
||||||
self._cidrs = {}
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set
|
|
||||||
|
|
||||||
:return: ``True`` if this IP set is equivalent to the ``other`` IP set,
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self._cidrs == other._cidrs
|
|
||||||
except AttributeError:
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set
|
|
||||||
|
|
||||||
:return: ``False`` if this IP set is equivalent to the ``other`` IP set,
|
|
||||||
``True`` otherwise.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return self._cidrs != other._cidrs
|
|
||||||
except AttributeError:
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __lt__(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set
|
|
||||||
|
|
||||||
:return: ``True`` if this IP set is less than the ``other`` IP set,
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not hasattr(other, '_cidrs'):
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
return self.size < other.size and self.issubset(other)
|
|
||||||
|
|
||||||
def issubset(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: ``True`` if every IP address and subnet in this IP set
|
|
||||||
is found within ``other``.
|
|
||||||
"""
|
|
||||||
for cidr in self._cidrs:
|
|
||||||
if cidr not in other:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
__le__ = issubset
|
|
||||||
|
|
||||||
def __gt__(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: ``True`` if this IP set is greater than the ``other`` IP set,
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not hasattr(other, '_cidrs'):
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
return self.size > other.size and self.issuperset(other)
|
|
||||||
|
|
||||||
def issuperset(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: ``True`` if every IP address and subnet in other IP set
|
|
||||||
is found within this one.
|
|
||||||
"""
|
|
||||||
if not hasattr(other, '_cidrs'):
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
for cidr in other._cidrs:
|
|
||||||
if cidr not in self:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
__ge__ = issuperset
|
|
||||||
|
|
||||||
def union(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: the union of this IP set and another as a new IP set
|
|
||||||
(combines IP addresses and subnets from both sets).
|
|
||||||
"""
|
|
||||||
ip_set = self.copy()
|
|
||||||
ip_set.update(other)
|
|
||||||
return ip_set
|
|
||||||
|
|
||||||
__or__ = union
|
|
||||||
|
|
||||||
def intersection(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: the intersection of this IP set and another as a new IP set.
|
|
||||||
(IP addresses and subnets common to both sets).
|
|
||||||
"""
|
|
||||||
result_cidrs = {}
|
|
||||||
|
|
||||||
own_nets = sorted(self._cidrs)
|
|
||||||
other_nets = sorted(other._cidrs)
|
|
||||||
own_idx = 0
|
|
||||||
other_idx = 0
|
|
||||||
own_len = len(own_nets)
|
|
||||||
other_len = len(other_nets)
|
|
||||||
while own_idx < own_len and other_idx < other_len:
|
|
||||||
own_cur = own_nets[own_idx]
|
|
||||||
other_cur = other_nets[other_idx]
|
|
||||||
|
|
||||||
if own_cur == other_cur:
|
|
||||||
result_cidrs[own_cur] = True
|
|
||||||
own_idx += 1
|
|
||||||
other_idx += 1
|
|
||||||
elif own_cur in other_cur:
|
|
||||||
result_cidrs[own_cur] = True
|
|
||||||
own_idx += 1
|
|
||||||
elif other_cur in own_cur:
|
|
||||||
result_cidrs[other_cur] = True
|
|
||||||
other_idx += 1
|
|
||||||
else:
|
|
||||||
# own_cur and other_cur have nothing in common
|
|
||||||
if own_cur < other_cur:
|
|
||||||
own_idx += 1
|
|
||||||
else:
|
|
||||||
other_idx += 1
|
|
||||||
|
|
||||||
# We ran out of networks in own_nets or other_nets. Either way, there
|
|
||||||
# can be no further result_cidrs.
|
|
||||||
result = IPSet()
|
|
||||||
result._cidrs = result_cidrs
|
|
||||||
return result
|
|
||||||
|
|
||||||
__and__ = intersection
|
|
||||||
|
|
||||||
def symmetric_difference(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: the symmetric difference of this IP set and another as a new
|
|
||||||
IP set (all IP addresses and subnets that are in exactly one
|
|
||||||
of the sets).
|
|
||||||
"""
|
|
||||||
# In contrast to intersection() and difference(), we cannot construct
|
|
||||||
# the result_cidrs easily. Some cidrs may have to be merged, e.g. for
|
|
||||||
# IPSet(["10.0.0.0/32"]).symmetric_difference(IPSet(["10.0.0.1/32"])).
|
|
||||||
result_ranges = []
|
|
||||||
|
|
||||||
own_nets = sorted(self._cidrs)
|
|
||||||
other_nets = sorted(other._cidrs)
|
|
||||||
own_idx = 0
|
|
||||||
other_idx = 0
|
|
||||||
own_len = len(own_nets)
|
|
||||||
other_len = len(other_nets)
|
|
||||||
while own_idx < own_len and other_idx < other_len:
|
|
||||||
own_cur = own_nets[own_idx]
|
|
||||||
other_cur = other_nets[other_idx]
|
|
||||||
|
|
||||||
if own_cur == other_cur:
|
|
||||||
own_idx += 1
|
|
||||||
other_idx += 1
|
|
||||||
elif own_cur in other_cur:
|
|
||||||
own_idx = _subtract(other_cur, own_nets, own_idx, result_ranges)
|
|
||||||
other_idx += 1
|
|
||||||
elif other_cur in own_cur:
|
|
||||||
other_idx = _subtract(own_cur, other_nets, other_idx, result_ranges)
|
|
||||||
own_idx += 1
|
|
||||||
else:
|
|
||||||
# own_cur and other_cur have nothing in common
|
|
||||||
if own_cur < other_cur:
|
|
||||||
result_ranges.append((own_cur._module.version,
|
|
||||||
own_cur.first, own_cur.last))
|
|
||||||
own_idx += 1
|
|
||||||
else:
|
|
||||||
result_ranges.append((other_cur._module.version,
|
|
||||||
other_cur.first, other_cur.last))
|
|
||||||
other_idx += 1
|
|
||||||
|
|
||||||
# If the above loop terminated because it processed all cidrs of
|
|
||||||
# "other", then any remaining cidrs in self must be part of the result.
|
|
||||||
while own_idx < own_len:
|
|
||||||
own_cur = own_nets[own_idx]
|
|
||||||
result_ranges.append((own_cur._module.version,
|
|
||||||
own_cur.first, own_cur.last))
|
|
||||||
own_idx += 1
|
|
||||||
|
|
||||||
# If the above loop terminated because it processed all cidrs of
|
|
||||||
# self, then any remaining cidrs in "other" must be part of the result.
|
|
||||||
while other_idx < other_len:
|
|
||||||
other_cur = other_nets[other_idx]
|
|
||||||
result_ranges.append((other_cur._module.version,
|
|
||||||
other_cur.first, other_cur.last))
|
|
||||||
other_idx += 1
|
|
||||||
|
|
||||||
result = IPSet()
|
|
||||||
for start, stop in _iter_merged_ranges(result_ranges):
|
|
||||||
cidrs = iprange_to_cidrs(start, stop)
|
|
||||||
for cidr in cidrs:
|
|
||||||
result._cidrs[cidr] = True
|
|
||||||
return result
|
|
||||||
|
|
||||||
__xor__ = symmetric_difference
|
|
||||||
|
|
||||||
def difference(self, other):
|
|
||||||
"""
|
|
||||||
:param other: an IP set.
|
|
||||||
|
|
||||||
:return: the difference between this IP set and another as a new IP
|
|
||||||
set (all IP addresses and subnets that are in this IP set but
|
|
||||||
not found in the other.)
|
|
||||||
"""
|
|
||||||
result_ranges = []
|
|
||||||
result_cidrs = {}
|
|
||||||
|
|
||||||
own_nets = sorted(self._cidrs)
|
|
||||||
other_nets = sorted(other._cidrs)
|
|
||||||
own_idx = 0
|
|
||||||
other_idx = 0
|
|
||||||
own_len = len(own_nets)
|
|
||||||
other_len = len(other_nets)
|
|
||||||
while own_idx < own_len and other_idx < other_len:
|
|
||||||
own_cur = own_nets[own_idx]
|
|
||||||
other_cur = other_nets[other_idx]
|
|
||||||
|
|
||||||
if own_cur == other_cur:
|
|
||||||
own_idx += 1
|
|
||||||
other_idx += 1
|
|
||||||
elif own_cur in other_cur:
|
|
||||||
own_idx += 1
|
|
||||||
elif other_cur in own_cur:
|
|
||||||
other_idx = _subtract(own_cur, other_nets, other_idx,
|
|
||||||
result_ranges)
|
|
||||||
own_idx += 1
|
|
||||||
else:
|
|
||||||
# own_cur and other_cur have nothing in common
|
|
||||||
if own_cur < other_cur:
|
|
||||||
result_cidrs[own_cur] = True
|
|
||||||
own_idx += 1
|
|
||||||
else:
|
|
||||||
other_idx += 1
|
|
||||||
|
|
||||||
# If the above loop terminated because it processed all cidrs of
|
|
||||||
# "other", then any remaining cidrs in self must be part of the result.
|
|
||||||
while own_idx < own_len:
|
|
||||||
result_cidrs[own_nets[own_idx]] = True
|
|
||||||
own_idx += 1
|
|
||||||
|
|
||||||
for start, stop in _iter_merged_ranges(result_ranges):
|
|
||||||
for cidr in iprange_to_cidrs(start, stop):
|
|
||||||
result_cidrs[cidr] = True
|
|
||||||
|
|
||||||
result = IPSet()
|
|
||||||
result._cidrs = result_cidrs
|
|
||||||
return result
|
|
||||||
|
|
||||||
__sub__ = difference
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
"""
|
|
||||||
:return: the cardinality of this IP set (i.e. sum of individual IP \
|
|
||||||
addresses). Raises ``IndexError`` if size > maxint (a Python \
|
|
||||||
limitation). Use the .size property for subnets of any size.
|
|
||||||
"""
|
|
||||||
size = self.size
|
|
||||||
if size > _sys_maxint:
|
|
||||||
raise IndexError(
|
|
||||||
"range contains more than %d (sys.maxint) IP addresses!"
|
|
||||||
"Use the .size property instead." % _sys_maxint)
|
|
||||||
return size
|
|
||||||
|
|
||||||
@property
|
|
||||||
def size(self):
|
|
||||||
"""
|
|
||||||
The cardinality of this IP set (based on the number of individual IP
|
|
||||||
addresses including those implicitly defined in subnets).
|
|
||||||
"""
|
|
||||||
return sum([cidr.size for cidr in self._cidrs])
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
""":return: Python statement to create an equivalent object"""
|
|
||||||
return 'IPSet(%r)' % [str(c) for c in sorted(self._cidrs)]
|
|
||||||
|
|
||||||
__str__ = __repr__
|
|
||||||
|
|
||||||
def iscontiguous(self):
|
|
||||||
"""
|
|
||||||
Returns True if the members of the set form a contiguous IP
|
|
||||||
address range (with no gaps), False otherwise.
|
|
||||||
|
|
||||||
:return: ``True`` if the ``IPSet`` object is contiguous.
|
|
||||||
"""
|
|
||||||
cidrs = self.iter_cidrs()
|
|
||||||
if len(cidrs) > 1:
|
|
||||||
previous = cidrs[0][0]
|
|
||||||
for cidr in cidrs:
|
|
||||||
if cidr[0] != previous:
|
|
||||||
return False
|
|
||||||
previous = cidr[-1] + 1
|
|
||||||
return True
|
|
||||||
|
|
||||||
def iprange(self):
|
|
||||||
"""
|
|
||||||
Generates an IPRange for this IPSet, if all its members
|
|
||||||
form a single contiguous sequence.
|
|
||||||
|
|
||||||
Raises ``ValueError`` if the set is not contiguous.
|
|
||||||
|
|
||||||
:return: An ``IPRange`` for all IPs in the IPSet.
|
|
||||||
"""
|
|
||||||
if self.iscontiguous():
|
|
||||||
cidrs = self.iter_cidrs()
|
|
||||||
if not cidrs:
|
|
||||||
return None
|
|
||||||
return IPRange(cidrs[0][0], cidrs[-1][-1])
|
|
||||||
else:
|
|
||||||
raise ValueError("IPSet is not contiguous")
|
|
||||||
|
|
||||||
def iter_ipranges(self):
|
|
||||||
"""Generate the merged IPRanges for this IPSet.
|
|
||||||
|
|
||||||
In contrast to self.iprange(), this will work even when the IPSet is
|
|
||||||
not contiguous. Adjacent IPRanges will be merged together, so you
|
|
||||||
get the minimal number of IPRanges.
|
|
||||||
"""
|
|
||||||
sorted_ranges = [(cidr._module.version, cidr.first, cidr.last) for
|
|
||||||
cidr in self.iter_cidrs()]
|
|
||||||
|
|
||||||
for start, stop in _iter_merged_ranges(sorted_ranges):
|
|
||||||
yield IPRange(start, stop)
|
|
@ -1,273 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
Shared logic for various address types.
|
|
||||||
"""
|
|
||||||
import re as _re
|
|
||||||
|
|
||||||
from netaddr.compat import _range, _is_str
|
|
||||||
|
|
||||||
|
|
||||||
def bytes_to_bits():
|
|
||||||
"""
|
|
||||||
:return: A 256 element list containing 8-bit binary digit strings. The
|
|
||||||
list index value is equivalent to its bit string value.
|
|
||||||
"""
|
|
||||||
lookup = []
|
|
||||||
bits_per_byte = _range(7, -1, -1)
|
|
||||||
for num in range(256):
|
|
||||||
bits = 8 * [None]
|
|
||||||
for i in bits_per_byte:
|
|
||||||
bits[i] = '01'[num & 1]
|
|
||||||
num >>= 1
|
|
||||||
lookup.append(''.join(bits))
|
|
||||||
return lookup
|
|
||||||
|
|
||||||
#: A lookup table of 8-bit integer values to their binary digit bit strings.
|
|
||||||
BYTES_TO_BITS = bytes_to_bits()
|
|
||||||
|
|
||||||
|
|
||||||
def valid_words(words, word_size, num_words):
|
|
||||||
"""
|
|
||||||
:param words: A sequence of unsigned integer word values.
|
|
||||||
|
|
||||||
:param word_size: Width (in bits) of each unsigned integer word value.
|
|
||||||
|
|
||||||
:param num_words: Number of unsigned integer words expected.
|
|
||||||
|
|
||||||
:return: ``True`` if word sequence is valid for this address type,
|
|
||||||
``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not hasattr(words, '__iter__'):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if len(words) != num_words:
|
|
||||||
return False
|
|
||||||
|
|
||||||
max_word = 2 ** word_size - 1
|
|
||||||
|
|
||||||
for i in words:
|
|
||||||
if not 0 <= i <= max_word:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_words(int_val, word_size, num_words):
|
|
||||||
"""
|
|
||||||
:param int_val: Unsigned integer to be divided into words of equal size.
|
|
||||||
|
|
||||||
:param word_size: Width (in bits) of each unsigned integer word value.
|
|
||||||
|
|
||||||
:param num_words: Number of unsigned integer words expected.
|
|
||||||
|
|
||||||
:return: A tuple contain unsigned integer word values split according
|
|
||||||
to provided arguments.
|
|
||||||
"""
|
|
||||||
max_int = 2 ** (num_words * word_size) - 1
|
|
||||||
|
|
||||||
if not 0 <= int_val <= max_int:
|
|
||||||
raise IndexError('integer out of bounds: %r!' % hex(int_val))
|
|
||||||
|
|
||||||
max_word = 2 ** word_size - 1
|
|
||||||
|
|
||||||
words = []
|
|
||||||
for _ in range(num_words):
|
|
||||||
word = int_val & max_word
|
|
||||||
words.append(int(word))
|
|
||||||
int_val >>= word_size
|
|
||||||
|
|
||||||
return tuple(reversed(words))
|
|
||||||
|
|
||||||
|
|
||||||
def words_to_int(words, word_size, num_words):
|
|
||||||
"""
|
|
||||||
:param words: A sequence of unsigned integer word values.
|
|
||||||
|
|
||||||
:param word_size: Width (in bits) of each unsigned integer word value.
|
|
||||||
|
|
||||||
:param num_words: Number of unsigned integer words expected.
|
|
||||||
|
|
||||||
:return: An unsigned integer that is equivalent to value represented
|
|
||||||
by word sequence.
|
|
||||||
"""
|
|
||||||
if not valid_words(words, word_size, num_words):
|
|
||||||
raise ValueError('invalid integer word sequence: %r!' % (words,))
|
|
||||||
|
|
||||||
int_val = 0
|
|
||||||
for i, num in enumerate(reversed(words)):
|
|
||||||
word = num
|
|
||||||
word = word << word_size * i
|
|
||||||
int_val = int_val | word
|
|
||||||
|
|
||||||
return int_val
|
|
||||||
|
|
||||||
|
|
||||||
def valid_bits(bits, width, word_sep=''):
|
|
||||||
"""
|
|
||||||
:param bits: A network address in a delimited binary string format.
|
|
||||||
|
|
||||||
:param width: Maximum width (in bits) of a network address (excluding
|
|
||||||
delimiters).
|
|
||||||
|
|
||||||
:param word_sep: (optional) character or string used to delimit word
|
|
||||||
groups (default: '', no separator).
|
|
||||||
|
|
||||||
:return: ``True`` if network address is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not _is_str(bits):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if word_sep != '':
|
|
||||||
bits = bits.replace(word_sep, '')
|
|
||||||
|
|
||||||
if len(bits) != width:
|
|
||||||
return False
|
|
||||||
|
|
||||||
max_int = 2 ** width - 1
|
|
||||||
|
|
||||||
try:
|
|
||||||
if 0 <= int(bits, 2) <= max_int:
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def bits_to_int(bits, width, word_sep=''):
|
|
||||||
"""
|
|
||||||
:param bits: A network address in a delimited binary string format.
|
|
||||||
|
|
||||||
:param width: Maximum width (in bits) of a network address (excluding
|
|
||||||
delimiters).
|
|
||||||
|
|
||||||
:param word_sep: (optional) character or string used to delimit word
|
|
||||||
groups (default: '', no separator).
|
|
||||||
|
|
||||||
:return: An unsigned integer that is equivalent to value represented
|
|
||||||
by network address in readable binary form.
|
|
||||||
"""
|
|
||||||
if not valid_bits(bits, width, word_sep):
|
|
||||||
raise ValueError('invalid readable binary string: %r!' % (bits,))
|
|
||||||
|
|
||||||
if word_sep != '':
|
|
||||||
bits = bits.replace(word_sep, '')
|
|
||||||
|
|
||||||
return int(bits, 2)
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_bits(int_val, word_size, num_words, word_sep=''):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:param word_size: Width (in bits) of each unsigned integer word value.
|
|
||||||
|
|
||||||
:param num_words: Number of unsigned integer words expected.
|
|
||||||
|
|
||||||
:param word_sep: (optional) character or string used to delimit word
|
|
||||||
groups (default: '', no separator).
|
|
||||||
|
|
||||||
:return: A network address in a delimited binary string format that is
|
|
||||||
equivalent in value to unsigned integer.
|
|
||||||
"""
|
|
||||||
bit_words = []
|
|
||||||
|
|
||||||
for word in int_to_words(int_val, word_size, num_words):
|
|
||||||
bits = []
|
|
||||||
while word:
|
|
||||||
bits.append(BYTES_TO_BITS[word & 255])
|
|
||||||
word >>= 8
|
|
||||||
bits.reverse()
|
|
||||||
bit_str = ''.join(bits) or '0' * word_size
|
|
||||||
bits = ('0' * word_size + bit_str)[-word_size:]
|
|
||||||
bit_words.append(bits)
|
|
||||||
|
|
||||||
if word_sep != '':
|
|
||||||
# Check custom separator.
|
|
||||||
if not _is_str(word_sep):
|
|
||||||
raise ValueError('word separator is not a string: %r!' % (word_sep,))
|
|
||||||
|
|
||||||
return word_sep.join(bit_words)
|
|
||||||
|
|
||||||
|
|
||||||
def valid_bin(bin_val, width):
|
|
||||||
"""
|
|
||||||
:param bin_val: A network address in Python's binary representation format
|
|
||||||
('0bxxx').
|
|
||||||
|
|
||||||
:param width: Maximum width (in bits) of a network address (excluding
|
|
||||||
delimiters).
|
|
||||||
|
|
||||||
:return: ``True`` if network address is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if not _is_str(bin_val):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not bin_val.startswith('0b'):
|
|
||||||
return False
|
|
||||||
|
|
||||||
bin_val = bin_val.replace('0b', '')
|
|
||||||
|
|
||||||
if len(bin_val) > width:
|
|
||||||
return False
|
|
||||||
|
|
||||||
max_int = 2 ** width - 1
|
|
||||||
|
|
||||||
try:
|
|
||||||
if 0 <= int(bin_val, 2) <= max_int:
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_bin(int_val, width):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:param width: Maximum allowed width (in bits) of a unsigned integer.
|
|
||||||
|
|
||||||
:return: Equivalent string value in Python's binary representation format
|
|
||||||
('0bxxx').
|
|
||||||
"""
|
|
||||||
bin_tokens = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Python 2.6.x and upwards.
|
|
||||||
bin_val = bin(int_val)
|
|
||||||
except NameError:
|
|
||||||
# Python 2.4.x and 2.5.x
|
|
||||||
i = int_val
|
|
||||||
while i > 0:
|
|
||||||
word = i & 0xff
|
|
||||||
bin_tokens.append(BYTES_TO_BITS[word])
|
|
||||||
i >>= 8
|
|
||||||
|
|
||||||
bin_tokens.reverse()
|
|
||||||
bin_val = '0b' + _re.sub(r'^[0]+([01]+)$', r'\1', ''.join(bin_tokens))
|
|
||||||
|
|
||||||
if len(bin_val[2:]) > width:
|
|
||||||
raise IndexError('binary string out of bounds: %s!' % (bin_val,))
|
|
||||||
|
|
||||||
return bin_val
|
|
||||||
|
|
||||||
|
|
||||||
def bin_to_int(bin_val, width):
|
|
||||||
"""
|
|
||||||
:param bin_val: A string containing an unsigned integer in Python's binary
|
|
||||||
representation format ('0bxxx').
|
|
||||||
|
|
||||||
:param width: Maximum allowed width (in bits) of a unsigned integer.
|
|
||||||
|
|
||||||
:return: An unsigned integer that is equivalent to value represented
|
|
||||||
by Python binary string format.
|
|
||||||
"""
|
|
||||||
if not valid_bin(bin_val, width):
|
|
||||||
raise ValueError('not a valid Python binary string: %r!' % (bin_val,))
|
|
||||||
|
|
||||||
return int(bin_val.replace('0b', ''), 2)
|
|
@ -1,296 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
IEEE 48-bit EUI (MAC address) logic.
|
|
||||||
|
|
||||||
Supports numerous MAC string formats including Cisco's triple hextet as well
|
|
||||||
as bare MACs containing no delimiters.
|
|
||||||
"""
|
|
||||||
import struct as _struct
|
|
||||||
import re as _re
|
|
||||||
|
|
||||||
# Check whether we need to use fallback code or not.
|
|
||||||
try:
|
|
||||||
from socket import AF_LINK
|
|
||||||
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,
|
|
||||||
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 = 48
|
|
||||||
|
|
||||||
#: The AF_* constant value of this address type.
|
|
||||||
family = AF_LINK
|
|
||||||
|
|
||||||
#: A friendly string name address type.
|
|
||||||
family_name = 'MAC'
|
|
||||||
|
|
||||||
#: The version of this address type.
|
|
||||||
version = 48
|
|
||||||
|
|
||||||
#: The maximum integer value that can be represented by this address type.
|
|
||||||
max_int = 2 ** width - 1
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Dialect classes.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class mac_eui48(object):
|
|
||||||
"""A standard IEEE EUI-48 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 number base to be used when interpreting word values as integers.
|
|
||||||
word_base = 16
|
|
||||||
|
|
||||||
|
|
||||||
class mac_unix(mac_eui48):
|
|
||||||
"""A UNIX-style MAC address dialect class."""
|
|
||||||
word_size = 8
|
|
||||||
num_words = width // word_size
|
|
||||||
word_sep = ':'
|
|
||||||
word_fmt = '%x'
|
|
||||||
word_base = 16
|
|
||||||
|
|
||||||
|
|
||||||
class mac_unix_expanded(mac_unix):
|
|
||||||
"""A UNIX-style MAC address dialect class with leading zeroes."""
|
|
||||||
word_fmt = '%.2x'
|
|
||||||
|
|
||||||
|
|
||||||
class mac_cisco(mac_eui48):
|
|
||||||
"""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 mac_bare(mac_eui48):
|
|
||||||
"""A bare (no delimiters) MAC address dialect class."""
|
|
||||||
word_size = 48
|
|
||||||
num_words = width // word_size
|
|
||||||
word_sep = ''
|
|
||||||
word_fmt = '%.12X'
|
|
||||||
word_base = 16
|
|
||||||
|
|
||||||
|
|
||||||
class mac_pgsql(mac_eui48):
|
|
||||||
"""A PostgreSQL style (2 x 24-bit words) MAC address dialect class."""
|
|
||||||
word_size = 24
|
|
||||||
num_words = width // word_size
|
|
||||||
word_sep = ':'
|
|
||||||
word_fmt = '%.6x'
|
|
||||||
word_base = 16
|
|
||||||
|
|
||||||
#: The default dialect to be used when not specified by the user.
|
|
||||||
DEFAULT_DIALECT = mac_eui48
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
#: Regular expressions to match all supported MAC address formats.
|
|
||||||
RE_MAC_FORMATS = (
|
|
||||||
# 2 bytes x 6 (UNIX, Windows, EUI-48)
|
|
||||||
'^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$',
|
|
||||||
'^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$',
|
|
||||||
|
|
||||||
# 4 bytes x 3 (Cisco)
|
|
||||||
'^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$',
|
|
||||||
'^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$',
|
|
||||||
'^' + r'\.'.join(['([0-9A-F]{1,4})'] * 3) + '$',
|
|
||||||
|
|
||||||
# 6 bytes x 2 (PostgreSQL)
|
|
||||||
'^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$',
|
|
||||||
'^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$',
|
|
||||||
|
|
||||||
# 12 bytes (bare, no delimiters)
|
|
||||||
'^(' + ''.join(['[0-9A-F]'] * 12) + ')$',
|
|
||||||
'^(' + ''.join(['[0-9A-F]'] * 11) + ')$',
|
|
||||||
)
|
|
||||||
# For efficiency, each string regexp converted in place to its compiled
|
|
||||||
# counterpart.
|
|
||||||
RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS]
|
|
||||||
|
|
||||||
|
|
||||||
def valid_str(addr):
|
|
||||||
"""
|
|
||||||
:param addr: An IEEE EUI-48 (MAC) address in string form.
|
|
||||||
|
|
||||||
:return: ``True`` if MAC address string is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
for regexp in RE_MAC_FORMATS:
|
|
||||||
try:
|
|
||||||
match_result = regexp.findall(addr)
|
|
||||||
if len(match_result) != 0:
|
|
||||||
return True
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def str_to_int(addr):
|
|
||||||
"""
|
|
||||||
:param addr: An IEEE EUI-48 (MAC) address in string form.
|
|
||||||
|
|
||||||
:return: An unsigned integer that is equivalent to value represented
|
|
||||||
by EUI-48/MAC string address formatted according to the dialect
|
|
||||||
settings.
|
|
||||||
"""
|
|
||||||
words = []
|
|
||||||
if _is_str(addr):
|
|
||||||
found_match = False
|
|
||||||
for regexp in RE_MAC_FORMATS:
|
|
||||||
match_result = regexp.findall(addr)
|
|
||||||
if len(match_result) != 0:
|
|
||||||
found_match = True
|
|
||||||
if isinstance(match_result[0], tuple):
|
|
||||||
words = match_result[0]
|
|
||||||
else:
|
|
||||||
words = (match_result[0],)
|
|
||||||
break
|
|
||||||
if not found_match:
|
|
||||||
raise AddrFormatError('%r is not a supported MAC format!' % (addr,))
|
|
||||||
else:
|
|
||||||
raise TypeError('%r is not str() or unicode()!' % (addr,))
|
|
||||||
|
|
||||||
int_val = None
|
|
||||||
|
|
||||||
if len(words) == 6:
|
|
||||||
# 2 bytes x 6 (UNIX, Windows, EUI-48)
|
|
||||||
int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
|
|
||||||
elif len(words) == 3:
|
|
||||||
# 4 bytes x 3 (Cisco)
|
|
||||||
int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
|
|
||||||
elif len(words) == 2:
|
|
||||||
# 6 bytes x 2 (PostgreSQL)
|
|
||||||
int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16)
|
|
||||||
elif len(words) == 1:
|
|
||||||
# 12 bytes (bare, no delimiters)
|
|
||||||
int_val = int('%012x' % int(words[0], 16), 16)
|
|
||||||
else:
|
|
||||||
raise AddrFormatError('unexpected word count in MAC address %r!' % (addr,))
|
|
||||||
|
|
||||||
return int_val
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_str(int_val, dialect=None):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:param dialect: (optional) a Python class defining formatting options.
|
|
||||||
|
|
||||||
:return: An IEEE EUI-48 (MAC) address string that is equivalent to
|
|
||||||
unsigned integer formatted according to the dialect settings.
|
|
||||||
"""
|
|
||||||
if dialect is None:
|
|
||||||
dialect = mac_eui48
|
|
||||||
|
|
||||||
words = int_to_words(int_val, dialect)
|
|
||||||
tokens = [dialect.word_fmt % i for i in words]
|
|
||||||
addr = dialect.word_sep.join(tokens)
|
|
||||||
|
|
||||||
return addr
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
return _struct.pack(">HI", int_val >> 32, int_val & 0xffffffff)
|
|
||||||
|
|
||||||
|
|
||||||
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('>6B', packed_int))
|
|
||||||
|
|
||||||
int_val = 0
|
|
||||||
for i, num in enumerate(reversed(words)):
|
|
||||||
word = num
|
|
||||||
word = word << 8 * i
|
|
||||||
int_val = int_val | word
|
|
||||||
|
|
||||||
return int_val
|
|
||||||
|
|
||||||
|
|
||||||
def valid_words(words, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _valid_words(words, dialect.word_size, dialect.num_words)
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_words(int_val, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _int_to_words(int_val, dialect.word_size, dialect.num_words)
|
|
||||||
|
|
||||||
|
|
||||||
def words_to_int(words, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _words_to_int(words, dialect.word_size, dialect.num_words)
|
|
||||||
|
|
||||||
|
|
||||||
def valid_bits(bits, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _valid_bits(bits, width, dialect.word_sep)
|
|
||||||
|
|
||||||
|
|
||||||
def bits_to_int(bits, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _bits_to_int(bits, width, dialect.word_sep)
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_bits(int_val, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
return _int_to_bits(
|
|
||||||
int_val, dialect.word_size, dialect.num_words, dialect.word_sep)
|
|
||||||
|
|
||||||
|
|
||||||
def valid_bin(bin_val, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_DIALECT
|
|
||||||
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)
|
|
@ -1,273 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""
|
|
||||||
IEEE 64-bit EUI (Extended Unique Indentifier) logic.
|
|
||||||
"""
|
|
||||||
import struct as _struct
|
|
||||||
import re as _re
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
# This is a fake constant that doesn't really exist. Here for completeness.
|
|
||||||
AF_EUI64 = 64
|
|
||||||
|
|
||||||
#: The width (in bits) of this address type.
|
|
||||||
width = 64
|
|
||||||
|
|
||||||
#: The AF_* constant value of this address type.
|
|
||||||
family = AF_EUI64
|
|
||||||
|
|
||||||
#: A friendly string name address type.
|
|
||||||
family_name = 'EUI-64'
|
|
||||||
|
|
||||||
#: The version of this address type.
|
|
||||||
version = 64
|
|
||||||
|
|
||||||
#: The maximum integer value that can be represented by this address type.
|
|
||||||
max_int = 2 ** width - 1
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# 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 number base to be used when interpreting word values as integers.
|
|
||||||
word_base = 16
|
|
||||||
|
|
||||||
|
|
||||||
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) + '$',
|
|
||||||
'^' + r'\.'.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):
|
|
||||||
for regexp in formats:
|
|
||||||
match = regexp.findall(address)
|
|
||||||
if match:
|
|
||||||
return match[0]
|
|
||||||
|
|
||||||
|
|
||||||
def valid_str(addr):
|
|
||||||
"""
|
|
||||||
:param addr: An IEEE EUI-64 indentifier in string form.
|
|
||||||
|
|
||||||
:return: ``True`` if EUI-64 indentifier is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
if _get_match_result(addr, RE_EUI64_FORMATS):
|
|
||||||
return True
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
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 address formatted according to the dialect
|
|
||||||
"""
|
|
||||||
words = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
words = _get_match_result(addr, RE_EUI64_FORMATS)
|
|
||||||
if not words:
|
|
||||||
raise TypeError
|
|
||||||
except TypeError:
|
|
||||||
raise AddrFormatError('invalid IEEE EUI-64 identifier: %r!' % (addr,))
|
|
||||||
|
|
||||||
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_val
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_str(int_val, dialect=None):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:param dialect: (optional) a Python class defining formatting options
|
|
||||||
|
|
||||||
:return: An IEEE EUI-64 identifier that is equivalent to unsigned integer.
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
return _struct.pack('>8B', *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('>8B', packed_int))
|
|
||||||
|
|
||||||
int_val = 0
|
|
||||||
for i, num in enumerate(reversed(words)):
|
|
||||||
word = num
|
|
||||||
word = word << 8 * i
|
|
||||||
int_val = int_val | word
|
|
||||||
|
|
||||||
return int_val
|
|
||||||
|
|
||||||
|
|
||||||
def valid_words(words, dialect=None):
|
|
||||||
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):
|
|
||||||
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):
|
|
||||||
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):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_EUI64_DIALECT
|
|
||||||
return _valid_bits(bits, width, dialect.word_sep)
|
|
||||||
|
|
||||||
|
|
||||||
def bits_to_int(bits, dialect=None):
|
|
||||||
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):
|
|
||||||
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, dialect=None):
|
|
||||||
if dialect is None:
|
|
||||||
dialect = DEFAULT_EUI64_DIALECT
|
|
||||||
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)
|
|
@ -1,279 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
|
|
||||||
#
|
|
||||||
# Released under the BSD license. See the LICENSE file for details.
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
"""IPv4 address logic."""
|
|
||||||
|
|
||||||
import sys as _sys
|
|
||||||
import struct as _struct
|
|
||||||
|
|
||||||
from socket import inet_aton as _inet_aton
|
|
||||||
# Check whether we need to use fallback code or not.
|
|
||||||
if _sys.platform in ('win32', 'cygwin'):
|
|
||||||
# inet_pton() not available on Windows. inet_pton() under cygwin
|
|
||||||
# behaves exactly like inet_aton() and is therefore highly unreliable.
|
|
||||||
from netaddr.fbsocket import inet_pton as _inet_pton, AF_INET
|
|
||||||
else:
|
|
||||||
# All other cases, use all functions from the socket module.
|
|
||||||
from socket import inet_pton as _inet_pton, AF_INET
|
|
||||||
|
|
||||||
from netaddr.core import AddrFormatError, ZEROFILL, INET_PTON
|
|
||||||
|
|
||||||
from netaddr.strategy import (
|
|
||||||
valid_words as _valid_words, 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)
|
|
||||||
|
|
||||||
from netaddr.compat import _str_type
|
|
||||||
|
|
||||||
#: The width (in bits) of this address type.
|
|
||||||
width = 32
|
|
||||||
|
|
||||||
#: 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 = '%d'
|
|
||||||
|
|
||||||
#: The separator character used between each word.
|
|
||||||
word_sep = '.'
|
|
||||||
|
|
||||||
#: The AF_* constant value of this address type.
|
|
||||||
family = AF_INET
|
|
||||||
|
|
||||||
#: A friendly string name address type.
|
|
||||||
family_name = 'IPv4'
|
|
||||||
|
|
||||||
#: The version of this address type.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
#: The number base to be used when interpreting word values as integers.
|
|
||||||
word_base = 10
|
|
||||||
|
|
||||||
#: 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 IPv4 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 IPv4 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 IPv4 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 IPv4 hostmasks to their equivalent CIDR prefixes.
|
|
||||||
hostmask_to_prefix = dict(
|
|
||||||
[((2 ** (width - i) - 1), i) for i in range(0, width + 1)])
|
|
||||||
|
|
||||||
|
|
||||||
def valid_str(addr, flags=0):
|
|
||||||
"""
|
|
||||||
:param addr: An IPv4 address in presentation (string) format.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation of the
|
|
||||||
addr value. Supported constants are INET_PTON and ZEROFILL. See the
|
|
||||||
netaddr.core docs for details.
|
|
||||||
|
|
||||||
:return: ``True`` if IPv4 address is valid, ``False`` otherwise.
|
|
||||||
"""
|
|
||||||
if addr == '':
|
|
||||||
raise AddrFormatError('Empty strings are not supported!')
|
|
||||||
|
|
||||||
validity = True
|
|
||||||
|
|
||||||
if flags & ZEROFILL:
|
|
||||||
addr = '.'.join(['%d' % int(i) for i in addr.split('.')])
|
|
||||||
|
|
||||||
try:
|
|
||||||
if flags & INET_PTON:
|
|
||||||
_inet_pton(AF_INET, addr)
|
|
||||||
else:
|
|
||||||
_inet_aton(addr)
|
|
||||||
except Exception:
|
|
||||||
validity = False
|
|
||||||
|
|
||||||
return validity
|
|
||||||
|
|
||||||
|
|
||||||
def str_to_int(addr, flags=0):
|
|
||||||
"""
|
|
||||||
:param addr: An IPv4 dotted decimal address in string form.
|
|
||||||
|
|
||||||
:param flags: decides which rules are applied to the interpretation of the
|
|
||||||
addr value. Supported constants are INET_PTON and ZEROFILL. See the
|
|
||||||
netaddr.core docs for details.
|
|
||||||
|
|
||||||
:return: The equivalent unsigned integer for a given IPv4 address.
|
|
||||||
"""
|
|
||||||
if flags & ZEROFILL:
|
|
||||||
addr = '.'.join(['%d' % int(i) for i in addr.split('.')])
|
|
||||||
|
|
||||||
try:
|
|
||||||
if flags & INET_PTON:
|
|
||||||
return _struct.unpack('>I', _inet_pton(AF_INET, addr))[0]
|
|
||||||
else:
|
|
||||||
return _struct.unpack('>I', _inet_aton(addr))[0]
|
|
||||||
except Exception:
|
|
||||||
raise AddrFormatError('%r is not a valid IPv4 address string!' % (addr,))
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_str(int_val, dialect=None):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:param dialect: (unused) Any value passed in is ignored.
|
|
||||||
|
|
||||||
:return: The IPv4 presentation (string) format address equivalent to the
|
|
||||||
unsigned integer provided.
|
|
||||||
"""
|
|
||||||
if 0 <= int_val <= max_int:
|
|
||||||
return '%d.%d.%d.%d' % (
|
|
||||||
int_val >> 24,
|
|
||||||
(int_val >> 16) & 0xff,
|
|
||||||
(int_val >> 8) & 0xff,
|
|
||||||
int_val & 0xff)
|
|
||||||
else:
|
|
||||||
raise ValueError('%r is not a valid 32-bit unsigned integer!' % (int_val,))
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_arpa(int_val):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:return: The reverse DNS lookup for an IPv4 address in network byte
|
|
||||||
order integer form.
|
|
||||||
"""
|
|
||||||
words = ["%d" % i for i in int_to_words(int_val)]
|
|
||||||
words.reverse()
|
|
||||||
words.extend(['in-addr', 'arpa', ''])
|
|
||||||
return '.'.join(words)
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
return _struct.pack('>I', int_val)
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
return _struct.unpack('>I', packed_int)[0]
|
|
||||||
|
|
||||||
|
|
||||||
def valid_words(words):
|
|
||||||
return _valid_words(words, word_size, num_words)
|
|
||||||
|
|
||||||
|
|
||||||
def int_to_words(int_val):
|
|
||||||
"""
|
|
||||||
:param int_val: An unsigned integer.
|
|
||||||
|
|
||||||
:return: An integer word (octet) sequence that is equivalent to value
|
|
||||||
represented by an unsigned integer.
|
|
||||||
"""
|
|
||||||
if not 0 <= int_val <= max_int:
|
|
||||||
raise ValueError('%r is not a valid integer value supported by'
|
|
||||||
'this address type!' % (int_val,))
|
|
||||||
return ( int_val >> 24,
|
|
||||||
(int_val >> 16) & 0xff,
|
|
||||||
(int_val >> 8) & 0xff,
|
|
||||||
int_val & 0xff)
|
|
||||||
|
|
||||||
|
|
||||||
def words_to_int(words):
|
|
||||||
"""
|
|
||||||
:param words: A list or tuple containing integer octets.
|
|
||||||
|
|
||||||
:return: An unsigned integer that is equivalent to value represented
|
|
||||||
by word (octet) sequence.
|
|
||||||
"""
|
|
||||||
if not valid_words(words):
|
|
||||||
raise ValueError('%r is not a valid octet list for an IPv4 address!' % (words,))
|
|
||||||
return _struct.unpack('>I', _struct.pack('4B', *words))[0]
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def expand_partial_address(addr):
|
|
||||||
"""
|
|
||||||
Expands a partial IPv4 address into a full 4-octet version.
|
|
||||||
|
|
||||||
:param addr: an partial or abbreviated IPv4 address
|
|
||||||
|
|
||||||
:return: an expanded IP address in presentation format (x.x.x.x)
|
|
||||||
|
|
||||||
"""
|
|
||||||
tokens = []
|
|
||||||
|
|
||||||
error = AddrFormatError('invalid partial IPv4 address: %r!' % addr)
|
|
||||||
|
|
||||||
if isinstance(addr, _str_type):
|
|
||||||
if ':' in addr:
|
|
||||||
# Ignore IPv6 ...
|
|
||||||
raise error
|
|
||||||
|
|
||||||
try:
|
|
||||||
if '.' in addr:
|
|
||||||
tokens = ['%d' % int(o) for o in addr.split('.')]
|
|
||||||
else:
|
|
||||||
tokens = ['%d' % int(addr)]
|
|
||||||
except ValueError:
|
|
||||||
raise error
|
|
||||||
|
|
||||||
if 1 <= len(tokens) <= 4:
|
|
||||||
for i in range(4 - len(tokens)):
|
|
||||||
tokens.append('0')
|
|
||||||
else:
|
|
||||||
raise error
|
|
||||||
|
|
||||||
if not tokens:
|
|
||||||
raise error
|
|
||||||
|
|
||||||
return '%s.%s.%s.%s' % tuple(tokens)
|
|
||||||
|
|
@ -1,259 +0,0 @@
|
|||||||
#-----------------------------------------------------------------------------
|
|
||||||
# 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)
|
|
Loading…
Reference in New Issue
Block a user