Pcap读取tcp头字段无声地失败

时间:2014-02-20 22:24:23

标签: c pcap

我正在尝试使用pcap,但我在阅读序列号时遇到了一些麻烦。

我使用下面的代码来监听一个incomming数据包,但是当我尝试读取序列号时,程序停止而没有错误。只是默默地停止执行。

此代码主要来自tcpdump网站的pcap教程。

bpf_u_int32 net=0, mask=0;
pcap_t *descr = NULL;
struct bpf_program filter;
struct ip *iphdr = NULL;
struct tcphdr *tcphdr = NULL;
struct pcap_pkthdr pkthdr;
const unsigned char *packet = NULL;
char pcap_errbuf[PCAP_ERRBUF_SIZE];
char filter_exp[] = "tcp port 111 dst host 10.0.0.10";
char * dev;

// Define the device
dev = pcap_lookupdev(pcap_errbuf);
if (dev == NULL) {
    printf( "Couldn't find default device: %s\n", pcap_errbuf);  fflush(stdout);
    exit(1);
}

// Find the properties for the device 
if( pcap_lookupnet(dev, &net, &mask, pcap_errbuf) == -1 ){
    printf("Couldn't get netmask for device %s: %s\n", dev, pcap_errbuf);  fflush(stdout);
    net = 0;
    mask = 0;
}
// Open the session in non-promiscuous mode 
descr = pcap_open_live(dev, BUFSIZ, 0, 1000, pcap_errbuf);
if (descr == NULL) {
    printf("Couldn't open device %s: %s\n", dev, pcap_errbuf); fflush(stdout);
    exit(1);
}
// Compile and apply the filter 
if( pcap_compile(descr, &filter, filter_exp, 0, net) == -1) {
    printf("Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
    exit(1);
}
if (pcap_setfilter(descr, &filter) == -1) {
    printf( "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(descr)); fflush(stdout);
    exit(1);
}

packet = pcap_next(descr, &pkthdr );
iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);

printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
    printf("test2\n");
pcap_close(descr);

打印“test1”,但“test2”和“SEQ:%d”不打印。如果没有任何错误,很难调试。

之前有人见过这个吗?

由于

1 个答案:

答案 0 :(得分:2)

Nikolai Fetissov是正确的 - 您必须检查pcap_next()是否返回NULL。例如,如果超时到期且没有数据包到达,它可能会返回NULL。在这种情况下,您应该保持循环,直到它返回非空值。

然而,如果出现错误,它也可能返回NULL,并且该错误可能意味着您将不再获取任何数据包。使用的更好的例程是pcap_next_ex(),如果没有问题地读取数据包则返回1,如果从实时捕获读取数据包则返回0并且超时到期,如果读取数据包时发生错误则返回-1,如果正在从保存文件中读取数据包,则没有更多数据包要从保存文件中读取。

在你的情况下,你正在进行实时捕获,所以你应该使用pcap_next_ex(),并循环直到它返回1,在这种情况下你打印包信息,或-1,在这种情况下你报告错误并退出:

int status;

while ((status = pcap_next_ex(descr, &pkthdr, &packet)) == 0)
    ;

if (status == -1) {
    fprintf(stderr, "pcap_next_ex failed: %s\n", pcap_geterr(descr));
    exit(1);
}

iphdr = (struct ip *)(packet+14);
tcphdr = (struct tcphdr *)(packet+14+20);

printf("test1\n"); fflush( stdout );
printf("SEQ: %d\n", ntohl(tcphdr->th_seq) ); fflush( stdout );
printf("test2\n");
pcap_close(descr);

另请注意,无法保证IPv4标头长度为20个字节 - 可能更长,因此您需要从IPv4标头的第一个字节(标头长度​​/版本字段)中提取标头长度,将其相乘乘以4(因为它是以4字节字为单位),并在计算TCP标头的地址时使用它,而不是使用硬编码的20。

此外,您还应确保pcap_datalink(descr)返回的设备的链接层标头类型为DLT_EN10MB,以确保 >以太网报头而不是其他类型的报头。

另外,我刚刚复制了你的printf代码,因为我专注于捕获问题。其他人添加了ntohl()调用,这在打印序列号时是必需的 - IP和TCP头中的多字节数字字段是“网络字节顺序”,即大端,但您可能正在运行little-endian机器,因此在打印之前必须将序列号转换为主机字节顺序。