One of the ways I learn a new scripting language is to implement a subroutine to convert a network MAC addr into the IPv6 link-local address, as described in RFC 4862. So here is a quick few lines.

My question to you is:

  • can this code be shrunk any more?
  • is there a more pythonic way of doing this?
  • is there anything non-optimal?
  • is the code future proof to 3.4 python?

Especially with casting a string to an int, and finally back to hex, seems overly weird.

Also, I did recently discover some IP related factory functions, but so far I've not discovered any builtin way of doing this. I'm using python 2.7, but my Linux distro is soon to switch to 3.x.

convert mac addr to ipv6 link local (rfc 4862)

# the mac should become: fe80::2177:02ff:fed2:ff9b

def mac_to_ipv6_linklocal(mac):

  # remove the most common macaddr delimiters, dots, dashes, etc.
  # cast the hex string to an base-16 int for math safety.
  # use xor of 02 on the 2nd most significant hex char.
  # Remove/slice the '0x' off the begining from using hex().

  m = hex(int(mac.translate(None,' .:-'),16)^0x020000000000)[2:]
  return 'fe80::%s:%sff:fe%s:%s' %(m[:4],m[4:6],m[6:8],m[8:12])

print mac_to_ipv6_linklocal(mac)

2 Answers 2


Note that the purpose of the hex() function is to produce a hexadecimal integer literal (a string that can be used in Python source code), which is why it returns a hex value with the 0x prefix. Use the format() function instead, as it gives you much more control over the output.

For example, format(value, '012x') formats the integer value to a 12-digit hexadecimal (using lowercase digits), padding out the result to the minimum width with leading zeros as needed:

>>> mac_value = 0x237702d2ff9b
>>> format(mac_value, '012x')
>>> format(42, '012x')

In this case, I'd actually split out the various bytes of the MAC address binary value that make up the IPv6 parts, and use str.format() to interpolate those into the IPv6 string.

def mac_to_ipv6_linklocal(mac):
    # Remove the most common delimiters; dots, dashes, etc.
    mac_value = int(mac.translate(None, ' .:-'), 16)

    # Split out the bytes that slot into the IPv6 address
    # XOR the most significant byte with 0x02, inverting the 
    # Universal / Local bit
    high2 = mac_value >> 32 & 0xffff ^ 0x0200
    high1 = mac_value >> 24 & 0xff
    low1 = mac_value >> 16 & 0xff
    low2 = mac_value & 0xffff

    return 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(
        high2, high1, low1, low2)

I followed the PEP 8 whitespace recommendations to format the code here; whitespace around operators and after commas, for example.

In Python, we usually don't strive for shorter code; we aim for readable code. Sometimes you can express an algorithm in a very concise and readable way, but avoid code-golfing for the sake of compactness.

Python 3.3 added a ipaddress module; this module does not however include a utility to generate an IPv6 address from a given MAC address.

  • \$\begingroup\$ Cool, it would seem the .format() is an entirely other way around doing things opposed to using the % way. \$\endgroup\$
    – masta
    May 9, 2015 at 16:56

If MAC addresses can have leading zeroes, hex(...)[2:] may not have 12 digits. Then the string interpolation won't line up properly. May need to use str.zfill(12).

For Python 3.x:

str.translate() does not take the optional deletechars argument. Use str.maketrans() to make the translation table.

Use '_' in the xor mask to make it obvious which byte is being changed (don't need to count how many zeroes).

Use f-strings instead of the str.format() method:

def mac_to_ipv6_linklocal(mac):
    mac = mac.translate(str.maketrans('','',' .:-'))

    mac = f"{int(mac,16) ^ 0x02_00_00_00_00_00:012x}"

    return f'fe80::{mac[:4]}:{mac[4:6]}ff:fe{mac[6:8]}:{mac[8:]}'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.