kern/143855: non-blocking BPF reads return -1/EWOULDBLOCK until the store buffer fills up

Guy Harris guy at alum.mit.edu
Sat Feb 13 04:50:02 UTC 2010


>Number:         143855
>Category:       kern
>Synopsis:       non-blocking BPF reads return -1/EWOULDBLOCK until the store buffer fills up
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Feb 13 04:50:01 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator:     Guy Harris
>Release:        7.0-RELEASE
>Organization:
>Environment:
FreeBSD gharris-freebsd.localdomain 7.0-RELEASE FreeBSD 7.0-Release #0: Sun Feb 24 19:59:52 UTC 2000     root at logan.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
Non-blocking reads from a BPF device not in immediate mode will not rotate the buffers even if there's data in the store buffer but not in the hold buffer, so, until the store buffer fills up, the reads will return -1 and set errno to EWOULDBLOCK.
>How-To-Repeat:
Compile the following (C++) program, run it on an adapter with the filter "icmp" on a reasonably quiet network, and then, on the same machine, ping some host the pings to which will go out on the same network.  Note that the program doesn't report any packets having been seen.

#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <net/bpf.h>
#include <pcap.h>

pcap_t* create(const std::string& name, 
			   const std::string& pcapFilter, 
			   uint32_t snapLen, bool promisc);
bool capture(pcap_t * pcapSession);
void close(pcap_t* pcapSession);


int main(int argc, char** argv)
{
	if (argc != 3)
	{
		std::cerr << "Usage: libpcaptest <interface> <filter>" 
               << std::endl;
		return 1;
	}
	
	std::string name(argv[1]), filter(argv[2]);
	std::cout << "Capturing from '" << name << " with filter "
       << filter << std::endl;
	 
	pcap_t * pcapSession = create(name, filter, 128, true);
	capture(pcapSession);
	close(pcapSession);
	return 0;
}

/**
This is the callback
**/
void test_pcap_handler(u_char* user, const struct pcap_pkthdr* header, 
					   const u_char* pkt_data)
{
	std::cout << "Packet captured" << std::endl;
}

/**
Temporary used since on Windows they forgot to sign as 'const char*'
the filter string provided to pcap_compile...
**/
void duplicateFilterString(const std::string& pcapFilter, 
    std::vector<char>& dupFilter)
{
	dupFilter.clear();
	dupFilter.resize(pcapFilter.size()+1, 0);
	
	for (uint32_t i=0; i<pcapFilter.size(); ++i)
		dupFilter[i] = pcapFilter[i];
}

void close(pcap_t* pcapSession)
{
	if (pcapSession)
	{
		pcap_close(pcapSession);
	}
}
		
		
pcap_t* create(const std::string& name, 
			   const std::string& pcapFilter, 
			   uint32_t snapLen, bool promisc)
{
	char errbuf[PCAP_ERRBUF_SIZE];
	pcap_t* pcapSession;
		
	if ((pcapSession = pcap_open_live(name.c_str(), 
             snapLen, promisc ? 1 : 0, 1000,	errbuf)) == NULL)
	{
		std::cerr << "Failed pcap_open_live because <"
                <<errbuf<<">" << std::endl;
		return NULL;
	}
			
	// compile the filter if it's been supplied or snapLen is provided
	if (pcapFilter.empty()==false || snapLen<65535)
	{
		// get netmask
		bpf_u_int32 pcapNetaddr, pcapMask;
		pcap_lookupnet(name.c_str(), &pcapNetaddr, &pcapMask, errbuf);
				
		struct bpf_program pcapFilterProgram;		
		std::vector<char> filterDup;
		duplicateFilterString(pcapFilter, filterDup);
				
		if (pcap_compile(pcapSession, &pcapFilterProgram, 
                    &filterDup[0], 1, pcapMask) == -1) 
		{
			std::string error = pcap_geterr(pcapSession);
			pcap_close(pcapSession);
			std::cerr << "Failed pcap_compile because <"
                       <<errbuf<<">" << std::endl;
			return NULL;
		}	
				
		if (pcap_setfilter(pcapSession, &pcapFilterProgram) == -1)
		{
			std::string error = pcap_geterr(pcapSession);
			pcap_freecode(&pcapFilterProgram);
			pcap_close(pcapSession);
			std::cerr << "Failed pcap_setfilter because <"
                       <<errbuf<<">" << std::endl;
			return NULL;
		}
				
		pcap_freecode(&pcapFilterProgram);
	}
			
	// set session in non blocking mode
	if (pcap_setnonblock(pcapSession, 1, errbuf)!=0)
	{
		pcap_close(pcapSession);
		std::cerr << "Failed pcap_setnonblock because <"
               <<errbuf<<">" << std::endl;
		return NULL;
	}

	/*
		Enable this for immediate delivery of packets through callback.
		
	uint32_t v = 1;
	if (ioctl(pcap_fileno(pcapSession), BIOCIMMEDIATE, &v) < 0) {
		pcap_close(pcapSession);
		std::cerr << "Failed ioctl BIOCIMMEDIATE" << std::endl;
		return NULL;
	}
	*/
	
	int dlt;
	const char *dlt_name;
	dlt = pcap_datalink(pcapSession);
	dlt_name = pcap_datalink_val_to_name(dlt);
	if (dlt_name == NULL) {
		(void)fprintf(stderr, 
"listening on %s, link-type %u, capture size %u bytes\n",
					  name.c_str(), dlt, snapLen);
	} else {
		(void)fprintf(stderr, 
"listening on %s, link-type %s (%s), capture size %u bytes\n",
					  name.c_str(), dlt_name,
					  pcap_datalink_val_to_description(dlt), snapLen);
	}
			
	return pcapSession;
}
		
bool capture(pcap_t * pcapSession)
{
	struct pcap_stat pcapStats;
		
	while (true)
	{
		int32_t ret = pcap_dispatch(pcapSession, 100, 
								test_pcap_handler, (u_char*)NULL);
		std::cout << "Read " << ret << " packets" << std::endl;
		if (pcap_stats(pcapSession, &pcapStats) != 0)
		{
				std::string error = pcap_geterr(pcapSession);
				std::cerr << "Failed pcap_setnonblock because <"
                               <<error<<">" << std::endl;
				return false;
		}
		std::cout << "ReceivedPackets " << pcapStats.ps_recv << 
				" DroppedPackets " << pcapStats.ps_drop << 
				" I/F DroppedPackets " << pcapStats.ps_ifdrop << std::endl;
				
				
		if (ret==-1)
		{
			std::string error = pcap_geterr(pcapSession);
			std::cerr << "Failed pcap_dispatch because <"<<error<<">" << std::endl;
			return NULL;
		}
				
				
		sleep(5);
	} 
	return true;
}

>Fix:


Patch attached with submission follows:

Index: bpf.c
===================================================================
--- bpf.c	(revision 68984)
+++ bpf.c	(working copy)
@@ -721,9 +721,12 @@
 	 * have arrived to fill the store buffer.
 	 */
 	while (d->bd_hbuf == 0) {
-		if (d->bd_immediate && d->bd_slen != 0) {
+		if ((d->bd_immediate || (ioflag & IO_NDELAY))
+		    && d->bd_slen != 0) {
 			/*
-			 * A packet(s) either arrived since the previous
+			 * We're in immediate mode, or are reading
+			 * in non-blocking mode, and a packet(s)
+			 * either arrived since the previous
 			 * read or arrived while we were asleep.
 			 * Rotate the buffers and return what's here.
 			 */


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list