libpcap没有实时接收,似乎是缓冲数据包

时间:2013-04-12 19:07:17

标签: ethernet libpcap buffering

所以我正在使用我需要发送和接收原始以太网帧的设备。它是无线电,它使用以太网向其主机发送状态消息。它使用的协议实际上是IPX,但我认为使用libpcap发送原始以太网帧要比挖掘数十年实现IPX的代码(由TCP / IP取代,因此它已经很老了)更容易。

我的程序发送一个请求数据包(这个数据包每次完全相同,它是无状态的),然后设备返回一个包含我需要的数据的响应数据包。我正在使用pcap_inject将帧和pcap_loop发送到另一个线程来进行接收。我最初在一个线程中拥有它,但尝试了2个线程,看看它是否解决了我遇到的问题。

问题是libpcap似乎没有实时接收数据包。它似乎缓冲了大约5个,然后立即处理它们。我希望能够尽快阅读它们。有没有办法在libpcap上禁用此缓冲,或者提高刷新率?

一些示例输出(我刚刚打印出收到数据包的时间)。请注意每组之间的时间间隔是多少

Time: 1365792602.805750 
Time: 1365792602.805791
Time: 1365792602.805806
Time: 1365792602.805816
Time: 1365792602.805825
Time: 1365792602.805834

Time: 1365792603.806886
Time: 1365792603.806925
Time: 1365792603.806936
Time: 1365792603.806944
Time: 1365792603.806952

Time: 1365792604.808007
Time: 1365792604.808044
Time: 1365792604.808055
Time: 1365792604.808063
Time: 1365792604.808071

Time: 1365792605.809158
Time: 1365792605.809194
Time: 1365792605.809204
Time: 1365792605.809214
Time: 1365792605.809223

这是注入代码:

char errbuf[PCAP_ERRBUF_SIZE];
char *dev="en0";
if(dev==NULL){
    fprintf(stderr,"Pcap error: %s\n",errbuf);
    return 2;
}

printf("Device: %s\n",dev);

pcap_t *handle;
handle=pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if(handle==NULL){
    fprintf(stderr, "Device open error: %s\n",errbuf);
    return 2;
}

//Construct the packet that will get sent to the radio
struct ether_header header;
header.ether_type=htons(0x0170);
int i;
for(i=0;i<6;i++){
    header.ether_dhost[i]=radio_ether_address[i];
    header.ether_shost[i]=my_ether_address[i];
}

unsigned char frame[sizeof(struct ether_header)+sizeof(radio_request_packet)];
memcpy(frame, &header, sizeof(struct ether_header));
memcpy(frame+sizeof(struct ether_header), radio_request_packet, sizeof(radio_request_packet));
if(pcap_inject(handle, frame, sizeof(frame))==-1){
    pcap_perror(handle, errbuf);
    fprintf(stderr, "Couldn't send frame: %s\n",errbuf);
    return 2;
}

bpf_u_int32 mask;
bpf_u_int32 net;
if(pcap_lookupnet(dev,&net,&mask,errbuf)==-1){
    pcap_perror(handle, errbuf);
    fprintf(stderr,"Net mask error: %s\n",errbuf);
    return 2;
}

char *filter="ether src 00:30:30:01:b1:35";
struct bpf_program fp;
if(pcap_compile(handle, &fp, filter, 0, net)==-1){
    pcap_perror(handle, errbuf);
    fprintf(stderr,"Filter error: %s\n",errbuf);
    return 2;
}

if(pcap_setfilter(handle, &fp)==-1){
    pcap_perror(handle, errbuf);
    fprintf(stderr, "Install filter error: %s\n",errbuf);
    return 2;
}

printf("Starting capture\n");
pthread_t recvThread;
pthread_create(&recvThread, NULL, (void *(*)(void *))thread_helper, handle);

while(1){
    if(pcap_inject(handle, frame, sizeof(frame))==-1){
        pcap_perror(handle, errbuf);
        fprintf(stderr, "Couldn't inject frame: %s\n",errbuf);
        return 2;
    }
    usleep(200000);
}

pcap_close(handle); 
return 0;

接收代码:

void got_packet(u_char *args,const struct pcap_pkthdr * header,const u_char * packet){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    double seconds=(double)tv.tv_sec + ((double)tv.tv_usec)/1000000.0;
    printf("Time: %.6f\n",seconds);
}


void *thread_helper(pcap_t *handle){
    pcap_loop(handle, -1, got_packet, NULL);
    return NULL;
}

2 个答案:

答案 0 :(得分:2)

  

有没有办法在libpcap上禁用此缓冲

目前没有libpcap API可以做到这一点。

但是,根据您运行的操作系统,可能有办法为该特定操作系统执行此操作,即您可以执行此操作,但是以非便携方式执行。

对于使用BPF的系统,包括* BSD和......

... OS X,考虑到"en0",我怀疑你正在使用它,这样做的方法是做一些如:

创建包含以下内容的set_immediate_mode.h头文件

extern int set_immediate_mode(int fd);

创建包含以下内容的set_immediate_mode.c源文件

#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <net/bpf.h>

#include "set_immediate_mode.h"

int
set_immediate_mode(int fd)
{
    int on = 1;

    return ioctl(fd, BIOCIMMEDIATE, &on);
}

如果您的计划尚未包含这些文件,请将#include <string.h>#include <errno.h>添加到您的计划中,将#include "set_immediate_mode.h"添加到您的计划中,并在pcap_open_live()调用成功后添加以下代码:

int fd;
fd = pcap_fileno(handle);
if (fd == -1) {
    fprintf(stderr, "Can't get file descriptor for pcap_t (this should not happen)\n");
    return 2;
}
if (set_immediate_mode(fd) == -1) {
    fprintf(stderr, "BIOCIMMEDIATE failed: %s\n", strerror(errno));
    return 2;
}

这将完全禁用BPF通常执行的缓冲(这是您使用libpcap看到的缓冲;请参阅BPF(4)手册页),以便数据包一发布就会立即传送到达。这改变了缓冲的方式,可能导致BPF的内部缓冲区填充速度比正常缓冲完成时更快,因此可能导致数据包丢失丢失了,但是如Kiran Bandla所建议的那样使用pcap_set_buffer_size()可以帮助如果它发生(它可能没有,特别是考虑到你使用过滤器来保持“无趣”)数据包首先被放入BPF的缓冲区。)

在Linux上,这当前不是必需的 - 进行的缓冲没有超时传送数据包。在Solaris上,它将在Solaris 11上类似地完成(因为libpcap使用BPF),但在早期版本的Solaris上会有不同的做法(因为它们没有BPF,libpcap使用DLPI)。在使用WinPcap的Windows上,pcap_open()有一个标志。

未来版本的libpcap可能会有一个API;我不能保证什么时候会发生。

答案 1 :(得分:0)

您可以使用pcap_set_buffer_size设置捕获缓冲区大小。确保在激活捕获句柄之前执行此操作。

降低缓冲区大小并不总是一个好主意。注意你的CPU,并以高捕获率丢弃数据包。