Media access control (MAC) addresses play an important role in local-area networks. They also pack a lot of information into 48-bit hexadecimal strings!
The macaddress library makes it easy to evaluate the properties of MAC addresses and the extended identifiers of which they are subclasses.
macaddress is available on GitHub at https://github.com/critical-path/macaddress.
If you do not have pip version 18.1 or higher, then run the following command from your shell.
[user@host ~]$ sudo pip install --upgrade pip
To install macaddress with test-related dependencies, run the following command from your shell.
[user@host ~]$ sudo pip install --editable git+https://github.com/critical-path/macaddress.git#egg=macaddress[test]
To install it without test-related dependencies, run the following command from your shell.
[user@host ~]$ sudo pip install git+https://github.com/critical-path/macaddress.git
(If necessary, replace pip
with pip3
.)
While macaddress contains multiple classes, the only one with which you need to interact directly is MediaAccessControlAddress
.
Import MediaAccessControlAddress
.
>>> from macaddress import MediaAccessControlAddress
Instantiate MediaAccessControlAddress
by passing in a MAC address in plain, hyphen, colon, or dot notation.
>>> mac = MediaAccessControlAddress("a0b1c2d3e4f5")
>>> mac = MediaAccessControlAddress("a0-b1-c2-d3-e4-f5")
>>> mac = MediaAccessControlAddress("a0:b1:c2:d3:e4:f5")
>>> mac = MediaAccessControlAddress("a0b1.c2d3.e4f5")
To determine whether the MAC address is a broadcast, a multicast (layer-two), or a unicast address, access its is_broadcast
, is_multicast
, and is_unicast
properties.
>>> print(mac.is_broadcast)
False
>>> print(mac.is_multicast)
False
>>> print(mac.is_unicast)
True
To determine whether the MAC address is a universally-administered address (UAA) or a locally-administered address (LAA), access its is_uaa
and is_laa
properties.
>>> print(mac.is_uaa)
True
>>> print(mac.is_laa)
False
To work with the MAC address's octets, access its octets
property, which contains six Octet
objects.
>>> print(mac.octets)
[Octet('a0'), Octet('b1'), Octet('c2'), Octet('d3'), Octet('e4'), Octet('f5')]
To determine whether the MAC address is an extended unique identifier (EUI), an extended local identifier (ELI), or unknown, access its type
property.
>>> print(mac.type)
unique
To determine whether the MAC address has an organizationally-unique identifier (OUI) or a company ID (CID), access its has_oui
and has_cid
properties.
>>> print(mac.has_oui)
True
>>> print(mac.has_cid)
False
To view the decimal equivalent of the MAC address, access its decimal
property.
>>> print(mac.decimal)
176685338322165
To view the binary equivalent of the MAC address, access its binary
and reverse_binary
properties. With binary
, the most-significant digit of each octet appears first. With reverse_binary
, the least-significant digit of each octet appears first.
>>> print(mac.binary)
101000001011000111000010110100111110010011110101
>>> print(mac.reverse_binary)
000001011000110101000011110010110010011110101111
To return the MAC address's two "fragments," call the to_fragments
method. For an EUI, this means the 24-bit OUI as the first fragment and the remaining interface-specific bits as the second fragment. For an ELI, this means the 24-bit CID as the first fragment and the remaining interface-specific bits as the second fragment.
>>> fragments = mac.to_fragments()
>>> print(fragments)
('a0b1c2', 'd3e4f5')
To return the MAC address in different notations, call the to_plain_notation
, to_hyphen_notation
, to_colon_notation
, and to_dot_notation
methods.
>>> plain = mac.to_plain_notation()
>>> print(plain)
a0b1c2d3e4f5
>>> hyphen = mac.to_hyphen_notation()
>>> print(hyphen)
a0-b1-c2-d3-e4-f5
>>> colon = mac.to_colon_notation()
>>> print(colon)
a0:b1:c2:d3:e4:f5
>>> dot = mac.to_dot_notation()
>>> print(dot)
a0b1.c2d3.e4f5
# Import `pprint.pprint` and `macaddress.MediaAccessControlAddress`.
>>> from pprint import pprint
>>> from macaddress import MediaAccessControlAddress
# Identify the start and end of the range.
>>> start_mac = MediaAccessControlAddress("a0b1c2d3e4f5")
>>> end_mac = MediaAccessControlAddress("a0b1c2d3e4ff")
# Create a list containing one `MediaAccessControlAddress` object
# for each address in the range.
>>> mac_range = [
... MediaAccessControlAddress(format(decimal, "x"))
... for decimal in range(start_mac.decimal, end_mac.decimal + 1)
... ]
# Do something useful with the results, such as returning
# the colon notation of each MAC address in the list.
>>> colons = [
... mac.to_colon_notation() for mac in mac_range
... ]
>>> pprint(colons)
["a0:b1:c2:d3:e4:f5",
"a0:b1:c2:d3:e4:f6",
"a0:b1:c2:d3:e4:f7",
"a0:b1:c2:d3:e4:f8",
"a0:b1:c2:d3:e4:f9",
"a0:b1:c2:d3:e4:fa",
"a0:b1:c2:d3:e4:fb",
"a0:b1:c2:d3:e4:fc",
"a0:b1:c2:d3:e4:fd",
"a0:b1:c2:d3:e4:fe",
"a0:b1:c2:d3:e4:ff"]
# Import `functools.reduce`, `pprint.pprint`, and
# `macaddress.MediaAccessControlAddress`.
>>> from functools import reduce
>>> from pprint import pprint
>>> from macaddress import MediaAccessControlAddress
# Define `transform`, which is our map function.
>>> def transform(mac, attributes):
... transformed = {}
... transformed[mac.normalized] = {}
... for attribute in attributes:
... transformed[mac.normalized][attribute] = getattr(mac, attribute)
... return transformed
...
# Define `fold`, which is our reduce function.
>>> def fold(current_mac, next_mac):
... for key, value in next_mac.items():
... if key in current_mac:
... pass
... else:
... current_mac[key] = value
... return current_mac
...
# Define `map_reduce`, which calls `functools.reduce`, `transform`, and `fold`.
>>> def map_reduce(macs, attributes):
... return reduce(fold, [transform(mac, attributes) for mac in macs])
...
# Identify addresses of interest.
>>> addresses = [
... "a0:b1:c2:d3:e4:f5",
... "a0:b1:c2:d3:e4:f6",
... "a0:b1:c2:d3:e4:f7",
... "a0:b1:c2:d3:e4:f8",
... "a0:b1:c2:d3:e4:f9",
... "a0:b1:c2:d3:e4:fa",
... "a0:b1:c2:d3:e4:fb",
... "a0:b1:c2:d3:e4:fc",
... "a0:b1:c2:d3:e4:fd",
... "a0:b1:c2:d3:e4:fe",
... "a0:b1:c2:d3:e4:ff"
... ]
# Create a list containing one `MediaAccessControlAddress` object
# for each address of interest.
>>> macs = [
... MediaAccessControlAddress(address) for address in addresses
... ]
# Create a list with attributes of interest.
>>> attributes = [
... "is_unicast",
... "is_uaa"
... ]
# Call `map_reduce`, passing in the lists of `MediaAccessControlAddress`
# objects and attributes.
>>> mapped_reduced = map_reduce(macs, attributes)
>>> pprint(mapped_reduced)
{"a0b1c2d3e4f5": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4f6": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4f7": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4f8": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4f9": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4fa": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4fb": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4fc": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4fd": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4fe": {"is_uaa": True, "is_unicast": True},
"a0b1c2d3e4ff": {"is_uaa": True, "is_unicast": True}}
# Import `json.dumps`.
>>> from json import dumps
# Identify the addresses and attributes of interest.
>>> unserialized = {
... "a0b1c2d3e4f5": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4f6": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4f7": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4f8": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4f9": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4fa": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4fb": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4fc": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4fd": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4fe": {"is_uaa": True, "is_unicast": True},
... "a0b1c2d3e4ff": {"is_uaa": True, "is_unicast": True}
... }
# Call `json.dumps` on the unserialized addresses.
>>> serialized = dumps(unserialized, indent=2)
>>> print(serialized)
{
"a0b1c2d3e4f5": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4f6": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4f7": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4f8": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4f9": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4fa": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4fb": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4fc": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4fd": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4fe": {
"is_uaa": true,
"is_unicast": true
},
"a0b1c2d3e4ff": {
"is_uaa": true,
"is_unicast": true
}
}
To conduct testing, run the following commands from your shell.
[user@host macaddress]$ flake8 --count --ignore E125 macaddress
[user@host macaddress]$ pytest --cov --cov-report=term-missing
The macaddress library is also available in the following languages: