/* * Ioctl to force rotation of the two buffers, if there's any data available. * This can be used by user space to implement timeouts when waiting for a * buffer to fill. */ int bpf_zerocopy_ioctl_rotzbuf(struct thread *td, struct bpf_d *d, struct bpf_zbuf *bz) { struct zbuf *bzh; bzero(bz, sizeof(*bz)); BPFD_WLOCK(d); if (d->bd_hbuf == NULL && d->bd_slen != 0) { ROTATE_BUFFERS(d); bzh = (struct zbuf *)d->bd_hbuf; bz->bz_bufa = (void *)bzh->zb_uaddr; bz->bz_buflen = d->bd_hlen; } BPFD_WUNLOCK(d); return (0); }
/* * bpfread - read next chunk of packets from buffers */ int bpfread(dev_t dev, struct uio *uio, int ioflag) { struct bpf_d *d; int error; int s; d = bpfilter_lookup(minor(dev)); if (d->bd_bif == 0) return (ENXIO); /* * Restrict application to use a buffer the same size as * as kernel buffers. */ if (uio->uio_resid != d->bd_bufsize) return (EINVAL); s = splnet(); D_GET(d); /* * 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. */ while (d->bd_hbuf == 0) { if (d->bd_bif == NULL) { /* interface is gone */ if (d->bd_slen == 0) { D_PUT(d); splx(s); return (EIO); } ROTATE_BUFFERS(d); break; } if (d->bd_immediate && d->bd_slen != 0) { /* * A packet(s) either arrived since the previous * read or arrived while we were asleep. * Rotate the buffers and return what's here. */ 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) { D_PUT(d); splx(s); return (error); } if (error == EWOULDBLOCK) { /* * On a timeout, return what's in the buffer, * which may be nothing. If there is something * in the store buffer, we can rotate the buffers. */ if (d->bd_hbuf) /* * We filled up the buffer in between * getting the timeout and arriving * here, so we don't need to rotate. */ break; if (d->bd_slen == 0) { D_PUT(d); splx(s); return (0); } ROTATE_BUFFERS(d); break; } } /* * At this point, we know we have something in the hold slot. */ splx(s); /* * Move data from hold buffer into user space. * We know the entire buffer is transferred since * we checked above that the read buffer is bpf_bufsize bytes. */ error = uiomove(d->bd_hbuf, d->bd_hlen, uio); s = splnet(); d->bd_fbuf = d->bd_hbuf; d->bd_hbuf = 0; d->bd_hlen = 0; D_PUT(d); splx(s); return (error); }
/* * Move the packet data from interface memory (pkt) into the * store buffer. Return 1 if it's time to wakeup a listener (buffer full), * otherwise 0. "copy" is the routine called to do the actual data * transfer. bcopy is passed in to copy contiguous chunks, while * bpf_mcopy is passed in to copy mbuf chains. In the latter case, * pkt is really an mbuf. */ void bpf_catchpacket(struct bpf_d *d, u_char *pkt, size_t pktlen, size_t snaplen, void (*cpfn)(const void *, void *, size_t)) { struct bpf_hdr *hp; int totlen, curlen; int hdrlen = d->bd_bif->bif_hdrlen; struct timeval tv; /* * Figure out how many bytes to move. If the packet is * greater or equal to the snapshot length, transfer that * much. Otherwise, transfer the whole packet (unless * we hit the buffer size limit). */ totlen = hdrlen + min(snaplen, pktlen); if (totlen > d->bd_bufsize) totlen = d->bd_bufsize; /* * Round up the end of the previous packet to the next longword. */ curlen = BPF_WORDALIGN(d->bd_slen); if (curlen + totlen > d->bd_bufsize) { /* * This packet will overflow the storage buffer. * Rotate the buffers if we can, then wakeup any * pending reads. */ if (d->bd_fbuf == 0) { /* * We haven't completed the previous read yet, * so drop the packet. */ ++d->bd_dcount; return; } ROTATE_BUFFERS(d); bpf_wakeup(d); curlen = 0; } /* * Append the bpf header. */ hp = (struct bpf_hdr *)(d->bd_sbuf + curlen); microtime(&tv); hp->bh_tstamp.tv_sec = tv.tv_sec; hp->bh_tstamp.tv_usec = tv.tv_usec; hp->bh_datalen = pktlen; hp->bh_hdrlen = hdrlen; /* * Copy the packet data into the store buffer and update its length. */ (*cpfn)(pkt, (u_char *)hp + hdrlen, (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); } 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; } } }
/* * bpfread - read next chunk of packets from buffers */ int bpfread(dev_t dev, struct uio *uio, int ioflag) { struct bpf_d *d; int error; lck_mtx_lock(bpf_mlock); d = bpf_dtab[minor(dev)]; if (d == 0 || d == (void *)1) { lck_mtx_unlock(bpf_mlock); return (ENXIO); } /* * Restrict application to use a buffer the same size as * as kernel buffers. */ if (uio_resid(uio) != d->bd_bufsize) { lck_mtx_unlock(bpf_mlock); return (EINVAL); } /* * 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 && d->bd_slen != 0) { /* * A packet(s) either arrived since the previous * read or arrived while we were asleep. * Rotate the buffers and return what's here. */ 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) { lck_mtx_unlock(bpf_mlock); return (ENXIO); } if (ioflag & IO_NDELAY) { lck_mtx_unlock(bpf_mlock); return (EWOULDBLOCK); } error = BPF_SLEEP(d, PRINET|PCATCH, "bpf", d->bd_rtout); /* * Make sure device is still opened */ d = bpf_dtab[minor(dev)]; if (d == 0 || d == (void *)1) { lck_mtx_unlock(bpf_mlock); return (ENXIO); } if (error == EINTR || error == ERESTART) { lck_mtx_unlock(bpf_mlock); return (error); } if (error == EWOULDBLOCK) { /* * On a timeout, return what's in the buffer, * which may be nothing. If there is something * in the store buffer, we can rotate the buffers. */ if (d->bd_hbuf) /* * We filled up the buffer in between * getting the timeout and arriving * here, so we don't need to rotate. */ break; if (d->bd_slen == 0) { lck_mtx_unlock(bpf_mlock); return (0); } ROTATE_BUFFERS(d); break; } } /* * At this point, we know we have something in the hold slot. */ /* * Move data from hold buffer into user space. * We know the entire buffer is transferred since * we checked above that the read buffer is bpf_bufsize bytes. */ error = UIOMOVE(d->bd_hbuf, d->bd_hlen, UIO_READ, uio); d->bd_fbuf = d->bd_hbuf; d->bd_hbuf = NULL; d->bd_hlen = 0; lck_mtx_unlock(bpf_mlock); return (error); }