FreeBSD bpf bug

Peter Van Epp vanepp at sfu.ca
Mon Oct 16 12:48:54 EDT 2000


	With advise on how things should work from Carter I've found 
corrected (perhaps even properly :-)) a bug in FreeBSD's (and OpenBSD's) 
kernel bpf code. For small packet bursts (9 packets in the case I was testing)
bpf isn't passing the data up to libpcap on timeout (Solaris for instance
does). OpenBSD fixed part of the problem (so I shamelessly stole their fix
an put it in FreeBSD) but the fix is flawed in that it only got the first of 
the 8 packets up correctly. I have changed the timeout logic so that it will
flush the buffers on a timeout and tested it on the test code and with tcpdump
(both only once so you've been warned :-)). Now I'll submit this to the 
FreeBSD hackers list and see if anyone sees a problem I haven't considered, 
and then file a PR with both FreeBSD and OpenBSD to see if I can get the fix
committed. To reproduce this you need either tcpreplay (what I used) and a 
small capture file or to do something that creates a small number of packets
(then no more) on a link. Before the patch argus (1.8.1 or 2.0) won't see
any packets. After the patch argus should see the packets (and hopefully
nothing else should break :-)).
	You need to apply these two patches then rebuild and install a new
kernel.

Peter Van Epp / Operations and Technical Support 
Simon Fraser University, Burnaby, B.C. Canada


*** /sys/net/bpfdesc.h.orig	Sat Oct 14 19:16:07 2000
--- /sys/net/bpfdesc.h	Sat Oct 14 19:21:54 2000
***************
*** 69,74 ****
--- 69,75 ----
  
  	struct bpf_if *	bd_bif;		/* interface descriptor */
  	u_long		bd_rtout;	/* Read timeout in 'ticks' */
+         u_long          bd_rdStart;     /* when the read started */
  	struct bpf_insn *bd_filter; 	/* filter code */
  	u_long		bd_rcount;	/* number of packets received */
  	u_long		bd_dcount;	/* number of packets dropped */


*** /sys/net/bpf.c.orig	Sat Oct 14 19:00:59 2000
--- /sys/net/bpf.c	Mon Oct 16 09:30:24 2000
***************
*** 1054,1061 ****
  	if (events & (POLLIN | POLLRDNORM)) {
  		if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
  			revents |= events & (POLLIN | POLLRDNORM);
! 		else
! 			selrecord(p, &d->bd_sel);
  	}
  	splx(s);
  	return (revents);
--- 1054,1076 ----
  	if (events & (POLLIN | POLLRDNORM)) {
  		if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
  			revents |= events & (POLLIN | POLLRDNORM);
! 		else {
!         		/*
!          		 * If there is a timeout and no data in the hold buffer
!        			 * see if there has been data in the capture buffer
! 			 * for more than a timeout interval. If so rotate the
! 			 * buffer to push the packets to the user.
!         		 */
! 			if ((d->bd_slen != 0) && (d->bd_hlen == 0)) {
! 				if ((d->bd_rtout != -1) && 
! 				    (d->bd_rdStart + d->bd_rtout) > ticks) {
! 					ROTATE_BUFFERS(d);
! 					revents |= events & (POLLIN | POLLRDNORM);
! 				}
! 			} else 
! 				selrecord(p, &d->bd_sel);
! 				
! 		}
  	}
  	splx(s);
  	return (revents);
***************
*** 1219,1224 ****
--- 1234,1245 ----
  	 */
  	(*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen));
  	d->bd_slen = curlen + totlen;
+ 
+ 	/*
+ 	 * Mark the time the last packet was seen for poll timeout processing.
+ 	 */
+ 
+ 	d->bd_rdStart = ticks;
  }
  
  /*



More information about the argus mailing list