A MAC address looks like a random string of hex pairs, but it's not random at all. Every byte is doing a specific job, and once you know how to read them, a MAC address will tell you a surprising amount: who made the hardware, whether the address is even real, and how your OS derives an IPv6 address from it. Here's the breakdown, with a bit of Python to make it concrete.
The 48 bits
A MAC address is 6 bytes (48 bits), usually written as 6 hex pairs: 00:03:93:AB:12:34. It splits into two halves:
- Bytes 1-3 (the OUI): assigned by the IEEE Registration Authority to a specific organization
- Bytes 4-6: assigned by that organization to a specific device, however it wants
That's the theory, anyway. The two low-order bits of the very first byte break that clean split, and they're the most useful bits in the whole address.
The two bits that matter
Look at the first byte in binary. The two least significant bits are flags, not part of any vendor identifier:
- I/G bit (bit 0): 0 means unicast (one device), 1 means multicast (a group)
- U/L bit (bit 1): 0 means universally administered (globally unique, IEEE-assigned), 1 means locally administered (made up on the spot, not guaranteed unique)
In Python:
def mac_flags(mac: str) -> dict:
first_byte = int(mac.split(":")[0], 16)
return {
"unicast": not bool(first_byte & 0b0000_0001),
"universally_administered": not bool(first_byte & 0b0000_0010),
}
mac_flags("00:03:93:AB:12:34")
# {'unicast': True, 'universally_administered': True}
If the U/L bit is set, don't bother looking up the OUI. There's no vendor to find. The address was generated locally, not assigned by the IEEE.
OUI, MA-M, MA-S, IAB, CID: the registry blocks
"OUI" is the term everyone uses, but the IEEE Registration Authority actually issues five different kinds of blocks, and they matter if you're trying to figure out how big or how new a registration is:
- MA-L (MAC Address Block Large, the classic OUI): a 24-bit prefix, 16,777,216 addresses
- MA-M (Medium): a 28-bit prefix, 1,048,576 addresses
- MA-S (Small, replaced the old OUI-36): a 36-bit prefix, 4,096 addresses
- IAB (Individual Address Block): deprecated in 2014, replaced by MA-S, but still shows up in old hardware
- CID (Company ID): same registry, same format, but explicitly not for building MAC addresses, used for protocol identifiers instead
A vendor with a single IoT product line will often hold an MA-S block instead of a full MA-L, because 4,096 addresses is plenty and it's cheaper to register. If a lookup tool just says "OUI" for everything, you're losing that signal.
Randomization broke the "MAC = device" assumption
For a long time, "look up the OUI" was a reasonable way to answer "what kind of device is this." Then iOS 14, and most modern Android and desktop Wi-Fi stacks, started randomizing the MAC address per network (and sometimes per connection), specifically to stop this kind of tracking.
A randomized address almost always has the U/L bit set, which is exactly the bit from the flags above. So the same one-bit check that tells you "this isn't a registered vendor address" is also your best signal that you're looking at a randomized address, not a spoofed or malformed one. It's not a certainty (some real hardware legitimately uses locally administered addresses too), but it's a solid prior.
Deriving an EUI-64 / IPv6 link-local address
If you've ever wondered how an interface without DHCPv6 still gets an IPv6 address, this is how, at least under SLAAC with the (now largely deprecated but still common) modified EUI-64 method:
- Split the 48-bit MAC into two 24-bit halves
- Insert
FFFEbetween them, making it 64 bits - Flip the U/L bit of the first byte
00:03:93:AB:12:34
to 00:03:93:FF:FE:AB:12:34 (insert FFFE)
to 02:03:93:FF:FE:AB:12:34 (flip the U/L bit: 0x00 to 0x02)
That becomes the interface identifier, appended to fe80::/64:
fe80::0203:93ff:feab:1234
Same input, same deterministic output, every time. That's also exactly why privacy extensions (RFC 4941) exist: a stable EUI-64-derived address is as trackable as the MAC it came from.
Putting it together
None of this requires a database. U/L bit, I/G bit, and EUI-64 derivation are pure bit math, no registry lookup needed. Where you do need a database is the vendor name itself, and that part changes: the IEEE adds and reassigns blocks continuously, so a hardcoded vendor table goes stale. For quick one-offs I usually just query a synced OUI database (I've been using macadress.com's API for this) rather than maintaining my own copy of the registry.
If you want to try the bit math yourself, any MAC address will do, even a locally administered one will confirm the flags are working correctly, since it'll come back "not universally administered" instead of throwing a vendor name at you.
Top comments (0)