ANNOUNCEMENT: Live Wireshark University & Allegro Packets online APAC Wireshark Training Session
July 17th, 2024 | 10:00am-11:55am SGT (UTC+8) | Online

Ethereal-dev: [Ethereal-dev] [Fwd: [Bug 54161] New - libpcap hangs if no filter set, pcap_nex

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Alexander Dupuy <dupuy@xxxxxxxxxxxxxxx>
Date: Tue, 02 Oct 2001 16:29:48 -0400
I've run into a few pretty annoying bugs with the new Linux 7.1 (and
7.0) libpcap, mostly having to do with the implementation of the TURBO
mode supported by the kernel PACKET_RX_RING or PACKET_TRECV socket
options.

While you can work around some things (always install a default filter),
the pcap_next bug is serious and there is no workaround for that.  I
suspect there aren't many (any?) programs which use that interface, or
it would have been reported already.

For programs which want non-blocking behavior, the fact that libpcap
never calls recvfrom (but rather blocks in calls to poll() with an
infinite timeout) makes the trick of setting non-blocking mode on the
file descriptor useless, and the fencepost error in packet_ring_recv
means that even if, as Guy Harris suggests, you use select or poll
before calling pcap_dispatch() and you only ask for one packet, you may
end up blocking waiting for the second packet that you didn't ask for. 
With a narrow filter and/or low traffic, you may block quite a while.

Because of the impossibility of working around this blocking glitch, and
the desirability of avoiding excessive system calls (now that the
captured packets are read directly into user memory), it seemed very
worthwhile to make the read timeout parameter to pcap_open_live mean
something, and the attached patch not only fixes the fencepost error,
but modifies the libpcap source from the tcpdump-3.4-39.src.rpm (as
patched by the dif.gz and patch files in the SRPM) so that the read
timeout is honoured when TURBO mode is in effect.

If TURBO mode doesn't work for any reason, you should get the old
behavior (where read timeouts have no effect), but the traditional work
arounds of non-blocking mode and/or calls to select/poll will work in
the non-TURBO mode.  Use of non-blocking mode is probably better, since
it has no effect on TURBO mode performance, unlike the select() hack.

@alex
--- Begin Message ---
Date: Sat, 29 Sep 2001 02:45:32 -0400
Please do not reply directly to this email. All additional
comments should be made in the comments box of this bug
report.

https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=54161

--- shadow/54161	Sat Sep 29 02:45:32 2001
+++ shadow/54161.tmp.31534	Sat Sep 29 02:45:32 2001
@@ -0,0 +1,45 @@
+Bug#: 54161
+Product: Red Hat Linux
+Version: 7.1
+Platform: All
+OS/Version: Linux
+Status: NEW   
+Resolution: 
+Severity: normal
+Priority: normal
+Component: tcpdump
+AssignedTo: harald@xxxxxxxxxx                            
+ReportedBy: dupuy@xxxxxxxxxxxxxxx               
+URL: 
+Summary: libpcap hangs if no filter set,  pcap_next loses every other packet
+
+From Bugzilla Helper:
+User-Agent: Mozilla/4.78 [en] (X11; U; Linux 2.4.9 i686)
+
+Description of problem:
+When using the PACKET_RX_RING interface, poll() on the packet fd will never
+succeed if there is no kernel BPF filter installed.
+Due to a fencepost error in packet_ring_recv, the pcap_dispatch and
+pcap_loop interfaces will actually receive one more packet than requested. 
+The pcap_next function, which only returns one packet, effectively loses
+the first one.
+
+Version-Release number of selected component (if applicable):
+
+
+How reproducible:
+Always
+
+Steps to Reproduce:
+1. write a program that uses libpcap but doesn't set a filter
+2. write a program that loops calling pcap_next and prints destination
+address of packet
+3. run the programs and compare output to that of "tcpdump -nes 1"
+
+
+Actual Results:  the first program never prints anything
+the second program only prints every other packet
+
+Expected Results:  the programs should print lines for every packet
+
+Additional info:




--- End Message ---
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pcap/pcap.h>

