692 lines
23 KiB
Python
692 lines
23 KiB
Python
|
#-----------------------------------------------------------------------------
|
||
|
# Copyright (c) 2008-2012, 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.
|
||
|
"""
|
||
|
|
||
|
import sys as _sys
|
||
|
import os as _os
|
||
|
import os.path as _path
|
||
|
import re as _re
|
||
|
import csv as _csv
|
||
|
|
||
|
import pprint as _pprint
|
||
|
|
||
|
from netaddr.core import NotRegisteredError, AddrFormatError, \
|
||
|
AddrConversionError, Subscriber, Publisher, DictDotLookup
|
||
|
from netaddr.strategy import eui48 as _eui48, eui64 as _eui64
|
||
|
from netaddr.strategy.eui48 import mac_eui48
|
||
|
from netaddr.ip import IPAddress
|
||
|
|
||
|
from netaddr.compat import _is_int, _is_str
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
class BaseIdentifier(object):
|
||
|
"""Base class for all IEEE identifiers."""
|
||
|
__slots__ = ('_value',)
|
||
|
|
||
|
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
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
"""
|
||
|
:return: ``True`` if this BaseIdentifier object is numerically the
|
||
|
same as other, ``False`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return (self.__class__, self._value) == (other.__class__, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
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 = open(ieee.OUI_REGISTRY)
|
||
|
for (offset, size) in ieee.OUI_INDEX[self._value]:
|
||
|
fh.seek(offset)
|
||
|
data = fh.read(size)
|
||
|
self._parse_data(data, offset, size)
|
||
|
fh.close()
|
||
|
else:
|
||
|
raise NotRegisteredError('OUI %r not registered!' % oui)
|
||
|
|
||
|
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'] = ' '.join(line.split()[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
|
||
|
words = []
|
||
|
for _ in range(3):
|
||
|
word = int_val & 0xff
|
||
|
words.append('%02x' % word)
|
||
|
int_val >>= 8
|
||
|
return '-'.join(reversed(words)).upper()
|
||
|
|
||
|
def __repr__(self):
|
||
|
""":return: executable Python string to recreate equivalent object."""
|
||
|
return "OUI('%s')" % self
|
||
|
|
||
|
#-----------------------------------------------------------------------------
|
||
|
class IAB(BaseIdentifier):
|
||
|
"""
|
||
|
An individual IEEE IAB (Individual Address Block) identifier.
|
||
|
|
||
|
For online details see - http://standards.ieee.org/regauth/oui/
|
||
|
|
||
|
"""
|
||
|
__slots__ = ('record',)
|
||
|
|
||
|
@staticmethod
|
||
|
def split_iab_mac(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 0x50c2000 <= eui_int <= 0x50c2fff:
|
||
|
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 0x50c2000 <= iab_bits <= 0x50c2fff:
|
||
|
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) = IAB.split_iab_mac(int_val, strict)
|
||
|
self._value = iab_int
|
||
|
elif _is_int(iab):
|
||
|
(iab_int, user_int) = IAB.split_iab_mac(iab, strict)
|
||
|
self._value = iab_int
|
||
|
else:
|
||
|
raise TypeError('unexpected IAB format: %r!' % iab)
|
||
|
|
||
|
# Discover offsets.
|
||
|
if self._value in ieee.IAB_INDEX:
|
||
|
fh = open(ieee.IAB_REGISTRY)
|
||
|
(offset, size) = ieee.IAB_INDEX[self._value][0]
|
||
|
self.record['offset'] = offset
|
||
|
self.record['size'] = size
|
||
|
fh.seek(offset)
|
||
|
data = fh.read(size)
|
||
|
self._parse_data(data, offset, size)
|
||
|
fh.close()
|
||
|
else:
|
||
|
raise NotRegisteredError('IAB %r not unregistered!' % iab)
|
||
|
|
||
|
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'] = ' '.join(line.split()[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 << 12
|
||
|
words = []
|
||
|
for _ in range(6):
|
||
|
word = int_val & 0xff
|
||
|
words.append('%02x' % word)
|
||
|
int_val >>= 8
|
||
|
return '-'.join(reversed(words)).upper()
|
||
|
|
||
|
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 explict EUI address version. Mainly \
|
||
|
used to distinguish between 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' \
|
||
|
% str(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 hasattr(value, 'upper'):
|
||
|
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 _set_dialect(self, value):
|
||
|
if value is None:
|
||
|
self._dialect = mac_eui48
|
||
|
else:
|
||
|
if hasattr(value, 'word_size') and hasattr(value, 'word_fmt'):
|
||
|
self._dialect = value
|
||
|
else:
|
||
|
raise TypeError('custom dialects should subclass mac_eui48!')
|
||
|
|
||
|
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 '-'.join(["%02x" % i for i in self[3:6]]).upper()
|
||
|
elif self._module == _eui64:
|
||
|
return '-'.join(["%02x" % i for i in self[3:8]]).upper()
|
||
|
|
||
|
def is_iab(self):
|
||
|
""":return: True if this EUI is an IAB address, False otherwise"""
|
||
|
return 0x50c2000 <= (self._value >> 12) <= 0x50c2fff
|
||
|
|
||
|
@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):
|
||
|
"""Sets 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.
|
||
|
"""
|
||
|
try:
|
||
|
return(self.version, self._value) == (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
"""
|
||
|
:return: ``False`` if this EUI object is numerically the same as the \
|
||
|
other, ``True`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return(self.version, self._value) != (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __lt__(self, other):
|
||
|
"""
|
||
|
:return: ``True`` if this EUI object is numerically lower in value than \
|
||
|
other, ``False`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return (self.version, self._value) < (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __le__(self, other):
|
||
|
"""
|
||
|
:return: ``True`` if this EUI object is numerically lower or equal in \
|
||
|
value to other, ``False`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return(self.version, self._value) <= (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __gt__(self, other):
|
||
|
"""
|
||
|
:return: ``True`` if this EUI object is numerically greater in value \
|
||
|
than other, ``False`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return (self.version, self._value) > (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
def __ge__(self, other):
|
||
|
"""
|
||
|
:return: ``True`` if this EUI object is numerically greater or equal \
|
||
|
in value to other, ``False`` otherwise.
|
||
|
"""
|
||
|
try:
|
||
|
return(self.version, self._value) >= (other.version, other._value)
|
||
|
except AttributeError:
|
||
|
return NotImplemented
|
||
|
|
||
|
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 and EUI-64, it just returns 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:
|
||
|
eui64_words = ["%02x" % i for i in self[0:3]] + ['ff', 'fe'] + \
|
||
|
["%02x" % i for i in self[3:6]]
|
||
|
|
||
|
return self.__class__('-'.join(eui64_words))
|
||
|
else:
|
||
|
return EUI(str(self))
|
||
|
|
||
|
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.
|
||
|
"""
|
||
|
int_val = 0xfe800000000000000000000000000000
|
||
|
|
||
|
if self.version == 48:
|
||
|
eui64_tokens = ["%02x" % i for i in self[0:3]] + ['ff', 'fe'] + \
|
||
|
["%02x" % i for i in self[3:6]]
|
||
|
int_val += int(''.join(eui64_tokens), 16)
|
||
|
else:
|
||
|
int_val += self._value
|
||
|
|
||
|
# 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.
|
||
|
int_val ^= 0x00000000000000000200000000000000
|
||
|
|
||
|
return IPAddress(int_val, 6)
|
||
|
|
||
|
@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 __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
|
||
|
|