Linux系统
在Linux系统,可以通过系统调用函数ioctl很容易就获取到服务器的mac地址。
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
int sock, if_count, i;
struct ifconf ifc;
struct ifreq ifr[10];
unsigned char mac[6];
memset(&ifc, 0, sizeof(struct ifconf));
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifc.ifc_len = 10 * sizeof(struct ifreq);
ifc.ifc_buf = (char *)ifr;
//获取所有网卡信息
ioctl(sock, SIOCGIFCONF, (char *)&ifc);
if_count = ifc.ifc_len / (sizeof(struct ifreq));
for (i = 0; i < if_count; i++) {
if (ioctl(sock, SIOCGIFHWADDR, &ifr[i]) == 0) {
memcpy(mac, ifr[i].ifr_hwaddr.sa_data, 6);
printf("eth: %s, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", ifr[i].ifr_name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
return 0;
}
核心逻辑主要分两个部分,第一个部分是获取网卡,主要通过下面的函数完成:
ioctl(sock, SIOCGIFCONF, (char *)&ifc);
它的信息保存在结构体struct ifconf
中,有可能不止一个。获取到的信息保存在ifc_buf
中。
第二个逻辑就是根据网卡的名字去获取mac地址,主要用下面的函数完成:
ioctl(sock, SIOCGIFHWADDR, &ifr[i]);
通过上面简单的两步,就能获取到Linux服务器上所有的网卡对应的mac地址。
当前操作系统信息:
[root@vm101108 src]# uname -a
Linux vm101108 3.10.0-1160.15.2.el7.x86_64 #1 SMP Wed Feb 3 15:06:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
[root@vm101108 src]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
上面的程序运行结果:
[root@vm101108 src]# ./get_mac_addr
eth: lo, mac: 00:00:00:00:00:00
eth: em1, mac: b8:2a:72:dc:42:f2
eth: p5p2, mac: 90:e2:ba:89:46:bd
eth: docker0, mac: 02:42:1c:d3:f8:e8
查看本地网卡的mac地址:
[root@vm101108 src]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:1cff:fed3:f8e8 prefixlen 64 scopeid 0x20<link>
ether 02:42:1c:d3:f8:e8 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 446 (446.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
em1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.102.108 netmask 255.255.255.0 broadcast 192.168.102.255
inet6 fe80::9f88:a3a9:1748:56fc prefixlen 64 scopeid 0x20<link>
ether b8:2a:72:dc:42:f2 txqueuelen 1000 (Ethernet)
RX packets 4420019 bytes 547543658 (522.1 MiB)
RX errors 0 dropped 52 overruns 0 frame 0
TX packets 6526650 bytes 6637157039 (6.1 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 55
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 140886 bytes 12507428 (11.9 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 140886 bytes 12507428 (11.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
p5p2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.101.108 netmask 255.255.255.0 broadcast 192.168.101.255
inet6 fe80::427c:e13c:50b6:d747 prefixlen 64 scopeid 0x20<link>
ether 90:e2:ba:89:46:bd txqueuelen 1000 (Ethernet)
RX packets 57573979 bytes 50997944188 (47.4 GiB)
RX errors 0 dropped 41 overruns 0 frame 0
TX packets 1111442 bytes 673920374 (642.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
可见,取出来的mac地址是正确的。
AIX系统
AIX系统是power架构的,没有SIOCGIFHWADDR这个接口,因此,不能像Linux那样获取mac地址。
这里提供两种方法:
第一种方法
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/ndd_var.h>
#include <sys/kinfo.h>
#include <net/if_dl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h> /* for ifconf */
#include <netinet/in.h> /* for sockaddr_in */
#include <sys/ioctl.h>
static int aix_get_mac_addr(uint8_t mac[6])
{
struct ifconf ifc;
struct ifreq *ifr;
char buf[640] = "";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
ifc.ifc_len = 640;
ifc.ifc_buf = buf;
if (sock < 0)
{
free(ifr);
return -1;
}
ioctl(sock, CSIOCGIFCONF, &ifc);
ifr = (struct ifreq *)buf;
struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
memcpy(mac, ((caddr_t)((sdl)->sdl_data + (sdl)->sdl_nlen)), 6);
close(sock);
free(ifr);
return 0;
}
void print_mac(uint8_t mac[6]){
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
int main(void)
{
unsigned char mac[6];
if (aix_get_mac_addr(mac) == -1){
perror("aix_getmac");
exit(2);
}
print_mac(mac);
}
这种方法的核心逻辑是通过ioctl(sock, SIOCGIFCONF, &ifc)
取出网卡信息后,将其地址强转成struct sockaddr_dl
类型。
当前操作系统:
-bash-4.3# uname -a
AIX localhost 1 6 00C553DC4C00
-bash-4.3# oslevel
6.1.0.0
以上代码在AIX系统下运行结果:
-bash-4.3# ./mac1
00:11:25:c5:97:cc
查看系统网卡:
-bash-4.3# netstat -in
Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll
en0 1500 link#2 0.11.25.c5.97.cc 127705533 0 1124424 3 0
en0 1500 192.168.21 192.168.21.216 127705533 0 1124424 3 0
lo0 16896 link#1 1787514 0 1709788 0 0
lo0 16896 127 127.0.0.1 1787514 0 1709788 0 0
lo0 16896 ::1%1 1787514 0 1709788 0 0
可见,取出来的mac地址是正确的。
第二种方法
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/ndd_var.h>
#include <sys/kinfo.h>
#include <net/if_dl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h> /* for ifconf */
#include <netinet/in.h> /* for sockaddr_in */
#include <sys/ioctl.h>
/*
get ethernet MAC address on AIX
*/
static int aix_get_mac_addr(uint8_t mac[6])
{
struct ifconf ifc;
struct ifreq *ifr;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
ifc.ifc_len = sizeof(struct ifreq);
ifc.ifc_buf = (char *)ifr;
if (sock < 0)
{
free(ifr);
return -1;
}
ioctl(sock, SIOCGIFCONF, &ifc);
size_t ksize;
struct kinfo_ndd *ndd;
int count, i;
ksize = getkerninfo(KINFO_NDD, 0, 0, 0);
if (ksize == 0) {
errno = ENOSYS;
return -1;
}
ndd = (struct kinfo_ndd *)malloc(ksize);
if (ndd == NULL) {
errno = ENOMEM;
return -1;
}
if (getkerninfo(KINFO_NDD, ndd, &ksize, 0) == -1) {
errno = ENOSYS;
return -1;
}
count= ksize/sizeof(struct kinfo_ndd);
for (i=0;i<count;i++) {
if ((ndd[i].ndd_type == NDD_ETHER ||
ndd[i].ndd_type == NDD_ISO88023) &&
ndd[i].ndd_addrlen == 6 &&
(strcmp(ndd[i].ndd_alias, ifr->ifr_name) == 0 ||
strcmp(ndd[i].ndd_name, ifr->ifr_name) == 0)) {
memcpy(mac, ndd[i].ndd_addr, 6);
free(ndd);
return 0;
}
}
free(ndd);
errno = ENOENT;
return -1;
}
void print_mac(uint8_t mac[6]){
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
int main(void)
{
uint8_t mac[6];
int i, ret;
ret = aix_get_mac_addr(mac);
if (ret == -1) {
perror("aix_getmac");
exit(1);
}
print_mac(mac);
}
第二种方法是通过getkerninfo
函数去获取相关硬件信息。
其运行结果和第一种方法得到的一样:
-bash-4.3# ./mac2
00:11:25:c5:97:cc
Windows系统
//TODO
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。