上一节我们展示了如何获取适配器的基本信息 (如设备的名称和描述)。
实际上,获取适配器基本信息远远不止这些,让我们先看下pcap_if结构体的内容
pcap_if * next if not NULL, a pointer to the next element in the list; NULL for the last element of the list char * name a pointer to a string giving a name for the device to pass to pcap_open_live() char * description if not NULL, a pointer to a string giving a human-readable description of the device pcap_addr * addresses a pointer to the first element of a list of addresses for the interface u_int flags PCAP_IF_ interface flags. Currently the only possible flag is PCAP_IF_LOOPBACK, that is set if the interface is a loopback interface.大家可以发现它有一个pcap_addr 的结构体,而这个结构体的组成如下:
pcap_addr * next if not NULL, a pointer to the next element in the list; NULL for the last element of the list sockaddr * addr a pointer to a struct sockaddr containing an address sockaddr * netmask if not NULL, a pointer to a struct sockaddr that contains the netmask corresponding to the address pointed to by addr. sockaddr * broadaddr if not NULL, a pointer to a struct sockaddr that contains the broadcast address corre sponding to the address pointed to by addr; may be null if the interface doesn't support broadcasts sockaddr * dstaddr if not NULL, a pointer to a struct sockaddr that contains the destination address corre sponding to the address pointed to by addr; may be null if the interface isn't a point- to-point interface
下面的范例使用了ifprint()函数来打印出 pcap_if 结构体中所有的内容。程序对每一个由pcap_findalldevs_ex() 函数返回的pcap_if,都调用ifprint()函数来实现打印。代码如下:
#include "pcap.h" #ifndef WIN32 #include <sys/socket.h> #include <netinet/in.h> #else #include <winsock.h> #endif // 函数原型 void ifprint(pcap_if_t *d); char *iptos(u_long in); char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen); int main() { pcap_if_t *alldevs; pcap_if_t *d; char errbuf[PCAP_ERRBUF_SIZE+1]; char source[PCAP_ERRBUF_SIZE+1]; printf("Enter the device you want to list:\n" "rpcap:// ==> lists interfaces in the local machine\n" "rpcap://hostname:port ==> lists interfaces in a remote machine\n" " (rpcapd daemon must be up and running\n" " and it must accept 'null' authentication)\n" "file://foldername ==> lists all pcap files in the give folder\n\n" "Enter your choice: "); fgets(source, PCAP_ERRBUF_SIZE, stdin); source[PCAP_ERRBUF_SIZE] = '\0'; /* 获得接口列表 */ if (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf); exit(1); } /* 扫描列表并打印每一项 */ for(d=alldevs;d;d=d->next) { ifprint(d); } pcap_freealldevs(alldevs); return 1; } /* 打印所有可用信息 */ void ifprint(pcap_if_t *d) { pcap_addr_t *a; char ip6str[128]; /* 设备名(Name) */ printf("%s\n",d->name); /* 设备描述(Description) */ if (d->description) printf("\tDescription: %s\n",d->description); /* Loopback Address*/ printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no"); /* IP addresses */ for(a=d->addresses;a;a=a->next) { printf("\tAddress Family: #%d\n",a->addr->sa_family); switch(a->addr->sa_family) { case AF_INET: printf("\tAddress Family Name: AF_INET\n"); if (a->addr) printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr)); if (a->netmask) printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr)); if (a->broadaddr) printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr)); if (a->dstaddr) printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr)); break; case AF_INET6: printf("\tAddress Family Name: AF_INET6\n"); if (a->addr) printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str))); break; default: printf("\tAddress Family Name: Unknown\n"); break; } } printf("\n"); } /* 将数字类型的IP地址转换成字符串类型的 */ #define IPTOSBUFFERS 12 char *iptos(u_long in) { static char output[IPTOSBUFFERS][3*4+3+1]; static short which; u_char *p; p = (u_char *)∈ which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return output[which]; } char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen) { socklen_t sockaddrlen; #ifdef WIN32 sockaddrlen = sizeof(struct sockaddr_in6); #else sockaddrlen = sizeof(struct sockaddr_storage); #endif if(getnameinfo(sockaddr, sockaddrlen, address, addrlen, NULL, 0, NI_NUMERICHOST) != 0) address = NULL; return address; }
改编版:
#include <stdio.h> #include <stdlib.h> #include <pcap.h> void ifprint(pcap_if_t *d); char *iptos(u_long in); //u_long即为 unsigned long int main(){ pcap_if_t * alldevs; //所有网络适配器 pcap_if_t *d; //选中的网络适配器 char errbuf[PCAP_ERRBUF_SIZE]; //错误缓冲区,大小为256 char source[PCAP_ERRBUF_SIZE]; int i = 0; //适配器计数变量 /** int pcap_findalldevs_ex ( char * source, struct pcap_rmtauth * auth, pcap_if_t ** alldevs, char * errbuf ); PCAP_SRC_IF_STRING代表用户想从一个本地文件开始捕获内容; */ //获取本地适配器列表 if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&alldevs,errbuf) == -1){ //结果为-1代表出现获取适配器列表失败 fprintf(stderr,"Error in pcap_findalldevs_ex:\n",errbuf); //exit(0)代表正常退出,exit(other)为非正常退出,这个值会传给操作系统 exit(1); } //打印设备列表信息 /** d = alldevs 代表赋值第一个设备,d = d->next代表切换到下一个设备 结构体 pcap_if_t: pcap_if * next 指向下一个pcap_if,pcap_if_t和pcap_if 结构是一样的 char * name 代表适配器的名字 char * description 对适配器的描述 pcap_addr * addresses 适配器存储的地址 u_int flags 适配器接口标识符,值为PCAP_IF_LOOPBACK */ for(d = alldevs;d !=NULL;d = d->next){ printf("-----------------------------------------------------------------\nnumber:%d\nname:%s\n",++i,d->name); if(d->description){ //打印适配器的描述信息 printf("description:%s\n",d->description); }else{ //适配器不存在描述信息 printf("description:%s","no description\n"); } //打印本地环回地址 printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no"); /** pcap_addr * next 指向下一个地址的指针 sockaddr * addr IP地址 sockaddr * netmask 子网掩码 sockaddr * broadaddr 广播地址 sockaddr * dstaddr 目的地址 */ pcap_addr_t *a; //网络适配器的地址用来存储变量 for(a = d->addresses;a;a = a->next){ //sa_family代表了地址的类型,是IPV4地址类型还是IPV6地址类型 switch (a->addr->sa_family) { case AF_INET: //代表IPV4类型地址 printf("Address Family Name:AF_INET\n"); if(a->addr){ //->的优先级等同于括号,高于强制类型转换,因为addr为sockaddr类型,对其进行操作须转换为sockaddr_in类型 printf("Address:%s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr)); } if (a->netmask){ printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr)); } if (a->broadaddr){ printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr)); } if (a->dstaddr){ printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr)); } break; case AF_INET6: //代表IPV6类型地址 printf("Address Family Name:AF_INET6\n"); printf("this is an IPV6 address\n"); break; default: break; } } } //i为0代表上述循环未进入,即没有找到适配器,可能的原因为Winpcap没有安装导致未扫描到 if(i == 0){ printf("interface not found,please check winpcap installation"); } //释放网络适配器列表 pcap_freealldevs(alldevs); int inum; scanf_s("%d", &inum); return 0; } /* 将数字类型的IP地址转换成字符串类型的 */ #define IPTOSBUFFERS 12 char *iptos(u_long in) { static char output[IPTOSBUFFERS][3*4+3+1]; static short which; u_char *p; p = (u_char *)∈ which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); sprintf_s(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return output[which]; }
需要特别指出的是,如果你用的是VC,那么下面的ipv6信息是无法编译通过的,需要在VS2005以上的环境下才能编译通过。
我用的Eclipse,编译器是mingw,很遗憾,这个也不支持ipv6信息的获取,调试了一段时间后发现无法编译通过。
既然这样的话我们就将 包含ipv6信息的代码去掉来运行。
#include "pcap.h" #ifndef WIN32 #include <sys/socket.h> #include <netinet/in.h> #else #include <winsock.h> #endif // 函数原型 void ifprint(pcap_if_t *d); char *iptos(u_long in); char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen); int main() { pcap_if_t *alldevs; pcap_if_t *d; char errbuf[PCAP_ERRBUF_SIZE+1]; char source[PCAP_ERRBUF_SIZE+1]; printf("Enter the device you want to list:\n" "rpcap:// ==> lists interfaces in the local machine\n" "rpcap://hostname:port ==> lists interfaces in a remote machine\n" " (rpcapd daemon must be up and running\n" " and it must accept 'null' authentication)\n" "file://foldername ==> lists all pcap files in the give folder\n\n" "Enter your choice: "); fgets(source, PCAP_ERRBUF_SIZE, stdin); source[PCAP_ERRBUF_SIZE] = '\0'; /* 获得接口列表 */ if (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1) { fprintf(stderr,"Error in pcap_findalldevs: %s\n",errbuf); exit(1); } /* 扫描列表并打印每一项 */ for(d=alldevs;d;d=d->next) { ifprint(d); } pcap_freealldevs(alldevs); return 1; } /* 打印所有可用信息 */ void ifprint(pcap_if_t *d) { pcap_addr_t *a; /*char ip6str[128];*/ /* 设备名(Name) */ printf("%s\n",d->name); /* 设备描述(Description) */ if (d->description) printf("\tDescription: %s\n",d->description); /* Loopback Address*/ printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no"); /* IP addresses */ for(a=d->addresses;a;a=a->next) { printf("\tAddress Family: #%d\n",a->addr->sa_family); switch(a->addr->sa_family) { case AF_INET: printf("\tAddress Family Name: AF_INET\n"); if (a->addr) printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr)); if (a->netmask) printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr)); if (a->broadaddr) printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr)); if (a->dstaddr) printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr)); break; /* case AF_INET6: printf("\tAddress Family Name: AF_INET6\n"); if (a->addr) printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str))); break; */ default: printf("\tAddress Family Name: Unknown\n"); break; } } printf("\n"); } /* 将数字类型的IP地址转换成字符串类型的 */ #define IPTOSBUFFERS 12 char *iptos(u_long in) { static char output[IPTOSBUFFERS][3*4+3+1]; static short which; u_char *p; p = (u_char *)∈ which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1); sprintf(output[which], "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); return output[which]; } /* char* ip6tos(struct sockaddr *sockaddr, char *address, int addrlen) { socklen_t sockaddrlen; #ifdef WIN32 sockaddrlen = sizeof(struct sockaddr_in6); #else sockaddrlen = sizeof(struct sockaddr_storage); #endif if(getnameinfo(sockaddr, sockaddrlen, address, addrlen, NULL, 0, NI_NUMERICHOST) != 0) address = NULL; return address; } */
Enter the device you want to list: rpcap:// ==> lists interfaces in the local machine rpcap://hostname:port ==> lists interfaces in a remote machine (rpcapd daemon must be up and running and it must accept 'null' authentication) file://foldername ==> lists all pcap files in the give folder Enter your choice: rpcap://
rpcap://\Device\NPF_{5AC72F8D-019C-4003-B51B-7ABB67AF392A} Description: Network adapter 'Microsoft' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #23 Address Family Name: Unknown rpcap://\Device\NPF_{C17EB3F6-1E86-40E5-8790-AC2518B74D05} Description: Network adapter 'Microsoft' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #2 Address Family Name: AF_INET Address: 192.168.95.1 Netmask: 255.255.255.0 Broadcast Address: 255.255.255.255 rpcap://\Device\NPF_{33E23A2F-F791-409B-8452-A3FB5A78B73E} Description: Network adapter 'Qualcomm Atheros Ar81xx series PCI-E Ethernet Controller' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #23 Address Family Name: Unknown Address Family: #23 Address Family Name: Unknown Address Family: #2 Address Family Name: AF_INET Address: 121.250.216.237 Netmask: 255.255.255.0 Broadcast Address: 255.255.255.255 rpcap://\Device\NPF_{DCCF036F-A9A8-4225-B980-D3A3F0575F5B} Description: Network adapter 'Microsoft' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #23 Address Family Name: Unknown rpcap://\Device\NPF_{D62A0060-F424-46FC-83A5-3394081685FD} Description: Network adapter 'Microsoft' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #2 Address Family Name: AF_INET Address: 192.168.191.1 Netmask: 255.255.255.0 Broadcast Address: 255.255.255.255 rpcap://\Device\NPF_{B5224A53-8450-4537-AB3B-9869158121CD} Description: Network adapter 'Microsoft' on local host Loopback: no Address Family: #23 Address Family Name: Unknown Address Family: #2 Address Family Name: AF_INET Address: 0.0.0.0 Netmask: 255.0.0.0 Broadcast Address: 255.255.255.255