[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

kernel/1459: bpf select timeout incorrect




>Number:         1459
>Category:       kernel
>Synopsis:       bpf select timeout incorrect
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Oct 17 15:00:02 MDT 2000
>Last-Modified:
>Originator:     Peter Van Epp
>Organization:
Simon Fraser University
net
>Release:        2.7
>Environment:
	
	System      : OpenBSD 2.7
	Architecture: OpenBSD.i386
	Machine     : i386
>Description:

	/sys/net/bpf.c when accessed via libpcap with the select system call
	with timeout does not flush the buffers (if they aren't full) when the
	timeout occurs. There is code there that is supposed to do this (or
	something like it) but it doesn't work in this particular case. This
	doesn't affect tcpdump because it appears to be running in immediate
	mode (which is probably performance limiting but does work).


>How-To-Repeat:

To reproduce:

1) Install argus-1.8.1.tar.gz and argus-1.8.1.patches from 

ftp.andrew.cmu.edu/pub/argus	

2) Install the test.dif patch below to report Total packets received from 
   libpcap to show the problem.

3) start argus_bpf as below and supply it a less than buffer full of packets
   (I used tcpreplay from www.anzen.com/research/nidsbench on another machine
    playing back a tcpdump file).

4) Examine output as below (unpatched then patched).

Unpatched bpf.c (8 packets are still in bpf.c receive buffer only one gets 
 up to libpcap)):

bin/argus_bpf -ixl0 -P0 -w argus.log
Total packets: 1
^C
9 packets recv'd by filter
0 packets dropped by kernel
demoa# bin/ra -r argus.log -n
Tue 10/17 01:55:47      man     1.168.192.0             255.255.255.0       INT
Tue 10/17 01:55:51      tcp  130.71.240.184.2197   o>    142.58.12.12.80    TIM
Tue 10/17 01:55:54      man  pkts        9  drops     0   flows        1       CLO

bpf.c patched as below and kernel recompiled and installed:

demoa# bin/argus_bpf -ixl0 -P0 -w argus.log 
Total packets: 1
Total packets: 2
Total packets: 3
Total packets: 4
Total packets: 5
Total packets: 6
Total packets: 7
Total packets: 8
Total packets: 9
^C
9 packets recv'd by filter
0 packets dropped by kernel
demoa# bin/ra -r argus.log -n
Tue 10/17 01:59:52      man     1.168.192.0             255.255.255.0       INT
Tue 10/17 01:59:55  M   tcp  130.71.240.184.2197   |>    142.58.12.12.80    RST
Tue 10/17 02:00:00      man  pkts        9  drops     0   flows        1       CLO
demoa# 

Apply in argus-1.8.1/ directory.

*** server/cons_ether.c.orig	Tue Oct 17 01:45:10 2000
--- server/cons_ether.c	Tue Oct 17 01:45:59 2000
***************
*** 95,100 ****
--- 95,101 ----
  
     if (p && caplen) {
        totalPktsRcv++;
+       printf("Total packets: %d\n", totalPktsRcv);
  
        globaltvp.tv_sec  = h->ts.tv_sec;
        globaltvp.tv_usec = h->ts.tv_usec;

	
>Fix:

	This may or may not break what the original code was supposed to fix
	(which may be a different set of circumstances) but as noted if makes
	OpenBSD react the same as Solaris on the same input.

*** /sys/net/bpf.c.orig	Tue Oct 17 00:58:22 2000
--- /sys/net/bpf.c	Tue Oct 17 01:56:41 2000
***************
*** 414,428 ****
  	s = splimp();
  
  	/*
- 	 * bd_rdStart is tagged when we start the read, iff there's a timeout.
- 	 * we can then figure out when we're done reading.
- 	 */
- 	if (d->bd_rtout != -1 && d->bd_rdStart == 0)
- 		d->bd_rdStart = ticks;
- 	else
- 		d->bd_rdStart = 0;
- 
- 	/*
  	 * If the hold buffer is empty, then do a timed sleep, which
  	 * ends when the timeout expires or when enough packets
  	 * have arrived to fill the store buffer.
--- 414,419 ----
***************
*** 437,452 ****
  			ROTATE_BUFFERS(d);
  			break;
  		}
! 		if ((d->bd_rtout != -1) || (d->bd_rdStart + d->bd_rtout) < ticks) {
  			error = tsleep((caddr_t)d, PRINET|PCATCH, "bpf",
  			    d->bd_rtout);
! 		} else {
! 			if (d->bd_rtout == -1) {
! 				/* User requested non-blocking I/O */
! 				error = EWOULDBLOCK;
! 			} else
! 				error = 0;
! 		}
  		if (error == EINTR || error == ERESTART) {
  			splx(s);
  			return (error);
--- 428,440 ----
  			ROTATE_BUFFERS(d);
  			break;
  		}
! 		if (d->bd_rtout == -1) 
! 			/* User requested non-blocking I/O */
! 			error = EWOULDBLOCK;
! 		else
  			error = tsleep((caddr_t)d, PRINET|PCATCH, "bpf",
  			    d->bd_rtout);
! 
  		if (error == EINTR || error == ERESTART) {
  			splx(s);
  			return (error);
***************
*** 973,985 ****
  	register dev_t dev;
  	int rw;
  {
- 	/*
- 	 * if there isn't data waiting, and there's a timeout,
- 	 * mark the time we started waiting.
- 	 */
- 	if (b->db_rtout != -1 && (d->bd_rdStart == 0))
- 		d->bd_rdStart = ticks;
- 			    
  	return (bpf_select(dev, rw, u.u_procp));
  }
  #endif
--- 961,966 ----
***************
*** 1015,1026 ****
  		return (1);
  	}
  
! 	/*
! 	 * if there isn't data waiting, and there's a timeout,
! 	 * mark the time we started waiting.
  	 */
! 	if (d->bd_rtout != -1 && d->bd_rdStart == 0)
! 		d->bd_rdStart = ticks;
  			    
  #if BSD >= 199103
  	selrecord(p, &d->bd_sel);
--- 996,1017 ----
  		return (1);
  	}
  
!         /*
! 	 * 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);
! 			splx(s);
! 			return (1);
! 		}
! 	}
  			    
  #if BSD >= 199103
  	selrecord(p, &d->bd_sel);
***************
*** 1202,1220 ****
  	(*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen));
  	d->bd_slen = curlen + totlen;
  
! 	if (d->bd_rdStart && (d->bd_rtout + d->bd_rdStart < ticks)) {
! 		/*
! 		 * we could be selecting on the bpf, and we
! 		 * may have timeouts set.  We got here by getting
! 		 * a packet, so wake up the reader.
! 		 */
! 		if (d->bd_fbuf) {
! 			d->bd_rdStart = 0;
! 			ROTATE_BUFFERS(d);
! 			bpf_wakeup(d);
! 			curlen = 0;
! 		}
! 	}
  }
  
  /*
--- 1193,1204 ----
  	(*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;
! 
  }
  
  /*

>Audit-Trail:
>Unformatted: