SIOCGIFCONF返回接口中所有配置信息,目前该命令只在AF_INET地址族中有效。如果其他协议想使用该接口获取地址列表需要在协议初始化时去注册ioctl相关函数,下文分享该接口的使用方法、注册方法。
获取IPV4地址的使用方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main (int argc, const char* argv[])
{
int socketfd;
struct ifconf conf;
char data[4096];
struct ifreq *ifr;
char addrbuf[1024];
int i;
printf("Opening socket...");
socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (socketfd >= 0) {
printf(" OK\n");
conf.ifc_len = sizeof(data);
conf.ifc_buf = (caddr_t) data;
if (ioctl(socketfd,SIOCGIFCONF,&conf) < 0) {
perror("ioctl");
}
printf("Discovering interfaces...\n");
i = 0;
ifr = (struct ifreq*)data;
while ((char*)ifr < data+conf.ifc_len) {
if(ifr->ifr_addr.sa_family == AF_INET) {
++i;
printf("%d. %s : %s\n", i, ifr->ifr_name, inet_ntop(ifr->ifr_addr.sa_family, &((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr, addrbuf, sizeof(addrbuf)));
}
ifr++;
}
close(socketfd);
}
else {
printf(" Failed!\n");
}
return 0;
}
返回的数据会写入data内,按照ifreq格式去循环读取即可,获取的是所有地址列信息,所以需要判断 if(ifr->ifr_addr.sa_family == AF_INET) 来区分获取的地址类型,代码比较简单对照内核对应执行的函数看的话会更容易理解一些,如下:
//linux内核最终调用的对应接口,在所有的接口里面循环把地址等信息返回用户态
static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size)
{
struct in_device *in_dev = __in_dev_get_rtnl(dev);
const struct in_ifaddr *ifa;
struct ifreq ifr;
int done = 0;
if (WARN_ON(size > sizeof(struct ifreq)))
goto out;
if (!in_dev)
goto out;
in_dev_for_each_ifa_rtnl(ifa, in_dev) {
if (!buf) {
done += size;
continue;
}
if (len < size)
break;
memset(&ifr, 0, sizeof(struct ifreq));
strcpy(ifr.ifr_name, ifa->ifa_label);
(*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
ifa->ifa_local;
if (copy_to_user(buf + done, &ifr, size)) {
done = -EFAULT;
break;
}
len -= size;
done += size;
}
out:
return done;
}
register_gifconf函数是用来注册对应SIOCGIFCONF这个系统调用的一个回调函数,这个函数对于AF_INET来说就是register_gifconf(PF_INET, inet_gifconf)函数。如果需要自己注册相关函数参考即可。
感兴趣的可以看下,调用注册函数的相关接口:
int dev_ifconf(struct net *net, struct ifconf *ifc, int size)
{
struct net_device *dev;
char __user *pos;
int len;
int total;
int i;
/*
* Fetch the caller's info block.
*/
pos = ifc->ifc_buf;
len = ifc->ifc_len;
/*
* Loop over the interfaces, and write an info block for each.
*/
total = 0;
for_each_netdev(net, dev) {
for (i = 0; i < NPROTO; i++) {
if (gifconf_list[i]) {
int done;
if (!pos)
done = gifconf_list[i](dev, NULL, 0, size);
else
done = gifconf_list[i](dev, pos + total,
len - total, size);
if (done < 0)
return -EFAULT;
total += done;
}
}
}
/*
* All done. Write the updated control block back to the caller.
*/
ifc->ifc_len = total;
/*
* Both BSD and Solaris return 0 here, so we do too.
*/
return 0;
}
dev_ifconf函数被dev_ioctl函数调用,通用接口配置链表gifconf_list是一个装有SIOCGIFCONF注册接口的函数指针数组,对于AF_INET来说此处通过回调函数inet_gifconf得到相应的地址信息。