获得设备列表 pcap_findalldevs_ex,然后从设备列表中跳转到某个设备,并带开适配器。
调用pcap_datalink是为了减少数据包的数量:过滤掉不属于以太网的包。之后再设置过滤器,这里只是为了抓取 ip 和 tcp 的包
char packet_filter[] = "ip and tcp";
由于使用回调函数是由数据包捕获驱动直接控制,所以用户程序是无法直接控制的。这里我没有采用回调函数,而是使用的pcap_next_ex
#include <stdio.h> #include <iostream> #define HAVE_REMOTE #include "pcap.h" #include "remote-ext.h" #pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "wpcap.lib") using namespace std; // 用户保存4字节的IP地址 typedef struct ip_address { u_char byte1; u_char byte2; u_char byte3; u_char byte4; }ip_address; // 用于保存IPV4的首部 typedef struct ip_header { u_char ver_ihl; u_char tos; u_short tlen; u_short identification; u_short flags_fo; u_char ttl; u_char proto; u_short crc; ip_address saddr; ip_address daddr; u_int op_pad; }ip_header; // 保存UDP首部 typedef struct udp_header { u_short sport; u_short dport; u_short len; u_short crc; }udp_header; void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); int main() { pcap_if_t *alldevs; pcap_if_t *d; pcap_t *adhandle; char errbuf[PCAP_ERRBUF_SIZE]; int inum; int i = 0; u_int netmask; char packet_filter[] = "ip and tcp"; struct bpf_program fcode; int res; struct pcap_pkthdr *header; struct tm *ltime; const u_char *pkt_data; time_t local_tv_sec; char timestr[16]; ip_header *ih; u_char *packet; // 获得设备列表 pcap_findalldevs_ex() if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf); exit(1); } for(d = alldevs; d; d = d->next) { printf("%d. %s", ++i, d->name); if(d->description) { printf("(%s)\n", d->description); } else { printf("No description available\n"); } } if(0 == i) { printf("\nNo interface found!Make sure WinPcap is installed\n"); return -1; } printf("Enter the interface number(1-%d):", i); scanf_s("%d", &inum); if(inum < 1 || inum > i) { printf("\nInterface number out of range.\n"); pcap_freealldevs(alldevs); return -1; } for(d = alldevs, i = 0; i < inum-1; d=d->next, i++); // 跳转到该设备,打开适配器 // 设备名,要捕捉的数据包的部分(65536保证能捕获到不同数据链路层上的每个数据包的全部内容),混杂模式,读取超时时间,错误缓冲池 if((adhandle = pcap_open_live(d->name, 65536, 1, 1000, errbuf)) == NULL) { fprintf(stderr, "\nUnable to open the adapter.%s is not supported by WinPcap\n", errbuf); pcap_freealldevs(alldevs); return -1; } // 检查数据链路层(只考虑了以太网) if(pcap_datalink(adhandle) != DLT_EN10MB) { fprintf(stderr, "\nThis program works only on Ethernet networks.\n"); pcap_freealldevs(alldevs); return -1; } if(d->addresses != NULL) { // 获得接口的第一个地址的掩码 netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr; } else { netmask = 0xffffff; } // 编译过滤器 if(pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0) { fprintf(stderr, "\nUnable to compile the packet filter.Check the syntax\n"); pcap_freealldevs(alldevs); return -1; } // 设置过滤器 if(pcap_setfilter(adhandle, &fcode) < 0) { fprintf(stderr, "\nError setting the filter.\n"); pcap_freealldevs(alldevs); return -1; } printf("\nlistenting on %s...\n", d->description); while((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0) { // 请求超时 if(0 == res) { continue; } // 将时间戳转换成可识别的格式 local_tv_sec = header->ts.tv_sec; ltime = localtime(&local_tv_sec); strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime); ih = (ip_header *)(pkt_data + 14); //以太网头部长度 // 数据包的处理部分 printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len); printf("%d.%d.%d.%d -> %d.%d.%d.%d\n", ih->saddr.byte1, ih->saddr.byte2, ih->saddr.byte3, ih->saddr.byte4, ih->daddr.byte1, ih->daddr.byte2, ih->daddr.byte3, ih->daddr.byte4); } if(-1 == res) { printf("Error reading the packet:%s\n", pcap_geterr(adhandle)); return -1; } pcap_freealldevs(alldevs); return 0; }
可以发现 tcp 是对应着的