我在 Ubuntu 上工作。如何使用 C 程序获取机器的 MAC 地址或接口 eth0。
12 回答
比所有这些套接字或 shell 疯狂更好的是简单地使用 sysfs:
该文件/sys/class/net/eth0/address
将您的 mac 地址作为简单的字符串,您可以使用//fopen()
读取fscanf()
。fclose()
没有比这更容易的了。
如果您想支持 eth0 以外的其他网络接口(并且您可能想要),那么只需使用opendir()
// on 。readdir()
closedir()
/sys/class/net/
您需要遍历机器上所有可用的接口,并使用ioctl
with SIOCGIFHWADDR
flag 来获取 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);
}
您想查看getifaddrs(3)手册页。您可以使用手册页中的 C 语言示例。您想获取类型为 的地址AF_LINK
。
#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;
}
使用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;
}
我刚刚写了一个并在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;
}
假设 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));
}
在 Linux 上,通过 DBus 使用“网络管理器”服务。
还有一个good'ol shell程序可以被调用并抓取结果(使用C下的exec函数):
$ /sbin/ifconfig | grep HWaddr
一个非常便携的方法是解析这个命令的输出。
ifconfig | awk '$0 ~ /HWaddr/ { print $5 }'
前提是 ifconfig 可以以当前用户身份运行(通常可以)并且安装了 awk(通常是这样)。这会给你机器的mac地址。
扩展@ 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: https://stackoverflow.com/questions/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;
}
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
这是一个 Bash 行,打印所有可用的 mac 地址,环回除外:
for x in `ls /sys/class/net |grep -v lo`; do cat /sys/class/net/$x/address; done
可以从 C 程序执行。