如何使用C程序获取计算机的MAC地址?


Answers:


63

您需要遍历计算机上所有可用的接口,并ioctlSIOCGIFHWADDR标志一起使用以获取mac地址。mac地址将以6字节的二进制数组形式获取。您还希望跳过环回接口。

#include <sys/ioctl.h>
#include <net/if.h> 
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
    struct ifreq ifr;
    struct ifconf ifc;
    char buf[1024];
    int success = 0;

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock == -1) { /* handle error*/ };

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) { /* handle error */ }

    struct ifreq* it = ifc.ifc_req;
    const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));

    for (; it != end; ++it) {
        strcpy(ifr.ifr_name, it->ifr_name);
        if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) {
            if (! (ifr.ifr_flags & IFF_LOOPBACK)) { // don't count loopback
                if (ioctl(sock, SIOCGIFHWADDR, &ifr) == 0) {
                    success = 1;
                    break;
                }
            }
        }
        else { /* handle error */ }
    }

    unsigned char mac_address[6];

    if (success) memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6);
}

1
为什么ifreq和ifconf不需要在前面的结构?这些结构的typedef是否在写于2009年的较新内核中被删除?
Karl P

我认为应该指出,OpenVZ容器没有MAC地址,因此,这些解决方案都无法使用。MAC地址为(假定为)00:00:00:00:00:00
ub3rst4r 2014年

2
它仅在eth0上存在链接时才有效。如果拔下网络电缆(在Ubuntu上),则无法使用。
Florin Petriuc 2014年

5
也许关闭(袜子)?
Art Swri '02

@ArtSwri我认为您是对的,也许是错误hanle goto一些Lable并关闭(袜子)
J.Doe

135

比所有这些套接字或外壳疯狂问题要好得多的是,为此简单地使用sysfs:

文件 /sys/class/net/eth0/address携带的MAC地址作为简单的字符串,你可以读fopen()/ fscanf()/ fclose()。没有比这更容易的了。

而且,如果您想支持eth0以外的其他网络接口(并且可能需要),则只需使用opendir()/ readdir()/ closedir()on即可/sys/class/net/


3
好的答案,但并非在所有情况下都适用。例如,嵌入式系统(尤其是较旧的系统,例如ccs的旧版本,它们没有sysfs,也不能支持它,因为系统本身可能太旧了)
Alex Marshall

3
该问题专门要求使用C解决方案
Charles Salvia 2014年

10
@CharlesSalvia打开和读取系统文件似乎仍然是C解决方案……只要您知道目标系统提供了什么!当然,您的程序将与该类型的系统绑定。但是只需编译程序即可将其与系统的体系结构联系起来。我认为这个答案将在许多(但不是全部)情况下有所帮助。
moodboom

(在WiFi上使用Debian衍生的Bunsen),请参见:/ sys / class / net / wlp2s0 / address
AAAfarmclub


20
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>

int main()
{
  struct ifreq s;
  int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);

  strcpy(s.ifr_name, "eth0");
  if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
    int i;
    for (i = 0; i < 6; ++i)
      printf(" %02x", (unsigned char) s.ifr_addr.sa_data[i]);
    puts("\n");
    return 0;
  }
  return 1;
}

不要忘记对socket()进行错误检查,并在成功打开套接字后将其关闭。
匿名

17

使用getifaddrs,您可以从家庭获得MAC地址AF_PACKET

为了显示每个接口的MAC地址,您可以像下面这样进行:

#include <stdio.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>

int main (int argc, const char * argv[])
{
    struct ifaddrs *ifaddr=NULL;
    struct ifaddrs *ifa = NULL;
    int i = 0;

    if (getifaddrs(&ifaddr) == -1)
    {
         perror("getifaddrs");
    }
    else
    {
         for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
         {
             if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) )
             {
                  struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr;
                  printf("%-8s ", ifa->ifa_name);
                  for (i=0; i <s->sll_halen; i++)
                  {
                      printf("%02x%c", (s->sll_addr[i]), (i+1!=s->sll_halen)?':':'\n');
                  }
             }
         }
         freeifaddrs(ifaddr);
    }
    return 0;
}

