所以我正在使用一个需要发送和接收原始以太网帧的设备。这是一个无线收音机,它使用以太网向主机发送状态信息。它使用的协议实际上是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: %sn",errbuf);
return 2;
}
printf("Device: %sn",dev);
pcap_t *handle;
handle=pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if(handle==NULL){
fprintf(stderr, "Device open error: %sn",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: %sn",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: %sn",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: %sn",errbuf);
return 2;
}
if(pcap_setfilter(handle, &fp)==-1){
pcap_perror(handle, errbuf);
fprintf(stderr, "Install filter error: %sn",errbuf);
return 2;
}
printf("Starting capturen");
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: %sn",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: %.6fn",seconds);
}
void *thread_helper(pcap_t *handle){
pcap_loop(handle, -1, got_packet, NULL);
return NULL;
}
是否有办法在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: %sn", strerror(errno));
return 2;
}
这将完全禁用BPF通常做的缓冲(这就是你在libpcap中看到的缓冲;请参阅BPF(4)手册页),以便数据包一到达就被发送。改变缓冲的方式完成的方式可能会导致带通滤波器的内部缓冲区填满会比如果正常缓冲完成,所以可能导致数据包丢失时,他们不可能会丢失,但使用pcap_set_buffer_size()
Kiran Bandla建议,可以帮助如果它发生(它可能不会,特别是考虑到您正在使用一个过滤器来防止"无趣"数据包放入带通滤波器的缓冲区)。
在Linux上,这是目前没有必要的——缓冲所做的并没有一个数据包传递的超时。在Solaris上,它将在Solaris 11上进行类似的操作(因为libpcap使用BPF),但在早期版本的Solaris上进行不同的操作(因为它们没有BPF,而libpcap使用DLPI)。在WinPcap的Windows上,pcap_open()
有一个标志。
libpcap的未来版本可能会有这样的API;我不能保证什么时候会发生。
可以使用pcap_set_buffer_size设置捕获缓冲区的大小。请确保在激活捕获句柄之前执行此操作。
减小缓冲区大小并不总是一个好主意。注意您的CPU,并以高捕获率丢弃数据包。