MAC addresses

Every network card is born with a permanent name tag, and a switch quietly learns who sits on which port just by reading the return address on passing mail.

The idea

A MAC address (media access control address) is a 48-bit, 6-byte hardware identifier baked into a network interface, written as six hex pairs like 00:1B:44:11:3A:B7. The first three bytes are the OUI — a block assigned to a vendor — and the last three bytes are chosen by that vendor to make each device unique.

This lives at layer 2 (the link layer), which moves frames between machines on the same local network. It is distinct from an IP address at layer 3, which routes packets across networks. A switch never looks at IP — it forwards by MAC alone, learning which device hangs off which port simply by reading the source address of every frame it sees.

Press play, or step through it yourself.

How it works

Split the address on colons. The first three octets are the OUI (vendor); the last three identify the device. Two flags live in the low two bits of the very first octet: bit 0 (the 0x01 bit) is the I/G bit — set means the frame is multicast or broadcast rather than aimed at one machine — and bit 1 (the 0x02 bit) is the U/L bit — set means the address is locally administered (assigned by software) rather than the burned-in, globally-unique vendor value.

def parse_mac(mac):
    parts = mac.replace("-", ":").split(":")   # accept 00-1B-44 too
    if len(parts) != 6:
        raise ValueError("a MAC has six octets")
    octets = [int(p, 16) for p in parts]        # hex -> int, 0..255

    first = octets[0]
    is_multicast = bool(first & 0x01)            # I/G bit (low bit)
    is_local     = bool(first & 0x02)            # U/L bit (next bit)

    oui = ":".join(f"{b:02X}" for b in octets[:3])   # vendor block
    nic = ":".join(f"{b:02X}" for b in octets[3:])   # device part
    return oui, nic, is_multicast, is_local

print(parse_mac("00:1B:44:11:3A:B7"))
# ('00:1B:44', '11:3A:B7', False, False)  -> unicast, globally unique
print(parse_mac("FF:FF:FF:FF:FF:FF")[2]) # True  -> broadcast (I/G set)

For 00 the low byte is 0000 0000: both flags are clear, so this is a normal unicast, globally-administered address. A broadcast FF:FF:FF:FF:FF:FF has every bit set, including I/G, so a switch floods it to all ports.

Cost / trade-offs

PropertyWhat it costs
Address space248 ≈ 281 trillion addresses; the OUI splits it into vendor blocks
Switch forwarding tableO(hosts) memory — one CAM entry per learned MAC, with an aging timeout
Known destinationO(1) lookup, forwarded out one port only
Unknown / broadcast destinationFlooded to every other port — bandwidth cost grows with port count
ScopeLayer 2, one local segment only; a router (layer 3) rewrites the MAC at each hop

Watch out for

Worked example

Take 00:1B:44:11:3A:B7. OUI 00:1B:44 names the vendor; NIC part 11:3A:B7 is this card’s serial. The first octet 00 is 0000 0000 in binary — I/G bit clear (unicast, aimed at one machine) and U/L bit clear (globally administered, not software-set). So it is a normal, factory-assigned, globally-unique unicast address.

Now the switch. Host A (00:1B:44:11:3A:B7, port 1) sends a frame to B (00:1B:44:22:7C:91, port 2). The switch reads the source and learns A → port 1 in its CAM table. B is unknown, so it floods the frame out ports 2 and 3. B replies; now the switch reads B’s source and learns B → port 2. The next frame from A to B is a known-destination lookup — forwarded straight out port 2 only, never flooded again.

Check yourself

A frame arrives at a switch whose destination MAC is not yet in the CAM table. What does the switch do?

In the first octet of a MAC, which bit being set means “this is a multicast or broadcast address”?