Getting MAC Address
Asked Answered
T

17

137

I need a cross platform method of determining the MAC address of a computer at run time. For windows the 'wmi' module can be used and the only method under Linux I could find was to run ifconfig and run a regex across its output. I don't like using a package that only works on one OS, and parsing the output of another program doesn't seem very elegant not to mention error prone.

Does anyone know a cross platform method (windows and linux) method to get the MAC address? If not, does anyone know any more elegant methods then those I listed above?

Trapp answered 1/10, 2008 at 18:51 Comment(2)
There are many potentially helpful answers here! How can I get the IP address of eth0 in Python?Reynaud
Note that accepted answer is wrong: Running at the same time in two different processes on the same computer, results in 2 different values.Munich
W
191

Python 2.5 includes an uuid implementation which (in at least one version) needs the mac address. You can import the mac finding function into your own code easily:

from uuid import getnode as get_mac
mac = get_mac()

The return value is the mac address as 48 bit integer.

Wayfarer answered 1/10, 2008 at 19:6 Comment(10)
I just tried this on a Windows box and it works great... except that the laptop I tried it on has 2 NICs (one wireless) and there's no way to determine which MAC it returned. Just a word of warning if you want to use this code.Amor
Thats a feature of the windows device enumerator, whatever method you use to get the address - you have to be carefull if using it as a unique ID.Uhlan
Just a warning: If you look at the getnode documentation it says that it will return a random long if it fails to detect the mac: "If all attempts to obtain the hardware address fail, we choose a random 48-bit number with its eighth bit set to 1 as recommended in RFC 4122." So check that eighth bit!Mauricemauricio
hex(mac) to get the familiar hex format of the macRashid
Or ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2)) for an uppercase MAC with each byte separated by a colon.Taxdeductible
getnode() returns something different every time I restart my windows 7 laptop.Weak
@Mauricemauricio I have a valid mac address where the least significant bit of the first octect is 1. So it seems this is not enough to verify the return value. However, mhawke provides a solution in his answer below that might work.Emelia
Or ':'.join((itertools.starmap(operator.add, zip(*([iter("%012X" % mac)] * 2))))), an alternative way of doing it @TaxdeductibleGlowing
that bit me in the ass - on an embedded system it returned random value if ethernet jack not connected, however reading /sys/class/net/eth0/address (or parsing output of ifconfig) works either connected or notThermostatics
@Taxdeductible - that works excellent!! Very cryptic, but nice one liner that does just what I needed. ':'.join(("%012X" % mac)[i:i+2] for i in range(0, 12, 2))Iniquitous
Z
96

The pure python solution for this problem under Linux to get the MAC for a specific local interface, originally posted as a comment by vishnubob and improved by on Ben Mackey in this activestate recipe

#!/usr/bin/python

import fcntl, socket, struct

