Skip to content

Instantly share code, notes, and snippets.

@pklaus
Last active May 31, 2025 14:15
Show Gist options
  • Select an option

  • Save pklaus/9638536 to your computer and use it in GitHub Desktop.

Select an option

Save pklaus/9638536 to your computer and use it in GitHub Desktop.
Generating Random MAC Addresses with Python

The mini-tool has a CLI-Interface with the following options:

  • Unicast or Multicast? Default: Unicast
  • Locally Administered or Globally Unique? Default: Locally Administered
  • Prescribe specific OUI (overwrites the above two)

TODO

  • Add an option to generate a number of MACs without collisions.

Resources

#!/usr/bin/env python
import random
def random_bytes(num=6):
return [random.randrange(256) for _ in range(num)]
def generate_mac(uaa=False, multicast=False, oui=None, separator=':', byte_fmt='%02x'):
mac = random_bytes()
if oui:
if type(oui) == str:
oui = [int(chunk) for chunk in oui.split(separator)]
mac = oui + random_bytes(num=6-len(oui))
else:
if multicast:
mac[0] |= 1 # set bit 0
else:
mac[0] &= ~1 # clear bit 0
if uaa:
mac[0] &= ~(1 << 1) # clear bit 1
else:
mac[0] |= 1 << 1 # set bit 1
return separator.join(byte_fmt % b for b in mac)
def main():
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--uaa', action='store_true', help='generates a universally administered address (instead of LAA otherwise)')
parser.add_argument('--multicast', action='store_true', help='generates a multicast MAC (instead of unicast otherwise)')
parser.add_argument('--oui', help='enforces a specific organizationally unique identifier (like 00:60:2f for Cisco)')
parser.add_argument('--byte-fmt', default='%02x', help='The byte format. Set to %02X for uppercase hex formatting.')
parser.add_argument('--separator', default=':', help="The byte separator character. Defaults to ':'.")
args = parser.parse_args()
print(generate_mac(oui=args.oui, uaa=args.uaa, multicast=args.multicast, separator=args.separator, byte_fmt=args.byte_fmt))
if __name__ == '__main__':
main()
@fatestapestry

fatestapestry commented Mar 13, 2018

Copy link
Copy Markdown

@fquinto - has a bug. sometimes gives blank chars in mac string.

@AleaLabs

AleaLabs commented Jun 1, 2018

Copy link
Copy Markdown

@fquinto @jonathanelscpt - fix the issue by using "%012x" which gives leading zeros in the answer

@larshb

larshb commented Jan 17, 2019

Copy link
Copy Markdown
':'.join("%02x"%random.randint(0, 255) for _ in range(5))

@fquinto

fquinto commented Apr 11, 2019

Copy link
Copy Markdown

Now, I like to use this:

':'.join('%02x'%random.randrange(256) for _ in range(5))

Why? Because this:

t1 = timeit.Timer("':'.join(('%012x' % random.randint(0, 0xFFFFFFFFFFFF))[i:i+2] for i in range(0, 12, 2))", "import random")
t2 = timeit.Timer("':'.join('%02x'%random.randint(0, 255) for _ in range(5))", "import random")
t3 = timeit.Timer("':'.join('%02x'%random.randrange(2**8) for _ in range(5))", "import random")
t4 = timeit.Timer("':'.join('%02x'%random.randrange(256) for _ in range(5))", "import random")
for t in t1, t2, t3, t4:
    t.timeit()
# 9.202233295989572
# 6.3932015799946385
# 5.115052343986463
# 5.105975616010255

@ahmedbilal

Copy link
Copy Markdown

i have a noob question to ask,don't hate me for this because i am genuinely confused,but where am i supposed to type this code into?
A code editor? if yes how will it work?

It is Python Code. You need to create a file like code.py and put the code in it.
Then, run the code by entering the following command on terminal/command prompt
python code.py

@pklaus

pklaus commented Sep 4, 2019

Copy link
Copy Markdown
Author

Thanks for all the comments. I updated my code today with the possibility to specify LAA/UAA and Unicast/Multicast or even prescribe a specific OUI. The code is optimized for clarity not speed but still quite fast compared to a simplistic approach neglecting UAA/LAA or Unicast/Multicast:

$ python -m timeit 'from randmac import generate_mac' 'generate_mac()'
50000 loops, best of 5: 9.73 usec per loop
$ python -m timeit "import random" "':'.join('%02x'%random.randrange(256) for _ in range(6))"
50000 loops, best of 5: 7.86 usec per loop

For any application that I could think of, generating a random MAC in under 10 ฮผs shouln't be a showstopper ๐Ÿ˜„.

@jakabk

jakabk commented Oct 24, 2019

Copy link
Copy Markdown

Or just

def rand_mac():
  ...

or

   return ('{:02x}' * 6).format(*[random.randrange(256) for _ in range(6)])

@2foil

2foil commented Sep 28, 2020

Copy link
Copy Markdown

Here is a more dense version ๐Ÿ˜†:

>>> random_mac = lambda : ":".join([f"{random.randint(0, 255):02x}" for _ in range(6)])

>>> random_mac()
'b8:de:1a:98:8f:ab'

>>> random_mac()
'e4:c4:3e:40:27:7b'

@jpramosi

Copy link
Copy Markdown

A non-random version for generating a MAC address from a string:

def generate_mac(text: str, multicast=False, universally_administered=False) -> str:
    from hashlib import sha256

    h = sha256(text.encode("utf-8")).digest()

    # https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast_(I/G_bit)
    # https://gist.github.com/pklaus/9638536
    bit = h[0]
    if multicast:
        bit |= 1  # set bit 0
    else:
        bit &= ~1  # clear bit 0
    if universally_administered:
        bit &= ~(1 << 1)  # clear bit 1
    else:
        bit |= 1 << 1  # set bit 1

    m0 = int.from_bytes([bit], "little")
    m1 = int.from_bytes([h[4]], "little")
    m2 = int.from_bytes([h[8]], "little")
    m3 = int.from_bytes([h[8]], "little")
    m4 = int.from_bytes([h[16]], "little")
    m5 = int.from_bytes([h[20]], "little")

    return f"{m0:02x}:{m1:02x}:{m2:02x}:{m3:02x}:{m4:02x}:{m5:02x}"

It might have a high hash collision rate, but it's still pretty useful to me. I use it to generate a MAC from a hostname to stop my router from delivering an old IP address from my recreated virtual machines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment