From owner-freebsd-bugs@FreeBSD.ORG Sat Feb 13 04:50:02 2010 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 34EA81065676 for ; Sat, 13 Feb 2010 04:50:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 144938FC14 for ; Sat, 13 Feb 2010 04:50:02 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id o1D4o1h7056336 for ; Sat, 13 Feb 2010 04:50:01 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id o1D4o14x056335; Sat, 13 Feb 2010 04:50:01 GMT (envelope-from gnats) Resent-Date: Sat, 13 Feb 2010 04:50:01 GMT Resent-Message-Id: <201002130450.o1D4o14x056335@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Guy Harris Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 6EAFF106566C for ; Sat, 13 Feb 2010 04:45:31 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id 600C58FC0C for ; Sat, 13 Feb 2010 04:45:31 +0000 (UTC) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.3/8.14.3) with ESMTP id o1D4jVva075184 for ; Sat, 13 Feb 2010 04:45:31 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.3/8.14.3/Submit) id o1D4jUKm075183; Sat, 13 Feb 2010 04:45:31 GMT (envelope-from nobody) Message-Id: <201002130445.o1D4jUKm075183@www.freebsd.org> Date: Sat, 13 Feb 2010 04:45:31 GMT From: Guy Harris To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/143855: non-blocking BPF reads return -1/EWOULDBLOCK until the store buffer fills up X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 13 Feb 2010 04:50:02 -0000 >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@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 #include #include #include #include #include #include #include #include #include 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 " << 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& dupFilter) { dupFilter.clear(); dupFilter.resize(pcapFilter.size()+1, 0); for (uint32_t i=0; i" << 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 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 <" <" << 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 <" <" << 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 <" <" << 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 <" <" << 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 <"<" << 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: