109-获取接口信息(一)

1. 接口的其他信息

上一篇文章简要的介绍了接口的名字和索引号的概念,我们也可以通过一些函数去获取、转换它们。可是,接口除了这些信息外,还有很多其它信息,比如接口上配置的 ip 地址啊,子网掩码啦,MTU 等等。

说了这么多,那要怎么才能获取到这些信息呢?有一个类似 fcntl 的函数,叫 ioctl,也是一个垃圾桶函数,通过不同命令来完成不同的功能。它的函数原型如下:

#include <unistd.h> int ioctl(int fd, int request, ... /* void *arg */);

ioctl 不仅能操作接口,也能操作套接字,文件等等。它的命令非常非常多,根本数不过来,我们只能遇到一个学一个。

2. 获取所有接口信息

命令 SIOCGIFCONF,这个命令的意思是,Socket IOCtl Get InterFace Config,意思是用来获取接口配置。接口配置的数据类型是这样的(man netdevice):

struct ifconf {     int                 ifc_len; /* 第二个成员的大小,value-result 参数 */     union {         char           *ifc_buf; /* buffer address */         struct ifreq   *ifc_req; /* array of structures */     }; };

第一个成员是一个 value-result 参数,你需要手工传递缓冲区大小给它。当 ioctl 函数返回时,也会修改这个成员,表示实际获取的数据长度。

第二个成员是一个联合体,我们比较关注的是 struct ifreq 这个类型,它样子如下:

struct ifreq {     char ifr_name[IFNAMSIZ]; /* 接口名字,大小为 16 */     union {         struct sockaddr ifr_addr; // 接口配置的 IP 地址         struct sockaddr ifr_dstaddr; // P2P 中对端地址         struct sockaddr ifr_broadaddr; // 广播地址         struct sockaddr ifr_netmask; // 子网掩码         struct sockaddr ifr_hwaddr; // MAC 地址         short           ifr_flags; // 标志位,用来指示网卡的功能         int             ifr_ifindex; // 接口索引号         int             ifr_metric; // 度量距离         int             ifr_mtu; // MTU         struct ifmap    ifr_map; // 接口硬件参数,后面的都不用管了         char            ifr_slave[IFNAMSIZ];          char            ifr_newname[IFNAMSIZ];         char           *ifr_data;     }; };

需要特别注意的是,struct ifreq 的第二个成员也是一个联合体。那么使用 ioctl 的 SIOCGIFCONF 获取的值,到底是该联合体中的哪个含义?

答:SIOCGIFCONF 获取的是接口配置的 IP 地址,即 ifr_addr.

这样一来,工作就简单多了,我们甚至可以把 struct ifreq 简化为:

struct ifreq {     char ifr_name[IFNAMSIZ]; /* 接口名字,大小为 16 */     struct sockaddr ifr_addr; // 接口配置的 IP 地址 };

说了这么多,来个例子实践一下。

3. 实例

通过 SIOCGIFCONF 命令获取所有接口配置。

  • 伪代码前半部分——获取接口配置
int sockfd; char buf[4096]; struct ifconf ifc;  ifc.ifc_len = 4096; ifc.ifc_buf = buf;  // 因为 ioctl 第一个参数是描述符,没办法,必须得传一个 sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 获取接口配置 ioctl(sockfd, SIOCGIFCONF, &ifc);  // 之前我们说过,SIOCGIFCONF 是获取所有接口的配置,也就是说 ifconf 的第二个成员保存了所有的接口配置信息,是如何存储的?我们该如何遍历呢?

上面的程序写到一半,你拿到了 ifconf 的结果,但是你只知道 ifconf 的总长度,却不知道里面保存多少个接口的配置信息。看图 1,你就很容易明白,应该如何却遍历这里面的数据了。


这里写图片描述

接着,我们尝试去遍历 ifconf 里的 ifreq 结构体,打印接口名字和配置的 IP 地址。

  • 伪代码后半部分——遍历
// 计算ifreq 的个数 int count = ifc.ifc_len / sizeof(struct ifreq); struct ifreq *ifr = ifc.ifc_buf; for (int i = 0; i < count; ++i) {   printf("name = %s, ", ifr[i].ifr_name);   printf("addr = %s\n", inet_ntoa(ifr[i].ifr_addr); }

4. 实验

代码托管在:http://git.oschina.net/ivan_allen/unp
本文程序路径:unp/program/interface/ifconf


这里写图片描述
图2 运行结果

5. 总结

  • 掌握 struct ifconf 和 struct ifreq 结构体
  • 使用 ioctl 获取所有接口的配置

思考:在第 3 小节的实例中,使用了大小固定为 4096 的接收缓冲。如果使用 ioctl 获取数据,数据量非常有可能超过 4096,这时不就导致数据被截断了吗?你能解决这个问题吗?

注意:在 Linux 中,ioctl 数据被截断并不会返回错误,它什么都不会告诉你。
提示:在unp/program/util/common.cc 中,我将 ioctl 函数封装到了 getIfConf 中,你可以查看该函数的实现。

最后,到这里我们只学会了获取接口的名字和接口上配置的 IP,接口的子网掩码,MAC 地址等等这些我们都还不知道怎么获取,实际上也很简单,使用 ioctl 的其它命令就行,具体你可以使用 man netdevice 查看到,里头有非常非常多的命令让你去操作接口。不过在下一篇中,会介绍这些常用的接口操作命令。

说明:本文转自--Allen--,用于学习交流分享,仅代表原文作者观点。如有侵权,请联系我们删除~