Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 6 Nov 2003 00:38:48 -0800
From:      Guy Harris <guy@alum.mit.edu>
To:        Bruce Evans <bde@zeta.org.au>
Cc:        fenner@freebsd.org
Subject:   Re: bpf/pcap are weird
Message-ID:  <20031106003848.F331@quadrajet.sonic.net>

next in thread | raw e-mail | index | archive | help
> bpfpoll() is reported to be broken; see PR 36219.

Yes, that's the PR that indicated that "select()"/"poll()" don't, in
fact, work correctly with timeouts.

That was fixed in 4.5...

> Rev.1.113 of bpf.c may have disturbed this.

...but might have been re-broken.

> It removed the comment which said that
> bpf_ready() doesn't acually imitate resultof(FIONREAD) != 0.

...and it also removed the check for "d->bd_state == BPF_TIMED_OUT" that
made "select()"/"poll()" work with timeouts.

And, unfortunately, it looks as if that's in 4.9 - revision 1.59.2.13
has it, and that's tagged with RELENG_4_9_0_RELEASE.

If non-blocking reads *do* cause buffer rotation if necessary, this can,
at least for "select()"/"poll()"-based applications that can control the
timeout in their select loop, be worked around by making the timeout
timer be a timeout in "select()" or "poll()", putting the BPF device in
non-blocking mode (for which there is an API in recent versions of
libpcap, and in older versions you can just set the flag on the
descriptor you get from "pcap_fileno()"), and reading from the BPF
device (or the pcap_t) either if "select()"/"poll()" says you can *or*
the "select()"/"poll()" timer has expired. 

If they don't cause a buffer rotation, however, you're screwed unless
there's some other way to force the buffer rotation.

Turning BIOCIMMEDIATE mode on would take care of that - but it would
also turn off packet buffering, which might cause too much CPU overhead
when capturing on a busy network.  (Or it might not; I don't know
whether anybody's measured it.)

However, in 4.9, a buffer rotation will be done in "bpf_read()" if a
timeout has occurred, so it looks as if that workaround would work.

In 4.x releases from 4.5 through 4.8, "select()"/"poll()" should, I
thihnk, work with timeouts, and reads after a timeout has occurred
should rotate the buffers even if the timeout occurred before the
"read()" (such as in a "select()"/"poll()").

In releases prior to 4.4, "select()"/"poll()" won't work with timeouts -
but non-blocking reads will force a buffer rotation; if the hold buffer
is empty, and IO_NDELAY is set in "ioflag", "error" will be set to
EWOULDBLOCK, and it'll eventually fall through to the "rotate buffers if
EWOULDBLOCK is true, the hold buffer is empty, and the store buffer
isn't" code.

In 4.4, "bpf_read()" was changed (revision 1.59.2.5, MFC of 1.72) so
that if the hold buffer is empty, and IO_NDELAY is set in "ioflag",
it'll return EWOULDBLOCK.  I.e., it was changed so that non-blocking
reads *won't* force a buffer rotation; the comment for 1.72 is

	Fix bug: a read() on a bpf device which was in non-blocking mode
	and had no data available returned 0.  Now it returns -1 with
	errno set to EWOULDBLOCK (== EAGAIN) as it should.  This fix
	makes the bpf device usable in threaded programs.

(It didn't make it usable, as far as I know, because the
"select()"/"poll()" behavior also had to be fixed.)

I don't know whether there's a PR for that bug or not.  I have vague
memories of seeing the problem reported, but it might've been in one of
the mailing lists.  I don't remember what the symptoms were, but it
*might* mean that making non-blocking reads always rotate the buffers
might cause a regression.

Unfortunately, having non-blocking reads not force a buffer rotation
means that the workaround for the BPF "select()"/"poll()" will, I think,
not work - and it's not obvious how to make it work.

(This means that if I make Ethereal register the pcap_t descriptor in
the GTK+ main loop, and have the main loop wait both for UI events and
packet arrival, that probably won't work on FreeBSD 4.4....)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20031106003848.F331>