伊迪奥


11

我刚刚编写了一个,并在virtualbox中的gentoo上对其进行了测试。

// get_mac.c
#include <stdio.h>    //printf
#include <string.h>   //strncpy
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>   //ifreq
#include <unistd.h>   //close

int main()
{
    int fd;
    struct ifreq ifr;
    char *iface = "enp0s3";
    unsigned char *mac = NULL;

    memset(&ifr, 0, sizeof(ifr));

    fd = socket(AF_INET, SOCK_DGRAM, 0);

    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name , iface , IFNAMSIZ-1);

    if (0 == ioctl(fd, SIOCGIFHWADDR, &ifr)) {
        mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;

        //display mac address
        printf("Mac : %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    }

    close(fd);

    return 0;
}

4

假设c ++代码(c ++ 11)也可以,并且接口已知。

#include <cstdint>
#include <fstream>
#include <streambuf>
#include <regex>

using namespace std;

uint64_t getIFMAC(const string &ifname) {
  ifstream iface("/sys/class/net/" + ifname + "/address");
  string str((istreambuf_iterator<char>(iface)), istreambuf_iterator<char>());
  if (str.length() > 0) {
    string hex = regex_replace(str, std::regex(":"), "");
    return stoull(hex, 0, 16);
  } else {
    return 0;
  }
} 
int main()
{
  string iface = "eth0";
  printf("%s: mac=%016llX\n", iface.c_str(), getIFMAC(iface));
}

1
  1. 在Linux上,通过DBus使用“网络管理器”服务。

  2. 还有一个good'ol shell程序,可以调用它并获取结果(使用C下的exec函数):

$ /sbin/ifconfig | grep HWaddr


0

一种非常可移植的方法是解析此命令的输出。

ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'

提供的ifconfig可以以当前用户身份运行(通常可以)并且已安装awk(通常是)。这将为您提供机器的mac地址。


4
它根本不是便携式的。在Mac OS X上没有任何显示。的输出ifconfig不包含文本HWaddr
dreamlax

必须在今年早些时候在solaris,linux和hpux上执行此操作。
马特

1
好吧,这个概念是可移植的。文本可能需要针对其他平台进行调整。
马特

2
使用shell脚本,即使是与您的脚本一样小的脚本也很难移植。有个笑话说,shell比shell脚本更容易移植:p
sigjuice

8
而且,这不会在我所知道的任何C编译器中进行编译。
BobbyShaftoe

0

扩展@ user175104给出的答案...

std::vector<std::string> GetAllFiles(const std::string& folder, bool recursive = false)
{
  // uses opendir, readdir, and struct dirent.
  // left as an exercise to the reader, as it isn't the point of this OP and answer.
}

bool ReadFileContents(const std::string& folder, const std::string& fname, std::string& contents)
{
  // uses ifstream to read entire contents
  // left as an exercise to the reader, as it isn't the point of this OP and answer.
}

std::vector<std::string> GetAllMacAddresses()
{
  std::vector<std::string> macs;
  std::string address;

  // from: /programming/9034575/c-c-linux-mac-address-of-all-interfaces
  //  ... just read /sys/class/net/eth0/address

  // NOTE: there may be more than one: /sys/class/net/*/address
  //  (1) so walk /sys/class/net/* to find the names to read the address of.

  std::vector<std::string> nets = GetAllFiles("/sys/class/net/", false);
  for (auto it = nets.begin(); it != nets.end(); ++it)
  {
    // we don't care about the local loopback interface
    if (0 == strcmp((*it).substr(-3).c_str(), "/lo"))
      continue;
    address.clear();
    if (ReadFileContents(*it, "address", address))
    {
      if (!address.empty())
      {
        macs.push_back(address);
      }
    }
  }
  return macs;
}

0

可以使用netlink套接字

netlink(7) netlink(3) rtnetlink(7) rtnetlink(3)

#include <assert.h>
#include <stdio.h>
#include <linux/if.h>
#include <linux/rtnetlink.h>
#include <unistd.h>

#define SZ 8192

int main(){

  // Send
  typedef struct {
    struct nlmsghdr nh;
    struct ifinfomsg ifi;
  } Req_getlink;
  assert(NLMSG_LENGTH(sizeof(struct ifinfomsg))==sizeof(Req_getlink));
  int fd=-1;
  fd=socket(AF_NETLINK,SOCK_RAW,NETLINK_ROUTE);
  assert(0==bind(fd,(struct sockaddr*)(&(struct sockaddr_nl){
    .nl_family=AF_NETLINK,
    .nl_pad=0,
    .nl_pid=getpid(),
    .nl_groups=0
  }),sizeof(struct sockaddr_nl)));
  assert(sizeof(Req_getlink)==send(fd,&(Req_getlink){
    .nh={
      .nlmsg_len=NLMSG_LENGTH(sizeof(struct ifinfomsg)),
      .nlmsg_type=RTM_GETLINK,
      .nlmsg_flags=NLM_F_REQUEST|NLM_F_ROOT,
      .nlmsg_seq=0,
      .nlmsg_pid=0
    },
    .ifi={
      .ifi_family=AF_UNSPEC,
      // .ifi_family=AF_INET,
      .ifi_type=0,
      .ifi_index=0,
      .ifi_flags=0,
      .ifi_change=0,
    }
  },sizeof(Req_getlink),0));

  // Receive
  char recvbuf[SZ]={};
  int len=0;
  for(char *p=recvbuf;;){
    const int seglen=recv(fd,p,sizeof(recvbuf)-len,0);
    assert(seglen>=1);
    len += seglen;
    if(((struct nlmsghdr*)p)->nlmsg_type==NLMSG_DONE||((struct nlmsghdr*)p)->nlmsg_type==NLMSG_ERROR)
      break;
    p += seglen;
  }

  struct nlmsghdr *nh=(struct nlmsghdr*)recvbuf;
  for(;NLMSG_OK(nh,len);nh=NLMSG_NEXT(nh,len)){
    if(nh->nlmsg_type==NLMSG_DONE)
      break;

    struct ifinfomsg *ifm=(struct ifinfomsg*)NLMSG_DATA(nh);

    printf("#%d ",ifm->ifi_index);
    #ifdef _NET_IF_H
    #pragma GCC error "include <linux/if.h> instead of <net/if.h>"
    #endif

    // Part 3 rtattr
    struct rtattr *rta=IFLA_RTA(ifm); // /usr/include/linux/if_link.h
    int rtl=RTM_PAYLOAD(nh);

    for(;RTA_OK(rta,rtl);rta=RTA_NEXT(rta,rtl))switch(rta->rta_type){
    case IFLA_IFNAME:printf("%s ",(const char*)RTA_DATA(rta));break;
    case IFLA_ADDRESS:
      printf("hwaddr ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    case IFLA_BROADCAST:
      printf("bcast ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    case IFLA_PERM_ADDRESS:
      printf("perm ");
      for(int i=0;i<5;++i)
        printf("%02X:",*((unsigned char*)RTA_DATA(rta)+i));
      printf("%02X ",*((unsigned char*)RTA_DATA(rta)+5));
      break;
    }

    printf("\n");

  }

  close(fd);
  fd=-1;
  return 0;

}

#1 lo hwaddr 00:00:00:00:00:00 bcast 00:00:00:00:00:00
#2 eth0 hwaddr 57:da:52:45:5b:1a bcast ff:ff:ff:ff:ff:ff perm 57:da:52:45:5b:1a
#3 wlan0 hwaddr 3c:7f:46:47:58:c2 bcast ff:ff:ff:ff:ff:ff perm 3c:7f:46:47:58:c2

-1

这是一条Bash行,它打印所有可用的mac地址,但回送除外:

for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done

可以从C程序执行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.