1,906 bytes · the Python implementation · view raw
from .net_mac_address_types import MacValidation
HEX = "0123456789abcdefABCDEF"def _invalid(reason: str) -> MacValidation:
return MacValidation(
valid=False, canonical=None, reason=reason, multicast=None, locally_administered=None, oui=None
)
def validate_mac_address(text: str) -> MacValidation:
"""Check a MAC (EUI-48) address written in any of the four common forms and normalise it to lower-case colon form. Exactly four shapes are accepted: aa:bb:cc:dd:ee:ff, aa-bb-cc-dd-ee-ff, aabb.ccdd.eeff (Cisco) and aabbccddeeff. Nothing is trimmed and single-digit octets are refused, because "a:b:c:d:e:f" is more often a typo than a MAC. """ifnot isinstance(text, str):
return _invalid("not text")
if len(text) == 0:
return _invalid("empty")
seps = [s for s in (":", "-", ".") if s in text]
if len(seps) > 1:
return _invalid("mixed separators")
ifnot seps:
parts = [text]
shape_ok = len(text) == 12else:
parts = text.split(seps[0])
if seps[0] == ".":
shape_ok = len(parts) == 3and all(len(p) == 4for p in parts)
else:
shape_ok = len(parts) == 6and all(len(p) == 2for p in parts)
ifnot shape_ok:
return _invalid("not a recognised MAC address format")
hexdigits = "".join(parts)
for ch in hexdigits:
if ch notin HEX:
return _invalid("not a hex digit")
lower = hexdigits.lower()
octets = [lower[i : i + 2] for i in range(0, 12, 2)]
first = int(octets[0], 16)
return MacValidation(
valid=True,
canonical=":".join(octets),
reason=None,
# The I/G bit is the least significant bit of the first octet, the U/L bit the next.
multicast=(first & 1) == 1,
locally_administered=(first & 2) == 2,
oui="-".join(octets[:3]).upper(),
)