[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: