diff options
| author | darrenr <darrenr@NetBSD.org> | 2004-05-29 08:56:19 +0000 |
|---|---|---|
| committer | darrenr <darrenr@NetBSD.org> | 2004-05-29 08:56:19 +0000 |
| commit | 15f2ef8f2c351918f4e0a07e02fb09d9fbfaafe3 (patch) | |
| tree | d7b1dc6846e342eab2d156b42b302b09d9036158 /sys/net | |
| parent | fd8537e01d373589456402dc83be6d1295e3d25c (diff) | |
add mmap(2) interface to bpf(4) devices, along with BIOCMMAPINFO ioctl call
for applications to interact with the bpf device for the purpose of using
mmap to examinen captured data.
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/bpf.c | 215 | ||||
| -rw-r--r-- | sys/net/bpf.h | 17 | ||||
| -rw-r--r-- | sys/net/bpfdesc.h | 4 |
3 files changed, 188 insertions, 48 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 6e6ee9ec881..58af7281a73 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -1,4 +1,4 @@ -/* $NetBSD: bpf.c,v 1.98 2004/05/25 04:33:59 atatat Exp $ */ +/* $NetBSD: bpf.c,v 1.99 2004/05/29 08:56:19 darrenr Exp $ */ /* * Copyright (c) 1990, 1991, 1993 @@ -39,7 +39,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.98 2004/05/25 04:33:59 atatat Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bpf.c,v 1.99 2004/05/29 08:56:19 darrenr Exp $"); #include "bpfilter.h" @@ -122,6 +122,8 @@ static void catchpacket __P((struct bpf_d *, u_char *, u_int, u_int, static void reset_d __P((struct bpf_d *)); static int bpf_getdltlist __P((struct bpf_d *, struct bpf_dltlist *)); static int bpf_setdlt __P((struct bpf_d *, u_int)); +static int bpf_mmapinfo __P((struct bpf_d *, struct bpf_mmapinfo *)); +static int bpf_waitfordata __P((struct bpf_d *)); dev_type_open(bpfopen); dev_type_close(bpfclose); @@ -130,10 +132,11 @@ dev_type_write(bpfwrite); dev_type_ioctl(bpfioctl); dev_type_poll(bpfpoll); dev_type_kqfilter(bpfkqfilter); +dev_type_mmap(bpfmmap); const struct cdevsw bpf_cdevsw = { bpfopen, bpfclose, bpfread, bpfwrite, bpfioctl, - nostop, notty, bpfpoll, nommap, bpfkqfilter, + nostop, notty, bpfpoll, bpfmmap, bpfkqfilter, }; static int @@ -420,11 +423,14 @@ bpfclose(dev, flag, mode, p) * Zero the length of the new store buffer. */ #define ROTATE_BUFFERS(d) \ - (d)->bd_hbuf = (d)->bd_sbuf; \ - (d)->bd_hlen = (d)->bd_slen; \ - (d)->bd_sbuf = (d)->bd_fbuf; \ - (d)->bd_slen = 0; \ - (d)->bd_fbuf = 0; + do { \ + (d)->bd_hbuf = (d)->bd_sbuf; \ + (d)->bd_hlen = (d)->bd_slen; \ + (d)->bd_sbuf = (d)->bd_fbuf; \ + (d)->bd_slen = 0; \ + (d)->bd_fbuf = 0; \ + } while (0) + /* * bpfread - read next chunk of packets from buffers */ @@ -451,6 +457,43 @@ bpfread(dev, uio, ioflag) callout_stop(&d->bd_callout); timed_out = (d->bd_state == BPF_TIMED_OUT); d->bd_state = BPF_IDLE; + error = bpf_waitfordata(d); + if (error != 0) + goto done; + /* + * 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; +done: + splx(s); + if (error == -1) + error = 0; + return (error); +} + + +/* + * NOTE: splnet() is assumed to be held when calling this function. + * It is left to the caller to drop the spl. + */ +static int +bpf_waitfordata(d) + struct bpf_d *d; +{ + int error; + /* * If the hold buffer is empty, then do a timed sleep, which * ends when the timeout expires or when enough packets @@ -459,7 +502,6 @@ bpfread(dev, uio, ioflag) while (d->bd_hbuf == 0) { if (ioflag & IO_NDELAY) { if (d->bd_slen == 0) { - splx(s); return (EWOULDBLOCK); } ROTATE_BUFFERS(d); @@ -495,35 +537,19 @@ bpfread(dev, uio, ioflag) */ break; - if (d->bd_slen == 0) { - splx(s); - return (0); + if (d->bd_slen == 0) + return -1; + + if (d->bd_mapbuf == -1) { + ROTATE_BUFFERS(d); + break; } - ROTATE_BUFFERS(d); - break; } if (error != 0) - goto done; + return error; } - /* - * 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; -done: - splx(s); - return (error); + return 0; } @@ -910,6 +936,10 @@ bpfioctl(dev, cmd, addr, flag, p) d->bd_seesent = *(u_int *)addr; break; + case BIOCMMAPINFO: + error = bpf_mmapinfo(d, (struct bpf_mmapinfo *)addr); + break; + case FIONBIO: /* Non-blocking I/O */ /* * No need to do anything special as we use IO_NDELAY in @@ -1366,7 +1396,7 @@ catchpacket(d, pkt, pktlen, snaplen, cpfn) * Rotate the buffers if we can, then wakeup any * pending reads. */ - if (d->bd_fbuf == 0) { + if (d->bd_fbuf == 0 || d->bd_mapbuf != -1) { /* * We haven't completed the previous read yet, * so drop the packet. @@ -1413,16 +1443,22 @@ bpf_allocbufs(d) struct bpf_d *d; { - d->bd_fbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT); - if (!d->bd_fbuf) + d->bd_bufs[1] = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT); + if (d->bd_bufs[1] == NULL) return (ENOBUFS); - d->bd_sbuf = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT); - if (!d->bd_sbuf) { - free(d->bd_fbuf, M_DEVBUF); + d->bd_fbuf = d->bd_bufs[1]; + d->bd_bufs[0] = (caddr_t)malloc(d->bd_bufsize, M_DEVBUF, M_NOWAIT); + if (d->bd_bufs[0] == NULL) { + free(d->bd_bufs[1], M_DEVBUF); + d->bd_bufs[1] = NULL; + d->bd_fbuf = NULL; return (ENOBUFS); } + d->bd_sbuf = d->bd_bufs[0]; + d->bd_fbuf = d->bd_bufs[1]; d->bd_slen = 0; d->bd_hlen = 0; + d->bd_mapbuf = -1; return (0); } @@ -1439,13 +1475,10 @@ bpf_freed(d) * been detached from its interface and it yet hasn't been marked * free. */ - if (d->bd_sbuf != 0) { - free(d->bd_sbuf, M_DEVBUF); - if (d->bd_hbuf != 0) - free(d->bd_hbuf, M_DEVBUF); - if (d->bd_fbuf != 0) - free(d->bd_fbuf, M_DEVBUF); - } + if (d->bd_bufs[0] != 0) + free(d->bd_bufs[0], M_DEVBUF); + if (d->bd_bufs[1] != 0) + free(d->bd_bufs[1], M_DEVBUF); if (d->bd_filter) free((caddr_t)d->bd_filter, M_DEVBUF); @@ -1625,6 +1658,94 @@ bpf_setdlt(d, dlt) } if (bp == NULL) return EINVAL; + +/* + * Provide a mmap(2) interface to the BPF buffers. + * Read-only mapping (PROT_READ) is enforced by this driver - an application + * using this should never write to this buffer and especially not with copy + * on write as the real buffer contents will then disappear from the process + * view. + * + * An application should create two maps: one for each buffer that bpf has + * internally and use the information returned from BIOCMMAPINFO to determine + * which one has valid data in it and how much data is valid. + */ +paddr_t +bpfmmap(dev_t dev, off_t off, int prot) +{ + struct bpf_d *d; + u_int uoff; + + if (prot != VM_PROT_READ) + return -1; + + if (off & PAGE_MASK) + panic("bpfmmap"); + + d = &bpf_dtab[minor(dev)]; + uoff = (u_int)off; + + if (uoff >= 0 && uoff < d->bd_bufsize) + return (atop(d->bd_bufs[0] + uoff)); + + if (uoff >= d->bd_bufsize && uoff < (d->bd_bufsize * 2)) + return (atop(d->bd_bufs[1] + (uoff - d->bd_bufsize))); + + /* Page not found. */ + return (-1); +} + +static int +bpf_mmapinfo(d, info) + struct bpf_d *d; + struct bpf_mmapinfo *info; +{ + int which, s, error; + + s = splnet(); + + if (info->bpm_op == BPM_RELEASE) { /* only want to unlock */ + d->bd_mapbuf = -1; + splx(s); + return 0; + } + + /* + * Currently only two operations are supported, release and acquire. + * If it's not one of these then return an error. + */ + if (info->bpm_op != BPM_ACQUIRE) { + splx(s); + return EINVAL; + } + + /* + * An incoming call must give up the current buffer locked for use + * with mmap, if it has one, so that bpf has somewhere to write new + * data when this call returns. + */ + if (d->bd_mapbuf != -1) { + d->bd_fbuf = d->bd_hbuf; + d->bd_hbuf = NULL; + d->bd_hlen = 0; + d->bd_mapbuf = -1; + } + + error = bpf_waitfordata(d); + if (error == 0) { + if (d->bd_hbuf == d->bd_bufs[0]) + which = 0; + else if (d->bd_hbuf == d->bd_bufs[1]) + which = 1; + else + which = -1; + d->bd_mapbuf = which; + info->bpm_len = d->bd_hlen; + info->bpm_which = which; + } + splx(s); + return 0; +} s = splnet(); opromisc = d->bd_promisc; bpf_detachd(d); @@ -1688,3 +1809,5 @@ SYSCTL_SETUP(sysctl_net_bfp_setup, "sysctl net.bpf subtree setup") sysctl_net_bpf_maxbufsize, 0, &bpf_maxbufsize, 0, CTL_NET, node->sysctl_num, CTL_CREATE, CTL_EOL); } + + diff --git a/sys/net/bpf.h b/sys/net/bpf.h index 022ed249234..1503e9387d1 100644 --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -1,4 +1,4 @@ -/* $NetBSD: bpf.h,v 1.37 2004/04/30 22:07:21 dyoung Exp $ */ +/* $NetBSD: bpf.h,v 1.38 2004/05/29 08:56:19 darrenr Exp $ */ /* * Copyright (c) 1990, 1991, 1993 @@ -134,6 +134,7 @@ struct bpf_version { #define BIOCGDLTLIST _IOWR('B',119, struct bpf_dltlist) #define BIOCGSEESENT _IOR('B',120, u_int) #define BIOCSSEESENT _IOW('B',121, u_int) +#define BIOCMMAPINFO _IOWR('B',122, struct bpf_mmapinfo) /* * Structure prepended to each packet. @@ -246,6 +247,20 @@ struct bpf_dltlist { u_int *bfl_list; /* array of DLTs */ }; +/* + * Structure to pass information for use with mmap'd regions + */ +typedef enum bpf_mmapops { + BPM_RELEASE = 0, + BPM_ACQUIRE +} bpf_mmapops_t; + +struct bpf_mmapinfo { + bpf_mmapops_t bpm_op; /* operation to perform */ + int bpm_which; /* which buffer is ready */ + int bpm_len; /* # of bytes ready to process */ +}; + #ifdef _KERNEL int bpf_validate __P((struct bpf_insn *, int)); void bpf_tap __P((caddr_t, u_char *, u_int)); diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h index 71b49434e62..976ec6e007f 100644 --- a/sys/net/bpfdesc.h +++ b/sys/net/bpfdesc.h @@ -1,4 +1,4 @@ -/* $NetBSD: bpfdesc.h,v 1.18 2004/04/15 14:56:57 darrenr Exp $ */ +/* $NetBSD: bpfdesc.h,v 1.19 2004/05/29 08:56:19 darrenr Exp $ */ /* * Copyright (c) 1990, 1991, 1993 @@ -61,8 +61,10 @@ struct bpf_d { caddr_t bd_sbuf; /* store slot */ caddr_t bd_hbuf; /* hold slot */ caddr_t bd_fbuf; /* free slot */ + caddr_t bd_bufs[2]; int bd_slen; /* current length of store buffer */ int bd_hlen; /* current length of hold buffer */ + int bd_mapbuf; /* mmap locked buffer */ int bd_bufsize; /* absolute length of buffers */ |
