在Linux系統,能夠經過系統調用函數ioctl很容易就獲取到服務器的mac地址。docker
#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; }
核心邏輯主要分兩個部分,第一個部分是獲取網卡,主要經過下面的函數完成:centos
ioctl(sock, SIOCGIFCONF, (char *)&ifc);
它的信息保存在結構體struct ifconf
中,有可能不止一個。獲取到的信息保存在ifc_buf
中。
第二個邏輯就是根據網卡的名字去獲取mac地址,主要用下面的函數完成:bash
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地址:socket
[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系統是power架構的,沒有SIOCGIFHWADDR這個接口,所以,不能像Linux那樣獲取mac地址。
這裏提供兩種方法:oop
#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
類型。
當前操做系統:ui
-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
//TODO