Linux下C獲取所有可用網卡信息

在Linux下開發網絡程序時,經常會遇到需要取本地網絡接口名、IP、廣播地址、子網掩碼或者MAC地址等信息的需求,最常見的辦法是配合宏SIOCGIFHWADDR、SIOCGIFADDR、SIOCGIFBRDADDR與SIOCGIFNETMASK作爲參數調用函數ioctl分別獲得MAC地址、IP地址、廣播地址與子網掩碼來實現。一次性獲取此類信息的C語言代碼實現如下。

#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <errno.h>

int getLocalInfo(void)
{
    int fd;
    int interfaceNum = 0;
    struct ifreq buf[16];
    struct ifconf ifc;
    struct ifreq ifrcopy;
    char mac[16] = {0};
    char ip[32] = {0};
    char broadAddr[32] = {0};
    char subnetMask[32] = {0};

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket");

        close(fd);
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;
    if (!ioctl(fd, SIOCGIFCONF, (char *)&ifc))
    {
        interfaceNum = ifc.ifc_len / sizeof(struct ifreq);
        printf("interface num = %dn", interfaceNum);
        while (interfaceNum-- > 0)
        {
            printf("ndevice name: %sn", buf[interfaceNum].ifr_name);

            //ignore the interface that not up or not runing  
            ifrcopy = buf[interfaceNum];
            if (ioctl(fd, SIOCGIFFLAGS, &ifrcopy))
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);

                close(fd);
                return -1;
            }

            //get the mac of this interface  
            if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&buf[interfaceNum])))
            {
                memset(mac, 0, sizeof(mac));
                snprintf(mac, sizeof(mac), "%02x%02x%02x%02x%02x%02x",
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[0],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[1],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[2],

                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[3],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[4],
                    (unsigned char)buf[interfaceNum].ifr_hwaddr.sa_data[5]);
                printf("device mac: %sn", mac);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the IP of this interface  

            if (!ioctl(fd, SIOCGIFADDR, (char *)&buf[interfaceNum]))
            {
                snprintf(ip, sizeof(ip), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_addr))->sin_addr));
                printf("device ip: %sn", ip);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the broad address of this interface  

            if (!ioctl(fd, SIOCGIFBRDADDR, &buf[interfaceNum]))
            {
                snprintf(broadAddr, sizeof(broadAddr), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_broadaddr))->sin_addr));
                printf("device broadAddr: %sn", broadAddr);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;
            }

            //get the subnet mask of this interface  
            if (!ioctl(fd, SIOCGIFNETMASK, &buf[interfaceNum]))
            {
                snprintf(subnetMask, sizeof(subnetMask), "%s",
                    (char *)inet_ntoa(((struct sockaddr_in *)&(buf[interfaceNum].ifr_netmask))->sin_addr));
                printf("device subnetMask: %sn", subnetMask);
            }
            else
            {
                printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
                close(fd);
                return -1;

            }
        }
    }
    else
    {
        printf("ioctl: %s [%s:%d]n", strerror(errno), __FILE__, __LINE__);
        close(fd);
        return -1;
    }

    close(fd);

    return 0;
}

int main(void)
{
    getLocalInfo();

    return 0;
}

使用ioctl函數雖然可以獲取所有的信息,但是使用起來比較麻煩,如果不需要獲取MAC地址,那麼使用getifaddrs函數來獲取更加方便與簡潔。值得一提的是,在MacOS或iOS系統上(如iPhone程序開發),上述iotcl函數沒法獲得mac地址跟子網掩碼,這個使用,使用getifaddrs函數便更有優勢了。下面是使用getiaddrs函數獲取網卡信息的C語言代碼實現。

#include <stdio.h>  
#include <ifaddrs.h>  
#include <arpa/inet.h>  

int getSubnetMask()
{
    struct sockaddr_in *sin = NULL;
    struct ifaddrs *ifa = NULL, *ifList;

    if (getifaddrs(&ifList) < 0)
    {
        return -1;
    }

    for (ifa = ifList; ifa != NULL; ifa = ifa->ifa_next)
    {
        if(ifa->ifa_addr->sa_family == AF_INET)
        {
            printf("n>>> interfaceName: %sn", ifa->ifa_name);

            sin = (struct sockaddr_in *)ifa->ifa_addr;
            printf(">>> ipAddress: %sn", inet_ntoa(sin->sin_addr));

            sin = (struct sockaddr_in *)ifa->ifa_dstaddr;
            printf(">>> broadcast: %sn", inet_ntoa(sin->sin_addr));

            sin = (struct sockaddr_in *)ifa->ifa_netmask;
            printf(">>> subnetMask: %sn", inet_ntoa(sin->sin_addr));
        }
    }

    freeifaddrs(ifList);

    return 0;
}

int main(void)
{
    getSubnetMask();

    return 0;
}

ifaddrs結構體定義如下:

struct ifaddrs   
{   
    struct ifaddrs  *ifa_next;    /* Next item in list */   
    char            *ifa_name;    /* Name of interface */   
    unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */   
    struct sockaddr *ifa_addr;    /* Address of interface */   
    struct sockaddr *ifa_netmask; /* Netmask of interface */   
    union   
    {   
        struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */   
        struct sockaddr *ifu_dstaddr; /* Point-to-point destination address */   
    } ifa_ifu;   
    #define              ifa_broadaddr ifa_ifu.ifu_broadaddr   
    #define              ifa_dstaddr   ifa_ifu.ifu_dstaddr   
    void            *ifa_data;    /* Address-specific data */   
};

ifa_next指向鏈表的下一個成員;ifa_name是接口名稱,以0結尾的字符串,比如eth0,lo;ifa_flags是接口的標識位(比如當IFF_BROADCAST或IFF_POINTOPOINT設置到此標識位時,影響聯合體變量ifu_broadaddr存儲廣播地址或ifu_dstaddr記錄點對點地址);ifa_netmask存儲該接口的子網掩碼;結構體變量存儲廣播地址或點對點地址(見括弧介紹ifa_flags);ifa_data存儲了該接口協議族的特殊信息,它通常是NULL(一般不關注他)。

函數getifaddrs(int getifaddrs (struct ifaddrs **__ifap))獲取本地網絡接口信息,將之存儲於鏈表中,鏈表頭結點指針存儲於__ifap中帶回,函數執行成功返回0,失敗返回-1,且爲errno賦值。
很顯然,函數getifaddrs用於獲取本機接口信息,比如最典型的獲取本機IP地址。
發佈了19 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章