使用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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #ifndef __NET_INF_H #define __NET_INF_H #include <net/if.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <netinet/in.h> #include <sys/ioctl.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | #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; ptr<buf+ifc.ifc_len;) { ifr = (struct ifreq*)ptr; #ifdef HAVE_SOCKADDR_SA_LEN len = sizeof(struct sockaddr)>ifr->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 <inet4 | inet 6> <doaliases>"); 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 <address-family>"); 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #ifndef __ZNET_H__ #define __ZNET_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <net/if.h> #include <sys/ioctl.h> #include <ifaddrs.h> #define IFI_NAME 16 /* same as IFNAMSIZ in <net/if.h> */ #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 <net/if.h> */ 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | #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 <inet4 | inet 6>\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 <address-family>\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
++++++++++++++++++++++++++++++


COMMENTS