int main()
{
  char ebuf[PCAP_ERRBUF_SIZE];
  char *dev;
  pcap_t *pcap;
  const int PROMISC = 1;	/* put interface in promiscuous mode */
  const u_char *packet;
  time_t stamp;
  struct pcap_pkthdr hdr;

  dev = pcap_lookupdev(ebuf);

  if (!(pcap = pcap_open_live(dev, 96, PROMISC, 0, ebuf)))
    return 1;			/* need to be root */

#ifndef BUG_ONE
  /*
   * Linux 2.4 systems support TURBO mode, where the packets can be copied
   * directly into a receive ring in user memory.  This is great for
   * performance, but typically has a bug that if you do not set a kernel
   * bpf filter program, select/poll will never succeed, and you will hang
   * forever trying to receive packets.  So we always set a filter program
   * that just returns snaplen.
   */
  {
    struct bpf_program prog;
    struct bpf_insn bpfret;

    prog.bf_len = 1;
    prog.bf_insns = &bpfret;
    bpfret.code = BPF_RET;
    bpfret.jt = 0;
    bpfret.jf = 0;
    bpfret.k = 96;

    pcap_setfilter(pcap, &prog);
  }
#endif /* !BUG_ONE */

  /*
   * RedHat 7.1 systems have a buggy implementation of packet_ring_recv that
   * causes every other packet to be lost when using pcap_next, as shown below.
   */
  while ((packet = pcap_next(pcap, &hdr)))
    {
      stamp = hdr.ts.tv_sec;
      strftime(ebuf, PCAP_ERRBUF_SIZE, "%H:%M:%S", localtime(&stamp));
      printf ("%s.%06d %s - %x:%x:%x:%x:%x:%x %x:%x:%x:%x:%x:%x\n",
	      ebuf, hdr.ts.tv_usec, dev,
	      packet[6], packet[7], packet[8],
	      packet[9], packet[10], packet[11],
	      packet[0], packet[1], packet[2],
	      packet[3], packet[4], packet[5]);
      fflush(stdout);
    }

  return 0;
}

#ifdef PATCH_TO_LIBPCAP
--- pcap-int.h.ORIG	Fri Sep 28 13:53:14 2001
+++ pcap-int.h	Fri Sep 28 20:34:58 2001
@@ -65,6 +65,7 @@
 	unsigned int   ringbufsize;
 	unsigned int   iovmax;
 	unsigned int   iovhead;
+	int to_ms;
 	char *device;
 	int soft_bpf;
 	unsigned short protocol;
--- pcap-linux.c.ORIG	Fri Sep 28 13:53:14 2001
+++ pcap-linux.c	Sat Sep 29 02:30:33 2001
@@ -203,9 +203,12 @@
 {
 	struct iovec *ring = p->md.iovec;
 	int n = 0;
+	int to_ms = p->md.to_ms;
 
-	if (cnt<=0)
+	if (cnt<=0) {
 		cnt=0x7FFFFFFF;
+		to_ms = -1;
+	}
 
 	for (;;) {
 		while (*(unsigned long*)ring[p->md.iovhead].iov_base) {
@@ -257,7 +260,7 @@
 			h->tp_status = 0;
 			mb();
 			p->md.iovhead = (p->md.iovhead == p->md.iovmax) ? 0 : p->md.iovhead+1;
-			if (++n > cnt)
+			if (++n >= cnt)
 				return n;
 		}
 
@@ -269,8 +272,10 @@
 			pfd.revents = 0;
 			pfd.events = POLLIN|POLLRDNORM|POLLERR;
 			do {
-			  pres = poll(&pfd, 1, -1);
+			  pres = poll(&pfd, 1, to_ms);
 			} while (pres == -1 && errno == EINTR);
+			if (pres == 0)
+				return n;
 			if (pres < 0) {
 				sprintf(p->errbuf, "poll: %s", pcap_strerror(errno));
 				return n ? : -1;
@@ -605,6 +610,7 @@
 		p->bufsize = ifr.ifr_mtu + 64;
 	}
 
+	p->md.to_ms = to_ms > 0 ? to_ms : -1;
 	p->md.protocol = proto;
 	if (snaplen < 0 || snaplen >= 65535)
 		snaplen = 1514;


#endif