C語言獲取服務器mac地址

Linux系統

在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系統

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

Windows系統

//TODO

相關文章
相關標籤/搜索