diff options
author | rwatson <rwatson@FreeBSD.org> | 2000-03-19 05:42:34 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2000-03-19 05:42:34 +0000 |
commit | 5b7df33ce063503bdebb25d8f4026d194b5d636b (patch) | |
tree | bf2dd5113226273c2841422f0a442c00aa36b9ff /sys/net/bpf.c | |
parent | eab2bd6865d76d43c2ecc87aadaa72eb5e965499 (diff) | |
download | FreeBSD-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.c | 63 |
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 |