获取MAC地址

问题:获取MAC地址

我需要一种跨平台的方法来在运行时确定计算机的MAC地址。对于Windows,可以使用“ wmi”模块,在Linux下,我能找到的唯一方法是运行ifconfig并在其输出中运行正则表达式。我不喜欢使用只能在一个OS上运行的程序包,而且更不用说容易出错的语法解析另一个程序的输出了。

有谁知道跨平台方法(Windows和Linux)方法来获取MAC地址?如果没有,还有谁比我上面列出的方法更优雅?

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?


回答 0

Python 2.5包含一个uuid实现(至少在一个版本中),该实现需要mac地址。您可以轻松地将mac查找功能导入您自己的代码中:

from uuid import getnode as get_mac
mac = get_mac()

返回值是作为48位整数的mac地址。

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.


回答 1

在Linux下针对此问题的纯python解决方案,用于获取特定本地接口的MAC,最初由vishnubob作为注释发布,并在本activestate食谱中由Ben Mackey进行了改进

#!/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')

这是Python 3兼容的代码:

#!/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()

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()

回答 2

netifaces是一个很好的模块,可用于获取mac地址(和其他地址)。它是跨平台的,比使用套接字或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'}]

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'}]


回答 3

您应该注意的另一件事是,uuid.getnode()可以通过返回随机的48位数字来伪造MAC地址,这可能不是您所期望的。另外,也没有明显的迹象表明MAC地址已被伪造,但是您可以通过调用getnode()两次并查看结果是否变化来检测到它。如果两个调用都返回了相同的值,则说明您具有MAC地址,否则得到的是伪造的地址。

>>> 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.

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.

回答 4

有时我们有多个网络接口。

找出特定接口的mac地址的一种简单方法是:

def getmac(interface):

  try:
    mac = open('/sys/class/net/'+interface+'/address').readline()
  except:
    mac = "00:00:00:00:00:00"

  return mac[0:17]

调用方法很简单

myMAC = getmac("wlan0")

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")

回答 5

从这里使用我的答案:https : //stackoverflow.com/a/18031868/2362361

重要的是要知道您想要MAC到哪个iface,因为可能存在很多(蓝牙,几个nic等)。

当您知道需要使用MAC的iface的IP netifaces(使用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

测试:

>>> mac_for_ip('169.254.90.191')
'2c:41:38:0a:94:8b'

Using my answer from here: https://stackoverflow.com/a/18031868/2362361

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'

回答 6

您可以使用跨平台的psutil进行此操作:

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]

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]

回答 7

请注意,您可以使用条件导入在python中构建自己的跨平台库。例如

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

这将允许您使用os.system调用或特定于平台的库。

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.


回答 8

如果您不介意依赖,则跨平台的getmac软件包将对此有效。它适用于Python 2.7+和3.4+。它将尝试许多不同的方法,直到获得地址或返回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)

免责声明:我是软件包的作者。

更新(2019年1月14日):该软件包现在仅支持Python 2.7+和3.4+。如果您需要使用旧版本的Python(2.5、2.6、3.2、3.3),则仍可以使用该软件包的旧版本。

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).


回答 9

要获取eth0接口MAC地址,

import psutil

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

for interface in nics:
   if interface.family == 17:
      print(interface.address)

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)

回答 10

我不知道统一的方式,但是以下内容可能对您有用:

http://www.codeguru.com/Cpp/IN/network/networkinformation/article.php/c5451

在这种情况下,我要做的就是将它们包装成一个函数,并根据操作系统运行适当的命令,根据需要进行解析,并仅返回所需格式的MAC地址。当然,除了您只需要执行一次,并且与主代码相比,它看起来更干净以外,其他所有内容都一样。

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.


回答 11

对于Linux,让我介绍一个shell脚本,该脚本将显示mac地址并允许对其进行更改(MAC嗅探)。

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

减少参数(我不是专家)可以尝试:

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

要更改MAC,我们可以这样做:

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

会将mac地址更改为00:80:48:BA:d1:30(临时,重新启动后将还原为实际地址)。

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).


回答 12

或者,

import uuid
mac_id=(':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)

Alternatively,

import uuid
mac_id=(':'.join(['{:02x}'.format((uuid.getnode() >> ele) & 0xff)

回答 13

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.

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.