Fix bpf so that select will return for a timeout (from FreeBSD.)

Fix the behaviour of BIOCIMMEDIATE (fix from LBL BPF code via FreeBSD.)

In bpf_mtap(), optimise the calling of bpf_filter() and catchpacket()
based on whether or not the entire packet is in one mbuf (based on
similar change FreeBSD but fixes BIOC*SEESENT issue with that.)

Copy the implementation of BIOCSSEESENT, BIOCGSEESENT by FreeBSD.

Review Assistance: Guy Harris

PRs: kern/8674, kern/12170
This commit is contained in:
darrenr 2004-04-10 23:31:51 +00:00
parent 0488cca3a2
commit eee3190c06
3 changed files with 111 additions and 37 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpf.c,v 1.90 2004/03/24 15:34:54 atatat Exp $ */
/* $NetBSD: bpf.c,v 1.91 2004/04/10 23:31:51 darrenr Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@ -39,7 +39,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.90 2004/03/24 15:34:54 atatat Exp $");
__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.91 2004/04/10 23:31:51 darrenr Exp $");
#include "bpfilter.h"
@ -114,6 +114,7 @@ static int bpf_movein __P((struct uio *, int, int,
static void bpf_attachd __P((struct bpf_d *, struct bpf_if *));
static void bpf_detachd __P((struct bpf_d *));
static int bpf_setif __P((struct bpf_d *, struct ifreq *));
static void bpf_timed_out __P((void *));
static __inline void
bpf_wakeup __P((struct bpf_d *));
static void catchpacket __P((struct bpf_d *, u_char *, u_int, u_int,
@ -380,6 +381,8 @@ bpfopen(dev, flag, mode, p)
/* Mark "free" and do most initialization. */
memset((char *)d, 0, sizeof(*d));
d->bd_bufsize = bpf_bufsize;
d->bd_seesent = 1;
callout_init(&d->bd_callout);
return (0);
}
@ -400,6 +403,9 @@ bpfclose(dev, flag, mode, p)
int s;
s = splnet();
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
d->bd_state = BPF_IDLE;
if (d->bd_bif)
bpf_detachd(d);
splx(s);
@ -429,6 +435,7 @@ bpfread(dev, uio, ioflag)
int ioflag;
{
struct bpf_d *d = &bpf_dtab[minor(dev)];
int timed_out;
int error;
int s;
@ -440,17 +447,17 @@ bpfread(dev, uio, ioflag)
return (EINVAL);
s = splnet();
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
timed_out = (d->bd_state == BPF_TIMED_OUT);
d->bd_state = BPF_IDLE;
/*
* 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.
*/
while (d->bd_hbuf == 0) {
if (d->bd_immediate) {
if (d->bd_slen == 0) {
splx(s);
return (EWOULDBLOCK);
}
if ((d->bd_immediate || timed_out) && d->bd_slen != 0) {
/*
* A packet(s) either arrived since the previous
* read or arrived while we were asleep.
@ -463,11 +470,8 @@ bpfread(dev, uio, ioflag)
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;
/* User requested non-blocking I/O */
error = EWOULDBLOCK;
}
if (error == EINTR || error == ERESTART) {
splx(s);
@ -535,6 +539,24 @@ bpf_wakeup(d)
d->bd_sel.sel_pid = 0;
}
static void
bpf_timed_out(arg)
void *arg;
{
struct bpf_d *d = (struct bpf_d *)arg;
int s;
s = splnet();
if (d->bd_state == BPF_WAITING) {
d->bd_state = BPF_TIMED_OUT;
if (d->bd_slen != 0)
bpf_wakeup(d);
}
splx(s);
}
int
bpfwrite(dev, uio, ioflag)
dev_t dev;
@ -631,6 +653,12 @@ bpfioctl(dev, cmd, addr, flag, p)
struct bpf_insn **p;
#endif
s = splnet();
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
d->bd_state = BPF_IDLE;
splx(s);
switch (cmd) {
default:
@ -853,6 +881,20 @@ bpfioctl(dev, cmd, addr, flag, p)
d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0;
break;
/*
* Get "see sent packets" flag
*/
case BIOCGSEESENT:
*(u_int *)addr = d->bd_seesent;
break;
/*
* Set "see sent" packets flag
*/
case BIOCSSEESENT:
d->bd_seesent = *(u_int *)addr;
break;
case FIONBIO: /* Non-blocking I/O */
if (*(int *)addr)
d->bd_rtout = -1;
@ -1040,10 +1082,19 @@ bpfpoll(dev, events, p)
/*
* An imitation of the FIONREAD ioctl code.
*/
if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
if (d->bd_hlen != 0 ||
((d->bd_immediate || d->bd_state == BPF_TIMED_OUT) &&
d->bd_slen != 0))
revents |= events & (POLLIN | POLLRDNORM);
else
else {
selrecord(p, &d->bd_sel);
/* Start the read timeout if necessary */
if (d->bd_rtout > 0 && d->bd_state == BPF_IDLE) {
callout_reset(&d->bd_callout, d->bd_rtout,
bpf_timed_out, d);
d->bd_state = BPF_WAITING;
}
}
}
splx(s);
@ -1168,20 +1219,34 @@ bpf_mtap(arg, m)
caddr_t arg;
struct mbuf *m;
{
void *(*cpfn) __P((void *, const void *, size_t));
struct bpf_if *bp = (struct bpf_if *)arg;
struct bpf_d *d;
u_int pktlen, slen;
u_int pktlen, slen, buflen;
struct mbuf *m0;
void *marg;
pktlen = 0;
for (m0 = m; m0 != 0; m0 = m0->m_next)
pktlen += m0->m_len;
if (pktlen == m->m_len) {
cpfn = memcpy;
marg = mtod(m, void *);
buflen = pktlen;
} else {
cpfn = bpf_mcpy;
marg = m;
buflen = 0;
}
for (d = bp->bif_dlist; d != 0; d = d->bd_next) {
if (!d->bd_seesent && (m->m_pkthdr.rcvif == NULL))
continue;
++d->bd_rcount;
slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0);
slen = bpf_filter(d->bd_filter, marg, pktlen, buflen);
if (slen != 0)
catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcpy);
catchpacket(d, marg, pktlen, slen, cpfn);
}
}
@ -1234,7 +1299,13 @@ catchpacket(d, pkt, pktlen, snaplen, cpfn)
ROTATE_BUFFERS(d);
bpf_wakeup(d);
curlen = 0;
}
} else if (d->bd_immediate || d->bd_state == BPF_TIMED_OUT)
/*
* Immediate mode is set, or the read timeout has
* already expired during a select call. A packet
* arrived, so the reader should be woken up.
*/
bpf_wakeup(d);
/*
* Append the bpf header.
@ -1248,14 +1319,6 @@ catchpacket(d, pkt, pktlen, snaplen, cpfn)
*/
(*cpfn)((u_char *)hp + hdrlen, pkt, (hp->bh_caplen = totlen - hdrlen));
d->bd_slen = curlen + totlen;
if (d->bd_immediate) {
/*
* Immediate mode is set. A packet arrived so any
* reads should be woken up.
*/
bpf_wakeup(d);
}
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpf.h,v 1.33 2004/01/22 00:32:41 jonathan Exp $ */
/* $NetBSD: bpf.h,v 1.34 2004/04/10 23:31:52 darrenr Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@ -102,12 +102,12 @@ struct bpf_version {
* header files. If your using gcc, we assume that you
* have run fixincludes so the latter set should work.
*/
#define BIOCGBLEN _IOR('B',102, u_int)
#define BIOCSBLEN _IOWR('B',102, u_int)
#define BIOCSETF _IOW('B',103, struct bpf_program)
#define BIOCFLUSH _IO('B',104)
#define BIOCGBLEN _IOR('B',102, u_int)
#define BIOCSBLEN _IOWR('B',102, u_int)
#define BIOCSETF _IOW('B',103, struct bpf_program)
#define BIOCFLUSH _IO('B',104)
#define BIOCPROMISC _IO('B',105)
#define BIOCGDLT _IOR('B',106, u_int)
#define BIOCGDLT _IOR('B',106, u_int)
#define BIOCGETIF _IOR('B',107, struct ifreq)
#define BIOCSETIF _IOW('B',108, struct ifreq)
#define BIOCSRTIMEOUT _IOW('B',109, struct timeval)
@ -117,10 +117,12 @@ struct bpf_version {
#define BIOCVERSION _IOR('B',113, struct bpf_version)
#define BIOCSTCPF _IOW('B',114, struct bpf_program)
#define BIOCSUDPF _IOW('B',115, struct bpf_program)
#define BIOCGHDRCMPLT _IOR('B',116, u_int)
#define BIOCSHDRCMPLT _IOW('B',117, u_int)
#define BIOCSDLT _IOW('B',118, u_int)
#define BIOCGDLTLIST _IOWR('B',119, struct bpf_dltlist)
#define BIOCGHDRCMPLT _IOR('B',116, u_int)
#define BIOCSHDRCMPLT _IOW('B',117, u_int)
#define BIOCSDLT _IOW('B',118, u_int)
#define BIOCGDLTLIST _IOWR('B',119, struct bpf_dltlist)
#define BIOCGSEESENT _IOR('B',120, u_int)
#define BIOCSSEESENT _IOW('B',121, u_int)
/*
* Structure prepended to each packet.

View File

@ -1,4 +1,4 @@
/* $NetBSD: bpfdesc.h,v 1.16 2003/08/07 16:32:48 agc Exp $ */
/* $NetBSD: bpfdesc.h,v 1.17 2004/04/10 23:31:52 darrenr Exp $ */
/*
* Copyright (c) 1990, 1991, 1993
@ -41,6 +41,7 @@
#ifndef _NET_BPFDESC_H_
#define _NET_BPFDESC_H_
#include <sys/callout.h>
#include <sys/select.h>
/*
@ -75,6 +76,7 @@ struct bpf_d {
u_char bd_state; /* idle, waiting, or timed out */
u_char bd_immediate; /* true to return on packet arrival */
int bd_hdrcmplt; /* false to fill in src lladdr */
int bd_seesent; /* true if bpf should see sent packets */
int bd_async; /* non-zero if packet reception should generate signal */
pid_t bd_pgid; /* process or group id for signal */
#if BSD < 199103
@ -85,8 +87,15 @@ struct bpf_d {
u_char bd_pad; /* explicit alignment */
struct selinfo bd_sel; /* bsd select info */
#endif
struct callout bd_callout; /* for BPF timeouts with select */
};
/* Values for bd_state */
#define BPF_IDLE 0 /* no select in progress */
#define BPF_WAITING 1 /* waiting for read timeout in select */
#define BPF_TIMED_OUT 2 /* read timeout has expired in select */
/*
* Descriptor associated with each attached hardware interface.
*/