def getHwAddr(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', ifname[:15]))
    return ':'.join(['%02x' % ord(char) for char in info[18:24]])

print getHwAddr('eth0')

This is the Python 3 compatible code:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import fcntl
import socket
import struct


def getHwAddr(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', bytes(ifname, 'utf-8')[:15]))
    return ':'.join('%02x' % b for b in info[18:24])


def main():
    print(getHwAddr('enp0s8'))


if __name__ == "__main__":
    main()
Zoellick answered 25/1, 2011 at 1:57 Comment(1)
Beautiful, works like a charm!Auriga
D
43

netifaces is a good module to use for getting the mac address (and other addresses). It's crossplatform and makes a bit more sense than using socket or uuid.

import netifaces

netifaces.interfaces()
# ['lo', 'eth0', 'tun2']

netifaces.ifaddresses('eth0')[netifaces.AF_LINK]
# [{'addr': '08:00:27:50:f2:51', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]

Depressomotor answered 1/10, 2008 at 18:55 Comment(1)
Used it personally, requires a specific install/compile on each system but works well.Julianjuliana
K
31

Sometimes we have more than one net interface.

A simple method to find out the mac address of a specific interface, is:

def getmac(interface):
    try:
        mac = open('/sys/class/net/'+interface+'/address').readline()
    except:
        mac = "00:00:00:00:00:00"
    return mac[0:17]

to call the method is simple

myMAC = getmac("wlan0")
Kanal answered 18/8, 2015 at 19:16 Comment(3)
Not super useful for OS X or Windows. :|Messily
Perfect for Pis!Synthesize
return mac.strip() may be easier to get rid of the newline ;-)Forwent
M
23

One other thing that you should note is that uuid.getnode() can fake the MAC addr by returning a random 48-bit number which may not be what you are expecting. Also, there's no explicit indication that the MAC address has been faked, but you could detect it by calling getnode() twice and seeing if the result varies. If the same value is returned by both calls, you have the MAC address, otherwise you are getting a faked address.

>>> print uuid.getnode.__doc__
Get the hardware address as a 48-bit positive integer.

    The first time this runs, it may launch a separate program, which could
    be quite slow.  If all attempts to obtain the hardware address fail, we
    choose a random 48-bit number with its eighth bit set to 1 as recommended
    in RFC 4122.
Mcavoy answered 2/10, 2008 at 3:49 Comment(2)
Not that the value of the 8th is 1 precisely because it cannot be used by a network card. Just checking this bit is enough to detect whether the address is faked or not.Blackstock
The check is just done with: if (mac >> 40)%2 : raise OSError, "The system does not seem to have a valid MAC"Blackstock
F
9

Using my answer from here: https://mcmap.net/q/168387/-python-get-mac-address-of-only-the-connected-local-nic-duplicate

It would be important to know to which iface you want the MAC for since many can exist (bluetooth, several nics, etc.).

This does the job when you know the IP of the iface you need the MAC for, using netifaces (available in PyPI):

import netifaces as nif
def mac_for_ip(ip):
    'Returns a list of MACs for interfaces that have given IP, returns None if not found'
    for i in nif.interfaces():
        addrs = nif.ifaddresses(i)
        try:
            if_mac = addrs[nif.AF_LINK][0]['addr']
            if_ip = addrs[nif.AF_INET][0]['addr']
        except IndexError, KeyError: #ignore ifaces that dont have MAC or IP
            if_mac = if_ip = None
        if if_ip == ip:
            return if_mac
    return None

Testing:

>>> mac_for_ip('169.254.90.191')
'2c:41:38:0a:94:8b'
Freightage answered 3/8, 2013 at 10:40 Comment(0)
W
8

You can do this with psutil which is cross-platform:

import psutil
nics = psutil.net_if_addrs()
print [j.address for j in nics[i] for i in nics if i!="lo" and j.family==17]
Waggish answered 10/12, 2016 at 14:53 Comment(1)
import socket then [addr.address for n in nics for addr in nics[n] if n == 'enp4s0' and addr.family == socket.AF_PACKET] results in ['ab:cd:ef:01:02:03']Ene
G
6

The cross-platform getmac package will work for this, if you don't mind taking on a dependency. It works with Python 2.7+ and 3.4+. It will try many different methods until either getting a address or returning None.

from getmac import get_mac_address
eth_mac = get_mac_address(interface="eth0")
win_mac = get_mac_address(interface="Ethernet 3")
ip_mac = get_mac_address(ip="192.168.0.1")
ip6_mac = get_mac_address(ip6="::1")
host_mac = get_mac_address(hostname="localhost")
updated_mac = get_mac_address(ip="10.0.0.1", network_request=True)

Disclaimer: I am the author of the package.

Update (Jan 14 2019): the package now only supports Python 2.7+ and 3.4+. You can still use an older version of the package if you need to work with an older Python (2.5, 2.6, 3.2, 3.3).

Goodard answered 2/8, 2018 at 6:28 Comment(2)
Standard output for above variables: None None None None 00:00:00:00:00:00 NoneAston
Those are just examples of usage. You need to replace the arguments with the values applicable to your system, which is very likely why you're getting null results. For example, "eth0" is not applicable to a Windows system, and "Ethernet 3" isn't applicable to a Linux system, but both are included above as examples so you know how to use the library on both platforms. Note: "localhost" will always return "00:00:00:00:00:00".Goodard
S
2

To get the eth0 interface MAC address,

import psutil

nics = psutil.net_if_addrs()['eth0']

for interface in nics:
   if interface.family == 17:
      print(interface.address)
Spicy answered 4/10, 2019 at 13:49 Comment(0)
J
1

Note that you can build your own cross-platform library in python using conditional imports. e.g.

import platform
if platform.system() == 'Linux':
  import LinuxMac
  mac_address = LinuxMac.get_mac_address()
elif platform.system() == 'Windows':
  # etc

This will allow you to use os.system calls or platform-specific libraries.

Japheth answered 1/10, 2008 at 22:9 Comment(1)
that's not really an answer - just a commentAfrikaans
A
1

nice actually return a dictionary within the dictionary it returns a list and within the list, it returns a staple. but nics['Ethernet'][0].address iteration solve the problem.

import psutil

nics = psutil.net_if_addrs()

mac_address = nics['Ethernet'][0].address

print(mac_address)
Abvolt answered 1/8, 2022 at 5:52 Comment(0)
F
1

This cross-platform code does not 100% work on Windows. This works on Windows:

import psutil
print([(k, addr.address) for k, v in psutil.net_if_addrs().items() for addr in v if addr.family == -1])

Example:

[
    ('Local Area Connection', '01-23-45-67-89-0A'), 
    ('Wireless Network Connection', '23-45-67-89-0A-BC'), 
    ('Bluetooth Network Connection', '45-67-89-0A-BC-DE'), 
    ('isatap.{01ABCDEF-0123-4567-890A-0123456789AB}', '00-00-00-00-00-00-00-01')
]
Flournoy answered 11/1, 2023 at 7:56 Comment(0)
E
0

I dont know of a unified way, but heres something that you might find useful:

http://www.codeguru.com/Cpp/I-N/network/networkinformation/article.php/c5451

What I would do in this case would be to wrap these up into a function, and based on the OS it would run the proper command, parse as required and return only the MAC address formatted as you want. Its ofcourse all the same, except that you only have to do it once, and it looks cleaner from the main code.

Esque answered 1/10, 2008 at 19:2 Comment(0)
L
0

To get the MAC address as a 48-bit int, one can use uuid.getnode():

import uuid

uuid.getnode()
12345678901234

To format this as a colon-separated hex-string, you can use

":".join(f"{b:02x}" for b in uuid.getnode().to_bytes(6))
0b:3a:73:ce:2f:f2

(In Python 3.10 and lower, you'll have to add the byteorder arg: .to_bytes(6, byteorder='big').)

Leonidaleonidas answered 7/10, 2023 at 16:44 Comment(1)
Just a small comment, you need to add byte orders argument, otherwise you will get TypeError: to_bytes() missing required argument 'byteorder' (pos 2) The proper line looks: ":".join(f"{b:02x}" for b in uuid.getnode().to_bytes(6, "big"))Turnage
P
-1

Alternatively,

import uuid
mac_id=(':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)
Parish answered 28/1, 2019 at 10:19 Comment(0)
R
-2

For Linux let me introduce a shell script that will show the mac address and allows to change it (MAC sniffing).

 ifconfig eth0 | grep HWaddr |cut -dH -f2|cut -d\  -f2
 00:26:6c:df:c3:95

Cut arguements may dffer (I am not an expert) try:

ifconfig etho | grep HWaddr
eth0      Link encap:Ethernet  HWaddr 00:26:6c:df:c3:95  

To change MAC we may do:

ifconfig eth0 down
ifconfig eth0 hw ether 00:80:48:BA:d1:30
ifconfig eth0 up

will change mac address to 00:80:48:BA:d1:30 (temporarily, will restore to actual one upon reboot).

Rummel answered 26/7, 2013 at 14:46 Comment(1)
Does this have to do anything with the question?Coon
M
-9

For Linux you can retrieve the MAC address using a SIOCGIFHWADDR ioctl.

struct ifreq    ifr;
uint8_t         macaddr[6];

if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
    return -1;

strcpy(ifr.ifr_name, "eth0");

if (ioctl(s, SIOCGIFHWADDR, (void *)&ifr) == 0) {
    if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
        memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
        return 0;
... etc ...

You've tagged the question "python". I don't know of an existing Python module to get this information. You could use ctypes to call the ioctl directly.

Mauritius answered 1/10, 2008 at 19:12 Comment(1)
-1 He wants a python solution, so you go and give him the same regurgitated C solution plastered throughout the web.Julianjuliana

© 2022 - 2024 — McMap. All rights reserved.