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