数据流的过滤引擎是winpcap的一个较强功能,它是通过pcap_compile()和pcap_setfilter()函数来实现的。pcap_compile()用来编译一个过滤设备,pcap_setfilter()用来联系一个在内核驱动上过滤的过滤器,这个时候所有的网络数据包都将经过过滤器,并拷贝到应用程序中。
下面的代码显示的是只提取udp报文,并且解析出udp的各个字段:
#define HAVE_REMOTE
#include "pcap.h"
/**4 bit 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 version; /**版本+首部长度**/
u_char tos; /**服务类型**/
u_short tlen; /**总长度**/
u_short identification; /**标识**/
u_short flag_fo; /**标志+偏移**/
u_char ttl; /**生存时间**/
u_char proto; /**协议**/
u_short crc; /**校验和**/
ip_address saddr; /**源ip**/
ip_address daddr; /**目的ip**/
u_int op_pad; /**可选项**/
}ip_header;
typedef struct udp_header
{
u_short sport;
u_short dport;
u_short len;
u_short crc;
}udp_header;
/**数据包处理函数**/
void pacp_handle(u_char *param,const struct pcap_pkthdr *header,const u_char *pkt_data);
void main()
{
pcap_if_t *alldevs;
pcap_if *d;
int i=0;
char errbuf[PCAP_ERRBUF_SIZE];
/** 获取本地机器设备列表,以下两个函数等价 **/
//if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1)
if(pcap_findalldevs(&alldevs,errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs_ex: %s\n", errbuf);
exit(1);
}
/** 打印列表 **/
for(d= alldevs; d != NULL; d= d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No deion available)\n");
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return;
}
int inum;
printf("Enter the interface number:");
scanf("%d",&inum);
if(inum < 1 || inum > i)
{
printf("interface number out of range");
pcap_freealldevs(alldevs);
return;
}
for(d = alldevs, i = 0; i < inum-1; i++,d = d->next);
pcap_t *adhandle;
if((adhandle = pcap_open_live(d->name,65535,1,1000,errbuf)) == NULL)
{
printf("worng");
pcap_freealldevs(alldevs);
return;
}
/**判断是否为以太网类型**/
if(pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr,"\n This program works only on Ethernet networks \n");
pcap_freealldevs(alldevs);
return;
}
/**求掩码,如果没有求到,则默认为c类地址**/
u_int netmask;
if(d->addresses != NULL)
{
netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
}
else
netmask = 0xffffff;
/** 编译过滤器 **/
struct bpf_program fcode;
char packet_filter[] = "ip and udp";
if(pcap_compile(adhandle,&fcode,packet_filter,1,netmask) < 0)
{
fprintf(stderr,"\n Unable to compile the pcaket file.\n");
pcap_freealldevs(alldevs);
return;
}
/** 设置过滤器 **/
if(pcap_setfilter(adhandle,&fcode) < 0)
{
fprintf(stderr,"\n Error setting the filter\n");
pcap_freealldevs(alldevs);
return;
}
/** 不再需要设备列表了,释放它 **/
pcap_freealldevs(alldevs);
pcap_loop(adhandle,0,pacp_handle,NULL);
getchar();
}
/**回调函数:数据包处理函数**/
void pacp_handle(u_char *param,const struct pcap_pkthdr *header,const u_char *pkt_data)
{
struct tm *ltime;
char timestr[16];
ip_header *ih;
udp_header *uh;
u_int ip_len;
u_short sport,dport;
/**时间格式转换**/
ltime = localtime(&header->ts.tv_sec);
strftime(timestr,sizeof(timestr),"%H:%M:%S",ltime);
printf("%s,%.6d,%d",timestr,header->ts.tv_usec,header->len);
/**找到ip头部,以太网头部14个字节**/
ih = (ip_header *)(pkt_data + 14);
/**找到udp头部**/
ip_len = (ih->version & 0xf) * 4;
uh = (udp_header *)((u_char *)ih + ip_len);
/**将端口信息从网络型转换为主机型**/
sport = ntohs(uh->sport);
dport = ntohs(uh->dport);
/**输出ip地址和端口**/
printf(" %d.%d.%d.%d:%d --> %d.%d.%d.%d:%d\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport);
}
需要说明的是,在上一篇的环境下运行该程序的时候,还得添加ws2_32.lib,否则链接不过。