两个结构体 ifconf 和 ifreq

用 ioctl 获得本地 IP 地址、MAC 地址或者子网掩码时要用到两个结构体 ifconf 和 ifreq,它们对于大多数人来说都是比较陌生的,这里给大家一种比较简单的理解方法,当然只一种帮助
理解的方法,在描述中可能会有一些地方与真实定义有所出入,仅供参考.

首先先认识一下 ifconf 和 ifreq :

  1. ifconf 通常是用来保存所有接口信息。

  2. ifreq 用来保存某个接口的信息。

//if.h
struct ifconf 
{
    int    ifc_len;            /* size of buffer    */
    union 
    {
        char *ifcu_buf;                        /* input from user->kernel*/
        struct ifreq *ifcu_req;        /* return from kernel->user*/
    } ifc_ifcu;
};
#define    ifc_buf    ifc_ifcu.ifcu_buf        /* buffer address    */
#define    ifc_req    ifc_ifcu.ifcu_req        /* array of structures    */
 

//if.h
struct ifreq {
    char ifr_name[IFNAMSIZ];
    union {
        struct sockaddr ifru_addr;
        struct sockaddr ifru_dstaddr;
        struct sockaddr ifru_broadaddr;
        short ifru_flags;
        int ifru_metric;
        caddr_t ifru_data;
    } ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr
#define ifr_dstaddr ifr_ifru.ifru_dstaddr
#define ifr_broadaddr ifr_ifru.ifru_broadaddr

上边这两个结构看起来比较复杂,我们现在把它们简单化一些:

比如说现在我们向实现获得本地 IP 的功能。

我们的做法是:

1. 先通过 ioctl 获得本地所有接口的信息,并保存在 ifconf 中。

2. 再从 ifconf 中取出每一个 ifreq 中表示 ip 地址的信息。

具体使用时我们可以认为 ifconf 就有两个成员:

ifc_len 和 ifc_buf,如下图所示: 

ifc_len:表示用来存放所有接口信息的缓冲区长度。

ifc_buf:表示存放接口信息的缓冲区。

所以我们需要在程序开始时对 ifconf 的 ifc_len 和 ifc_buf 进行初始化,接下来使用 ioctl 获取所有接口信息,完成后 ifc_len 内存放实际获得的接口信息总长度并且信息被存放在 ifc_buf 中。
接下来我们只需要从一个一个的接口信息获取 ip 地址信息即可。
下面有一个简单的参考: 

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
  int i = 0;
  int sockfd;
  struct ifconf ifconf;
  unsigned char buf[512];
  struct ifreq *ifreq;

  //初始化ifconf
  ifconf.ifc_len = 512;
  ifconf.ifc_buf = buf;

  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
  }
  ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接口信息

  //接下来一个一个的获取IP地址
  ifreq = (struct ifreq *)buf;
  for (i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; i--) {
    printf("name = [%s]\n", ifreq->ifr_name);
    printf("local addr = [%s]\n",
           inet_ntoa(((struct sockaddr_in *)&(ifreq->ifr_addr))->sin_addr));
    ifreq++;
  }
  return 0;
}

结果

name = [lo]
local addr = [127.0.0.1]
name = [enp1s0]
local addr = [192.168.137.128]
name = [vmnet1]
local addr = [192.168.75.1]
name = [vmnet8]
local addr = [192.168.205.1]


简单的 ifconfig 功能实现

#include <net/if.h> /* for ifconf */
#include <linux/sockios.h> /* for net status mask */
#include <netinet/in.h> /* for sockaddr_in */
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/if.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define MAX_INTERFACE (16)

void port_status(unsigned int flags);

/* set == 0: do clean , set == 1: do set! */
int set_if_flags(char *pif_name, int sock, int status, int set)
{
    struct ifreq ifr;
    int ret = 0;

    strncpy(ifr.ifr_name, pif_name, strlen(pif_name) + 1);
    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
    if (ret)
        return -1;
    /* set or clean */
    if (set)
        ifr.ifr_flags |= status;
    else
        ifr.ifr_flags &= ~status;
    /* set flags */
    ret = ioctl(sock, SIOCSIFFLAGS, &ifr);
    if (ret)
        return -1;

    return 0;
}

int get_if_info(int fd)
{
    struct ifreq buf[MAX_INTERFACE];
    struct ifconf ifc;
    int ret = 0;
    int if_num = 0;

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;

    ret = ioctl(fd, SIOCGIFCONF, (char *)&ifc);
    if (ret) {
        printf("get if config info failed");
        return -1;
    }
    /* 网口总数 ifc.ifc_len 应该是一个出入参数 */
    if_num = ifc.ifc_len / sizeof(struct ifreq);
    printf("interface num is interface = %d\n", if_num);
    while (if_num-- > 0) {
        printf("net device: %s\n", buf[if_num].ifr_name);
        /* 获取第n个网口信息 */
        ret = ioctl(fd, SIOCGIFFLAGS, (char *)&buf[if_num]);
        if (ret)
            continue;

        /* 获取网口状态 */
        port_status(buf[if_num].ifr_flags);

        /* 获取当前网卡的ip地址 */
        ret = ioctl(fd, SIOCGIFADDR, (char *)&buf[if_num]);
        if (ret)
            continue;
        printf("IP address is: \n%s\n", inet_ntoa(((struct sockaddr_in *)(&buf[if_num].ifr_addr))->sin_addr));

        /* 获取当前网卡的mac */
        ret = ioctl(fd, SIOCGIFHWADDR, (char *)&buf[if_num]);
        if (ret)
            continue;

        printf("%02x:%02x:%02x:%02x:%02x:%02x\n\n",
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[0],
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[1],
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[2],
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[3],
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[4],
            (unsigned char)buf[if_num].ifr_hwaddr.sa_data[5]);
    }
}

void port_status(unsigned int flags)
{
    if (flags & IFF_UP) {
        printf("is up\n");
    }
    if (flags & IFF_BROADCAST) {
        printf("is broadcast\n");
    }
    if (flags & IFF_LOOPBACK) {
        printf("is loop back\n");
    }
    if (flags & IFF_POINTOPOINT) {
        printf("is point to point\n");
    }
    if (flags & IFF_RUNNING) {
        printf("is running\n");
    }
    if (flags & IFF_PROMISC) {
        printf("is promisc\n");
    }
}

int main()
{
    int fd;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd > 0) {
        get_if_info(fd);
        close(fd);
    }

    return 0;
}

转载:两个结构体ifconf和ifreq_CYKsky的博客-CSDN博客_ifconf结构体

(SAW:Game Over)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值