分类: LINUX
2013-12-19 09:25:32
原文地址:http://vindo.blog.51cto.com/2990328/561865
使用ioctl的SIOCGIFCONF可以读取所有网卡信息。ioctl调用后返回指向ifconf的结构链表,其中包含了指向ifreq的结构指针。ifconf及ifreq定义在net/if.h中。
《UNIX网络编程》中提供了get_ifi_info函数的实现方法,使用这种方式来获取网络信息。在LINUX下,这种方式不能获得IPV6的网卡信息。《UNIX网络编程》中有如下描述:
在支持IPV6的系统中,没有关于对SIOCGIFCONF请求是否返回IPV6地址的标准。我们给支持IPV6的新系统增加了一个case语句, 这是为了预防万一。问题在于ifreq中的联合把返回的地址定义成一个通用的16字节套接口地址结构,适合16字节的IPV4 socket_in结构,但对于24字节的IPV6 socket_in6结构太小了。如果返回IPV6地址,将可能破环现有的在每个ifreq结构中采用固定大小的套接口地址结构的代码。
经测试,在fedor6-2.6.18kernel中无法返回ipv6地址,事实上,返回的地址簇总是AF_INET,而并非AF_INET6。
这种方法的实现代码如下:
net_if.h
#ifndef __NET_INF_H
#define __NET_INF_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IFI_NAME 16
#define IFI_HADDR 8
typedef struct ifi_info
{
char ifi_name[IFI_NAME];
u_char ifi_haddr[IFI_HADDR];
u_short ifi_hlen;
short ifi_flags;
short ifi_myflags;
struct sockaddr *ifi_addr;
struct sockaddr *ifi_brdaddr;
struct sockaddr *ifi_dstaddr;
struct ifi_info *ifi_next;
}ifi_info;
#define IFI_ALIAS 1
struct ifi_info *get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *);
#endif
net_if.c
#include "net_if.h"
ifi_info *get_ifi_info(int family, int doaliases)
{
ifi_info *ifi, *ifihead, **ifipnext;
int sockfd, len, lastlen, flags, myflags;
char *ptr, *buf, lastname[IFNAMSIZ], *cptr;
struct ifconf ifc;
struct ifreq *ifr, ifrcopy;
struct sockaddr_in *sinptr;
if ((sockfd=socket(family, SOCK_DGRAM, 0))<0)
{
printf("socket error.\n");
exit(1);
}
lastlen = 0;
len = 10*sizeof(struct ifreq);
while (1)
{
buf = (char*)malloc(len);
ifc.ifc_len = len;
ifc.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &ifc)<0)
{
if (errno!=EINVAL||lastlen!=0)
{
printf("ioctl error.\n");
}
}
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; ptrifr->ifr_addr.sa_len?sizeof(struct sockaddr):ifr->ifr_addr.sa_len;
#else
switch (ifr->ifr_addr.sa_family)
{
#ifdef IPV6
case AF_INET6:
len = sizeof(struct sockaddr_in6);
break;
#endif
case AF_INET:
default:
len = sizeof(struct sockaddr);
break;
}
#endif
ptr += sizeof(ifr->ifr_name) + len;
if (ifr->ifr_addr.sa_family != family)
continue;
myflags = 0;
if ((cptr=strchr(ifr->ifr_name, ':'))!=NULL)
*cptr = 0;
if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ)==0)
{
if (doaliases == 0)
continue;
myflags = IFI_ALIAS;
}
memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
ifrcopy = *ifr;
ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
flags = ifrcopy.ifr_flags;
if ((flags&IFF_UP)==0)
continue;
/*
if ((flags&IFF_BROADCAST)==0)
continue;
*/
ifi = calloc(1, sizeof(struct ifi_info));
*ifipnext = ifi;
ifipnext = &ifi->ifi_next;
ifi->ifi_flags = flags;
ifi->ifi_myflags = myflags;
memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
ifi->ifi_name[IFI_NAME-1] = '\0';
switch (ifr->ifr_addr.sa_family)
{
case AF_INET:
sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
if (ifi->ifi_addr == NULL)
{
ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
if (flags & IFF_BROADCAST)
{
ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
sinptr = (struct sockaddr_in *)&ifrcopy.ifr_broadaddr;
ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
#ifdef SIOCGIFDSTADDR
if (flags & IFF_POINTOPOINT)
{
ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
sinptr = (struct sockaddr_in*)&ifrcopy.ifr_dstaddr;
ifi->ifi_dstaddr = calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
}
break;
default:
break;
}
}
free(buf);
return(ifihead);
}
void free_ifi_info(ifi_info *ifihead)
{
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);
}
}
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char portstr[7];
static char str[128];
switch (sa->sa_family)
{
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))==NULL)
return NULL;
if (ntohs(sin->sin_port)!=0)
{
snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin_port));
strcat(str, portstr);
}
return str;
}
break;
case AF_INET6:
{
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
if (inet_ntop(AF_INET6, &sin->sin6_addr, str, sizeof(str))==NULL)
return NULL;
if (ntohs(sin->sin6_port)!=0)
{
snprintf(portstr, sizeof(portstr), ".%d", ntohs(sin->sin6_port));
strcat(str, portstr);
}
return str;
}
break;
default:
return NULL;
break;
}
}
int main(int argc, char *argv[])
{
ifi_info *ifi, *ifihead;
struct sockaddr *sa;
u_char *ptr;
int i, family, doaliases;
if (argc!=3)
{
printf("usage: ./prifinfo ");
exit(1);
}
if (strcmp(argv[1], "inet4") == 0)
family = AF_INET;
#ifdef IPV6
else if (strcmp(argv[1], "inet6") == 0)
family =AF_INET6;
#endif
else
{
printf("invalid
");
exit(1);
}
doaliases = atoi(argv[2]);
for(ifihead = ifi = get_ifi_info(family, doaliases);
ifi!=NULL;ifi=ifi->ifi_next)
{
printf("%s:<", ifi->ifi_name);
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 ((i=ifi->ifi_hlen)>0)
{
ptr = ifi->ifi_haddr;
do
{
printf("%s%x", (i==ifi->ifi_hlen)?" ":":", *ptr++);
}while(--i>0);
printf("\n");
}
if ((sa=ifi->ifi_addr)!=NULL)
printf(" IP addr: %s\n",
sock_ntop(sa, sizeof(*sa)));
if ((sa=ifi->ifi_brdaddr)!=NULL)
printf(" broadcast addr: %s\n",
sock_ntop(sa, sizeof(*sa)));
if ((sa=ifi->ifi_dstaddr)!=NULL)
printf(" destnation addr: %s\n",
sock_ntop(sa, sizeof(*sa)));
}
free_ifi_info(ifihead);
exit(0);
}
使用gcc net_if.c -o net_if -DIPV6编译,在IPV4模式下运行输出为:
[root@localhost net_if]./net_if inet4 1
lo:
IP addr: 127.0.0.1
eth1:
IP addr: 192.168.1.2
broadcast addr: 192.168.1.255
eth0:
IP addr: 192.168.125.99
broadcast addr: 192.168.125.255
执行./net_if inet6 1在输出为空。
第二种方式是使用getifaddrs函数获取,需要包含ifaddrs.h头文件,这种方式可以获得IPV6地址,改写的《UNIX网络编程》中的get_ifi_info函数如下所示:
znet.h
#ifndef __ZNET_H__
#define __ZNET_H__
#include
#include
#include
#include
#include
#include
#include
#include
#define IFI_NAME 16 /* same as IFNAMSIZ in */
#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */
struct ifi_info {
char ifi_name[IFI_NAME]; /* interface name, null-terminated */
short ifi_index; /* interface index */
short ifi_flags; /* IFF_xxx constants from */
struct sockaddr *ifi_addr; /* primary address */
struct sockaddr *ifi_brdaddr;/* broadcast address */
struct ifi_info *ifi_next; /* next of these structures */
};
struct ifi_info* get_ifi_info(int, int);
void free_ifi_info(struct ifi_info *);
#endif
znet.c
#include "znet.h"
struct ifi_info* get_ifi_info(int family, int doaliases) {
struct ifi_info *ifi, *ifihead, **ifipnext,*p;
struct sockaddr_in *sinptr;
struct sockaddr_in6 *sin6ptr;
struct ifaddrs *ifas;
// char addr[128];
int sockfd;
ifihead = NULL;
ifipnext = &ifihead;
if(getifaddrs(&ifas)!=0)
return ;
for(;ifas!=NULL;ifas=(*ifas).ifa_next) {
if (((*ifas).ifa_addr)->sa_family != family)
continue; // ignore if not desired address family
/*
printf("%s %d\n",(*ifas).ifa_name,((*ifas).ifa_addr)->sa_family);
if(((*ifas).ifa_addr)->sa_family!=AF_INET6)
inet_ntop(AF_INET,&(((struct sockaddr_in *)((*ifas).ifa_addr))->sin_addr),addr,sizeof(addr));
else
inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)((*ifas).ifa_addr))->sin6_addr),addr,sizeof(addr));
printf("%s\t",addr);
printf("\n");
*/
ifi = (struct ifi_info*)calloc(1,sizeof(struct ifi_info));
*ifipnext = ifi;
ifipnext = &ifi->ifi_next;
ifi->ifi_flags = (*ifas).ifa_flags;
memcpy(ifi->ifi_name, (*ifas).ifa_name, IFI_NAME);
ifi->ifi_name[IFI_NAME-1] = '\0';
switch (((*ifas).ifa_addr)->sa_family) {
case AF_INET:
sinptr = (struct sockaddr_in *) (*ifas).ifa_addr;
ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
#ifdef SIOCGIFBRDADDR
if (ifi->ifi_flags & IFF_BROADCAST) {
sinptr = (struct sockaddr_in *) (*ifas).ifa_broadaddr;
ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
}
#endif
break;
case AF_INET6:
sin6ptr = (struct sockaddr_in6 *) (*ifas).ifa_addr;
ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6));
memcpy(ifi->ifi_addr, sin6ptr, sizeof(struct sockaddr_in6));
break;
default:
break;
}
}
freeifaddrs(ifas);
return(ifihead);
}
int main(int argc, char *argv[]) {
int family;
if (argc!=2) {
printf("usage: ./znet \n");
exit(1);
}
if (strcmp(argv[1], "inet4") == 0)
family = AF_INET;
else if (strcmp(argv[1], "inet6") == 0)
family =AF_INET6;
else {
printf("invalid
\n");
exit(1);
}
char addr[128];
struct ifi_info *ifi, *ifihead;
printf("name\tflag\tIP\t\tbroadcastaddr\n");
for (ifihead = ifi = get_ifi_info(family,1); ifi != NULL; ifi = ifi->ifi_next) {
printf("%s\t",ifi->ifi_name);
printf("%d\t",ifi->ifi_flags);
if((ifi->ifi_addr)->sa_family!=AF_INET6)
inet_ntop(AF_INET,&(((struct sockaddr_in *)(ifi->ifi_addr))->sin_addr),addr,sizeof(addr));
else
inet_ntop(AF_INET6,&(((struct sockaddr_in6 *)(ifi->ifi_addr))->sin6_addr),addr,sizeof(addr));
printf("%s\t",addr);
#ifdef SIOCGIFBRDADDR
if ((ifi->ifi_flags & IFF_BROADCAST) && (ifi->ifi_addr)->sa_family!=AF_INET6) {
inet_ntop(AF_INET,&(((struct sockaddr_in *) (ifi->ifi_brdaddr))->sin_addr),addr,sizeof(addr));
printf("%s\t",addr);
}
#endif
printf("\n+++++++++++++++++++++++++++++++++++++++++++\n");
}
return 0;
}
这段代码输出如下:
[root@localhost net_if]./znet inet4
name flag IP broadcastaddr
lo 73 127.0.0.1
++++++++++++++++++++++++++++++
eth1 4099 192.168.1.2 192.168.1.255
++++++++++++++++++++++++++++++
eth0 4163 192.168.125.99 192.168.125.255
++++++++++++++++++++++++++++++
[root@localhost net_if]./znet inet6
name flag IP broadcastaddr
lo 73 ::1
++++++++++++++++++++++++++++++
eth1 4163 2001:250:1800:1::1
++++++++++++++++++++++++++++++
eth0 4163 2001:250:1888:1::1
++++++++++++++++++++++++++++++