C语言获取服务器MAC地址

简介: 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

推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,

fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,

TCP/IP,协程,DPDK等技术内容,点击立即学习: C/C++Linux服务器开发/高级架构师

目录
相关文章
|
2月前
|
SQL 缓存 自然语言处理
实战案例1:基于C语言的Web服务器实现。
实战案例1:基于C语言的Web服务器实现。
117 15
|
2月前
|
C语言
【C语言】多进程服务器
【C语言】多进程服务器
18 4
|
2月前
|
网络安全
mac下通过ssh脚本实现免账号密码连接运服务器
mac下通过ssh脚本实现免账号密码连接运服务器
36 3
|
2月前
|
监控 网络安全 数据安全/隐私保护
Mac服务器ssh连接工具
Mac服务器ssh连接工具
59 2
|
28天前
|
网络协议 数据处理 C语言
利用C语言基于poll实现TCP回声服务器的多路复用模型
此代码仅为示例,展示了如何基于 `poll`实现多路复用的TCP回声服务器的基本框架。在实际应用中,你可能需要对其进行扩展或修改,以满足具体的需求。
48 0
|
2月前
|
移动开发 网络协议 编译器
实战案例3:C语言实现的HTTP服务器
实战案例3:C语言实现的HTTP服务器
51 0
|
2月前
|
C语言
【C语言】多线程服务器
【C语言】多线程服务器
19 0
|
25天前
|
Cloud Native Java 编译器
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
随着云计算技术的不断发展,云服务商们不断推出高性能、高可用的云服务器实例,以满足企业日益增长的计算需求。阿里云推出的倚天实例,凭借其基于ARM架构的倚天710处理器,提供了卓越的计算能力和能效比,特别适用于云原生、高性能计算等场景。然而,有的用户需要将传统基于x86平台的应用迁移到倚天实例上,本文将介绍如何将基于x86架构平台的应用迁移到阿里云倚天实例的服务器上,帮助开发者和企业用户顺利完成迁移工作,享受更高效、更经济的云服务。
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
|
23天前
|
编解码 前端开发 安全
通过阿里云的活动购买云服务器时如何选择实例、带宽、云盘
在我们选购阿里云服务器的过程中,不管是新用户还是老用户通常都是通过阿里云的活动去买了,一是价格更加实惠,二是活动中的云服务器配置比较丰富,足可以满足大部分用户的需求,但是面对琳琅满目的云服务器实例、带宽和云盘选项,如何选择更适合自己,成为许多用户比较关注的问题。本文将介绍如何在阿里云的活动中选择合适的云服务器实例、带宽和云盘,以供参考和选择。
通过阿里云的活动购买云服务器时如何选择实例、带宽、云盘
|
21天前
|
弹性计算 运维 安全
阿里云轻量应用服务器和经济型e实例区别及选择参考
目前在阿里云的活动中,轻量应用服务器2核2G3M带宽价格为82元1年,2核2G3M带宽的经济型e实例云服务器价格99元1年,对于云服务器配置和性能要求不是很高的阿里云用户来说,这两款服务器配置和价格都差不多,阿里云轻量应用服务器和ECS云服务器让用户二选一,很多用户不清楚如何选择,本文来说说轻量应用服务器和经济型e实例的区别及选择参考。
阿里云轻量应用服务器和经济型e实例区别及选择参考
下一篇
无影云桌面