使用ifconf 获取详细信息

通过ioctl 发出命令,获取所有接口的详细信息

设计思路

  1. 使用ioctl 发出SIOCGIFCONF 命令获取所有的接口信息
  2. 循环每一个接口,通过ioctl发出不同的命令获取不同的详细信息
  3. 返回一个链表

flags

所有的flags 在net/if.h头文件中, 常见值如下:

#define IFF_UP          0x1             /* interface is up */
#define IFF_BROADCAST   0x2             /* broadcast address valid */
#define IFF_DEBUG       0x4             /* turn on debugging */
#define IFF_LOOPBACK    0x8             /* is a loopback net */
#define IFF_POINTOPOINT 0x10            /* interface is point-to-point link */
#define IFF_NOTRAILERS  0x20            /* obsolete: avoid use of trailers */
#define IFF_RUNNING     0x40            /* 资源被分配,该标志在linux中没有用,只是为了兼容BSD */
#define IFF_NOARP       0x80            /* 不支持arp协议 */
#define IFF_PROMISC     0x100           /* receive all packets */
#define IFF_ALLMULTI    0x200           /* receive all multicast packets */
#define IFF_OACTIVE     0x400           /* transmission in progress */
#define IFF_SIMPLEX     0x800           /* can't hear own transmissions */
#define IFF_LINK0       0x1000          /* per link layer defined bit */
#define IFF_LINK1       0x2000          /* per link layer defined bit */
#define IFF_LINK2       0x4000          /* per link layer defined bit */
#define IFF_ALTPHYS     IFF_LINK2       /* use alternate physical connection */
#define IFF_MULTICAST   0x8000          /* supports multicast */

uif.h

#ifndef UIF_H
#define UIF_H

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

#define IFI_NAME_LEN  16  /* 定义接口的名字长度 */
#define IFI_HADDR_LEN 8   /*mac地址长度,为了后续兼容给长度8字节*/

struct ifi_info {
    uint8_t ifi_name[IFI_NAME_LEN];  // 名字
    uint16_t ifi_index;   // 索引
    uint16_t ifi_mtu;     //  mtu
    uint8_t  ifi_haddr[IFI_HADDR_LEN];  // mac地址
    uint16_t ifi_hlen;  // 用于识别是否有mac地址
    uint16_t ifi_flags;  // 接口flags
    uint16_t ifi_myflags;   
    struct sockaddr *ifi_addr;  // ip 地址
    struct sockaddr *ifi_brdaddr;  // 广播地址
    struct sockaddr *ifi_dstaddr;  // 点对点地址
    struct ifi_info *ifi_next;   // 下一个
};

struct ifi_info * get_ifi_info();
void free_ifi_info(struct ifi_info *);

#endif

get_ifi_info 函数: 获取所有接口信息,返回链表头

struct ifi_info * get_ifi_info() {
    struct ifi_info *ifi, *ifihead, **ifipnext;
    int sockfd, len, lastlen, flags;
    char *buf, *ptr, lastname[IFNAMSIZ];
    struct ifconf ifc;
    struct ifreq *ifr, ifrcopy;
    struct sockaddr_in *sinptr;

    if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) {
        perror("socket");
        exit(-1);
    }
    lastlen = 0;
    len = 100 * sizeof(struct ifreq);
    for (; ;) {
        buf = (char *)malloc(len);
        ifc.ifc_len = len;
        ifc.ifc_buf = buf;
        if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
            if (errno != EINVAL || lastlen != 0) {
                perror("ioctl");
                exit(-1);
            }
        } else {
            if (ifc.ifc_len == lastlen) {
                break;
            }
            lastlen = ifc.ifc_len;
        }
        len += 10 * sizeof(struct ifreq);
        free(buf);
    }
    ifihead = NULL;
    ifipnext = &ifihead;
    lastname[0] = 0;


    for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
        ifr = (struct ifreq *) ptr;

        len = sizeof(struct ifreq);

        ptr += len;  // 下一个开始的位置

        if ( ifr->ifr_addr.sa_family != AF_INET) {
            continue;
        }

        if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
            continue;
        }

        memcpy(lastname, ifr->ifr_name, IFNAMSIZ);

        ifrcopy = *ifr;

        // 请求得到flags
        ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);

        flags = ifrcopy.ifr_flags;  

        if ((flags & IFF_UP) == 0) continue;

        ifi = (struct ifi_info *)malloc(sizeof(struct ifi_info));
        *ifipnext = ifi;
        ifipnext = &ifi -> ifi_next;

        ifi->ifi_flags = flags;
        ifi->ifi_myflags = 0;
#if defined(SIOCGIFMTU)
        ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
        ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
        ifi->ifi_mtu = 0;
#endif
        memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME_LEN);
        ifi->ifi_name[IFI_NAME_LEN - 1] = '\0';

