summaryrefslogtreecommitdiffstats
path: root/sys/net/bpf.c
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2000-03-19 05:42:34 +0000
committerrwatson <rwatson@FreeBSD.org>2000-03-19 05:42:34 +0000
commit5b7df33ce063503bdebb25d8f4026d194b5d636b (patch)
treebf2dd5113226273c2841422f0a442c00aa36b9ff /sys/net/bpf.c
parenteab2bd6865d76d43c2ecc87aadaa72eb5e965499 (diff)
downloadFreeBSD-src-5b7df33ce063503bdebb25d8f4026d194b5d636b.zip
FreeBSD-src-5b7df33ce063503bdebb25d8f4026d194b5d636b.tar.gz
The advent of if_detach, allowing interface removal at runtime, makes it
possible for a panic to occur if BPF is in use on the interface at the time of the call to if_detach. This happens because BPF maintains pointers to the struct ifnet describing the interface, which is freed by if_detach. To correct this problem, a new call, bpfdetach, is introduced. bpfdetach locates BPF descriptor references to the interface, and NULLs them. Other BPF code is modified so that discovery of a NULL interface results in ENXIO (already implemented for some calls). Processes blocked on a BPF call will also be woken up so that they can receive ENXIO. Interface drivers that invoke bpfattach and if_detach must be modified to also call bpfattach(ifp) before calling if_detach(ifp). This is relevant for buses that support hot removal, such as pccard and usb. Patches to all effected devices will not be committed, only to if_wi.c, due to testing limitations. To reproduce the crash, load up tcpdump on you favorite pccard ethernet card, and then eject the card. As some pccard drivers do not invoke if_detach(ifp), this bug will not manifest itself for those drivers. Reviewed by: wes
Diffstat (limited to 'sys/net/bpf.c')
-rw-r--r--sys/net/bpf.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c
index 466227c..9d562e4 100644
--- a/sys/net/bpf.c
+++ b/sys/net/bpf.c
@@ -477,6 +477,18 @@ bpfread(dev, uio, ioflag)
ROTATE_BUFFERS(d);
break;
}
+
+ /*
+ * No data is available, check to see if the bpf device
+ * is still pointed at a real interface. If not, return
+ * ENXIO so that the userland process knows to rebind
+ * it before using it again.
+ */
+ if (d->bd_bif == NULL) {
+ splx(s);
+ return (ENXIO);
+ }
+
if (ioflag & IO_NDELAY)
error = EWOULDBLOCK;
else
@@ -1035,6 +1047,9 @@ bpfpoll(dev, events, p)
*/
d = dev->si_drv1;
+ if (d->bd_bif == NULL)
+ return (ENXIO);
+
s = splimp();
if (events & (POLLIN | POLLRDNORM)) {
if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
@@ -1287,6 +1302,54 @@ bpfattach(ifp, dlt, hdrlen)
printf("bpf: %s%d attached\n", ifp->if_name, ifp->if_unit);
}
+/*
+ * Detach bpf from an interface. This involves detaching each descriptor
+ * associated with the interface, and leaving bd_bif NULL. Notify each
+ * descriptor as it's detached so that any sleepers wake up and get
+ * ENXIO.
+ */
+void
+bpfdetach(ifp)
+ struct ifnet *ifp;
+{
+ struct bpf_if *bp, *bp_prev;
+ struct bpf_d *d;
+ int s;
+
+ s = splimp();
+
+ /* Locate BPF interface information */
+ bp_prev = NULL;
+ for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
+ if (ifp == bp->bif_ifp)
+ break;
+ bp_prev = bp;
+ }
+
+ /* Interface wasn't attached */
+ if (bp->bif_ifp == NULL) {
+ splx(s);
+ printf("bpfdetach: %s%d was not attached\n", ifp->if_name,
+ ifp->if_unit);
+ return;
+ }
+
+ while ((d = bp->bif_dlist) != NULL) {
+ bpf_detachd(d);
+ bpf_wakeup(d);
+ }
+
+ if (bp_prev) {
+ bp_prev->bif_next = bp->bif_next;
+ } else {
+ bpf_iflist = bp->bif_next;
+ }
+
+ free(bp, M_BPF);
+
+ splx(s);
+}
+
static void bpf_drvinit __P((void *unused));
static void
OpenPOWER on IntegriCloud