C语言获取服务器mac地址
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
推荐阅读
clickhouse的BACKUP/RESTORE命令介绍
clickhouse的数据备份和恢复功能在大数据运维中是非常常用的功能,目前也有很多比较优秀的开源方案可供选择,比如clickhouse-backup, 以及clickhouse自带的clickhouse-copier。本文介绍使用clickhouse自带的BACK...
禹鼎侯阅读 176
又一款眼前一亮的Linux终端工具!
今天给大家介绍一款最近发现的功能十分强大,颜值非常高的一款终端工具。这个神器我是在其他公众号文章上看到的,但他们都没把它的强大之处介绍明白,所以我自己体验一波后,再向大家分享自己的体验。
良许赞 5阅读 1.8k
Linux终端居然也可以做文件浏览器?
大家好,我是良许。在抖音上做直播已经整整 5 个月了,我很自豪我一路坚持到了现在【笑脸】最近我在做直播的时候,也开始学习鱼皮大佬,直播写代码。当然我不懂 Java 后端,因此就写写自己擅长的 Shell 脚本。但...
良许赞 1阅读 2.1k
Laravel入门及实践,快速上手ThinkSNS+二次开发
【摘要】自从ThinkSNS+不使用ThinkPHP框架而使用Laravel框架之后,很多人都说技术门槛抬高了,其实你与TS+的距离仅仅只是学习一个新框架而已,所以,我们今天来说说Laravel的入门。
ThinkSNS赞 1阅读 2.4k
confluence7.2.1的部署与迁移---呕心沥血版
Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki。使用简单,但它强大的编辑和站点管理特征能够帮助团队成员之间共享信息、文档协作、集体讨论,信息推送。 到官网下载最新版本,截止目前...
暗涌阅读 7.8k
C++编译器和链接器的完全指南
C++是一种强类型语言,它的编译和链接是程序开发过程中不可或缺的两个环节。编译器和链接器是两个非常重要的概念。本文将详细介绍C++中的编译器和链接器以及它们的工作原理和使用方法。
小万哥赞 2阅读 1k
Ubuntu 20.04 读写 Windows 10 共享目录(qbit)
本文实验环境中,Windows 操作系统版本为 Windows Server 2016,应该对 Windows 10 和 Windows Server 2019 同样适用
qbit赞 1阅读 6.3k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。