#if defined(SIOCGIFINDEX)
        ioctl(sockfd, SIOCGIFINDEX, &ifrcopy);
        ifi->ifi_index = ifrcopy.ifr_ifindex;
#else
        ifi->ifi_index = 0;
#endif

#ifdef SIOCGIFTXQLEN
        ioctl(sockfd, SIOCGIFTXQLEN, &ifrcopy);
        ifi->ifi_qlen = (uint16_t)ifrcopy.ifr_qlen;
#else
        ifi->ifi_qlen = 0;
#endif

#ifdef  SIOCGIFMETRIC
        ioctl(sockfd, SIOCGIFMETRIC, &ifrcopy);
        ifi->ifi_metric = (uint16_t)ifrcopy.ifr_metric;
#else
        ifi->ifi_metric = 0;
#endif
        // 获取mac地址
#if defined(SIOCGIFHWADDR) 
        if ( ioctl(sockfd, SIOCGIFHWADDR, &ifrcopy) == 0 ) {
            memcpy(ifi->ifi_haddr, ifrcopy.ifr_hwaddr.sa_data, 6);
            ifi->ifi_hlen = 6;
        }
#else
        ifi->ifi_hlen = 0;
#endif


        sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
        ifi->ifi_addr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
        memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));


#ifdef SIOCGIFNETMASK
        ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy);
        sinptr = (struct sockaddr_in *) &ifrcopy.ifr_netmask;
        ifi->ifi_netmask = (struct sockaddr *)malloc(sizeof(struct sockaddr));
        memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in));
#endif


        if(flags & IFF_BROADCAST) {
            ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
            sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
            ifi->ifi_brdaddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
            memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
        }

        if(flags & IFF_POINTOPOINT) {
            ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
            sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
            ifi->ifi_dstaddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
            memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
        }
    }
    free(buf);
    close(sockfd);
    return ifihead;
}

free_ifi_info 函数: 释放内存

void free_ifi_info(struct ifi_info *ifihead) {
    struct ifi_info *ifi, *ifinext;
    for (ifi = ifihead; ifi != NULL; ifi=ifinext) {
        if (ifi->ifi_addr != NULL) {
            free(ifi->ifi_addr);
        }
        if (ifi->ifi_brdaddr != NULL) {
            free(ifi->ifi_brdaddr);
        }
        if (ifi->ifi_dstaddr != NULL) {
            free(ifi->ifi_dstaddr);
        }
        ifinext = ifi->ifi_next;
        free(ifi);
    }
}

主函数测试

int main() {
    struct ifi_info *ifi, *ifihead;
    struct sockaddr_in *sa;
    int i;
    char addrstr[INET_ADDRSTRLEN];
    ifihead = ifi = get_ifi_info();

    while(ifi) {
        printf("%s: ", ifi->ifi_name);
        printf("(%d) ", ifi->ifi_index);

        printf("<");
        if (ifi->ifi_flags & IFF_UP) printf("UP ");
        if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
        if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
        if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
        if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
        printf(">\n");

        if(ifi->ifi_hlen) {
            printf(" MAC: ");
            for(i=0; i < 6; i++){
                if(i==5){
                    printf("%02x\n", ifi->ifi_haddr[i]);
                } else {
                    printf("%02x:", ifi->ifi_haddr[i]);
                }
            }
        }

        if(ifi->ifi_metric != 0) {
            printf(" METRIC: %u\n", ifi->ifi_metric);
        }

        if(ifi->ifi_qlen != 0) {
            printf(" qlen: %u\n", ifi->ifi_qlen);
        }

        if(ifi->ifi_mtu != 0) {
            printf(" MTU: %u\n", ifi->ifi_mtu);
        }
        if(ifi->ifi_addr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_addr;
            printf(" addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_brdaddr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_brdaddr;
            printf(" broadcast addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_dstaddr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_dstaddr;
            printf(" destination addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_netmask != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_netmask;
            printf(" netmask: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        ifi = ifi->ifi_next;
    }
    free_ifi_info(ifihead);
    return 0;
}

运行结果

[root@iz2zecj7a5r32f2axsctb9z ifconfig]# ./uif
lo: (1) <UP LOOP >
 MAC: 00:00:00:00:00:00
 qlen: 1000
 addr: 127.0.0.1
 netmask: 255.0.0.0
eth0: (2) <UP BCAST MCAST >
 MAC: 00:16:3e:0a:5c:f5
 qlen: 1000
 MTU: 1500
 addr: 172.17.147.135
 broadcast addr: 172.17.159.255
 netmask: 255.255.240.0
docker0: (3) <UP BCAST MCAST >
 MAC: 02:42:42:33:a1:76
 MTU: 1500
 addr: 172.18.0.1
 broadcast addr: 172.18.255.255
 netmask: 255.255.0.0
文档更新时间: 2021-02-04 13:18   作者:周国强