1.基础用法,获取eth0的mac地址

#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <net/if.h> // struct ifreq

int get_mac(unsigned char binMAC[6])
{
  int sock;
  struct ifreq ifr;
  unsigned char *puc;
  memset(binMAC, 0, 6);
  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock == -1)
  {
    perror("socket");
    return -1;
  }
  strcpy(ifr.ifr_name, "eth0"); // 虚拟机中是ens33
  if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
  {
    close(sock);
    perror("ioctl");
    return -1;
  }
  puc = ifr.ifr_hwaddr.sa_data;
  close(sock);
  memcpy(binMAC, puc, 6);
  return 0;
}

int main(){
  unsigned char mac[6];
  get_mac(mac);
  printf("get_mac: %02x-%02X-%02X-%02X-%02X-%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

此处是通过设置ifr_name,然后通过ioctl获取mac地址,ifr_name一般是eth0或者ens33。无论是否联网都可以获取到,但是当程序运行在其他客户端,不可能要求用户那里的网卡名称也是eth0,所以此方法仅能用作本地测试,或者用命令行方式传入网卡名称,然后获取指定网卡名称的mac地址。可是ifconfig命令不是更好用更方便吗?

2.遍历活动网卡

#include <stdio.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <stdlib.h>
#include <net/if.h> // struct ifreq

int print_macs()
{
  int sock;
  int i = 0, ifCount = 0;
  int code = 0;
  struct ifreq ifr;
  struct ifreq* it = NULL;
  struct ifconf ifc;
  unsigned char* pBuffer = NULL;
  unsigned char *puc = NULL;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock == -1)
  {
    perror("socket");
    return -1;
  }
  
  pBuffer = (unsigned char*)malloc(3000);
  ifc.ifc_len = 3000;
  ifc.ifc_buf = pBuffer;

  code = ioctl(sock, SIOCGIFCONF, &ifc);
  printf("ioctl return %d, ifc_len:3000->%d, ifr size:%d\n", code, ifc.ifc_len, sizeof(struct ifreq));
  if (code < 0)
  {
    free(pBuffer);
    close(sock);
    perror("ioctl");
    return -1;
  }

  it = ifc.ifc_req;
  ifCount =  ifc.ifc_len/sizeof(struct ifreq);
  
  for(i = 0; i < ifCount; ++i){
    strcpy(ifr.ifr_name, it[i].ifr_name);
    code = ioctl(sock, SIOCGIFFLAGS, &ifr);
    printf("\n%d:%s:\n", i, ifr.ifr_name);
    if(0 == code){
      printf("flags:%x\n", ifr.ifr_flags);
    }else{
      printf("flagserror\n");
    }

    code = ioctl(sock, SIOCGIFHWADDR, &ifr);
    if(0 == code){
      puc = ifr.ifr_hwaddr.sa_data;
      printf("mac:%02x-%02x-%02x-%02x-%02x-%02x\n", \
        puc[0], puc[1], puc[2], puc[3], puc[4], puc[5]);
    }else{
      printf("mac:errrrrrrrr\n");
    }
  }

  close(sock);
  free(pBuffer);
  
  return 0;
}

int main(){
  print_macs();
}

ioctl调用SIOCGIFCONF只能获取到活动网卡,如果把ens33或者eth0的网络连接断开,就获取不到了。

3.参考ifconfig

经过方式1和2,只要能获取到网卡的名称就能获取到mac地址了,但是方式2获取不到无连接的网卡,方式1能获取mac地址却不能提前知道网卡名称。只能想着用其他方式获取网卡名称了。

查看了ifconfig的部分源码发现:

interface name是从/proc/net/dev中读取的。如虚拟机从该文件中读到了ens33,

address是从/sys/class/net/ens33/address中读取的,ens33是interface name,address是ens33的其中一个属性,ens33目录下还有多个其他文件。通过文件属性查看是一个symbol link,指向一个device。

小贴士:

ioctl参数:

man ioctl_list

man的输出窗口中输入/sock可以搜索到关键词sock,下面就是sock相关的ioctl的command

sock的ioctl参数主要是ifreq和ifconf

man netdevice可以看到两个结构体的定义

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