diff options
-rw-r--r-- | sys/net/bpf.c | 104 | ||||
-rw-r--r-- | sys/net/bpf.h | 2 | ||||
-rw-r--r-- | sys/net/bpfdesc.h | 5 |
3 files changed, 87 insertions, 24 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c index cfeb1d2..7747b3e 100644 --- a/sys/net/bpf.c +++ b/sys/net/bpf.c @@ -94,7 +94,7 @@ static void bpf_detachd(struct bpf_d *d); static void bpf_freed(struct bpf_d *); static void bpf_mcopy(const void *, void *, size_t); static int bpf_movein(struct uio *, int, int, - struct mbuf **, struct sockaddr *); + struct mbuf **, struct sockaddr *, struct bpf_insn *); static int bpf_setif(struct bpf_d *, struct ifreq *); static void bpf_timed_out(void *); static __inline void @@ -102,7 +102,7 @@ static __inline void static void catchpacket(struct bpf_d *, u_char *, u_int, u_int, void (*)(const void *, void *, size_t)); static void reset_d(struct bpf_d *); -static int bpf_setf(struct bpf_d *, struct bpf_program *); +static int bpf_setf(struct bpf_d *, struct bpf_program *, u_long cmd); static int bpf_getdltlist(struct bpf_d *, struct bpf_dltlist *); static int bpf_setdlt(struct bpf_d *, u_int); static void filt_bpfdetach(struct knote *); @@ -152,17 +152,19 @@ static struct filterops bpfread_filtops = { 1, NULL, filt_bpfdetach, filt_bpfread }; static int -bpf_movein(uio, linktype, mtu, mp, sockp) +bpf_movein(uio, linktype, mtu, mp, sockp, wfilter) struct uio *uio; int linktype; int mtu; struct mbuf **mp; struct sockaddr *sockp; + struct bpf_insn *wfilter; { struct mbuf *m; int error; int len; int hlen; + int slen; /* * Build a sockaddr based on the data link layer type. @@ -243,10 +245,26 @@ bpf_movein(uio, linktype, mtu, mp, sockp) m->m_pkthdr.rcvif = NULL; *mp = m; + if (m->m_len < hlen) { + error = EPERM; + goto bad; + } + + error = uiomove(mtod(m, u_char *), len, uio); + if (error) + goto bad; + + slen = bpf_filter(wfilter, mtod(m, u_char *), len, len); + if (slen == 0) { + error = EPERM; + goto bad; + } + /* - * Make room for link header. + * Make room for link header, and copy it to sockaddr */ if (hlen != 0) { + bcopy(m->m_data, sockp->sa_data, hlen); m->m_pkthdr.len -= hlen; m->m_len -= hlen; #if BSD >= 199103 @@ -254,13 +272,9 @@ bpf_movein(uio, linktype, mtu, mp, sockp) #else m->m_off += hlen; #endif - error = uiomove(sockp->sa_data, hlen, uio); - if (error) - goto bad; } - error = uiomove(mtod(m, void *), len - hlen, uio); - if (!error) - return (0); + + return (0); bad: m_freem(m); return (error); @@ -602,7 +616,8 @@ bpfwrite(dev, uio, ioflag) return (0); bzero(&dst, sizeof(dst)); - error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, ifp->if_mtu, &m, &dst); + error = bpf_movein(uio, (int)d->bd_bif->bif_dlt, ifp->if_mtu, + &m, &dst, d->bd_wfilter); if (error) return (error); @@ -650,6 +665,7 @@ reset_d(d) * SIOCGIFADDR Get interface address - convenient hook to driver. * BIOCGBLEN Get buffer len [for read()]. * BIOCSETF Set ethernet read filter. + * BIOCSETWF Set ethernet write filter. * BIOCFLUSH Flush read packet buffer. * BIOCPROMISC Put interface into promiscuous mode. * BIOCGDLT Get link layer type. @@ -664,6 +680,7 @@ reset_d(d) * BIOCSHDRCMPLT Set "header already complete" flag * BIOCGSEESENT Get "see packets sent" flag * BIOCSSEESENT Set "see packets sent" flag + * BIOCLOCK Set "locked" flag */ /* ARGSUSED */ static int @@ -683,6 +700,28 @@ bpfioctl(dev, cmd, addr, flags, td) d->bd_state = BPF_IDLE; BPFD_UNLOCK(d); + if (d->bd_locked == 1) { + switch (cmd) { + case BIOCGBLEN: + case BIOCFLUSH: + case BIOCGDLT: + case BIOCGDLTLIST: + case BIOCGETIF: + case BIOCGRTIMEOUT: + case BIOCGSTATS: + case BIOCVERSION: + case BIOCGRSIG: + case BIOCGHDRCMPLT: + case FIONREAD: + case BIOCLOCK: + case BIOCSRTIMEOUT: + case BIOCIMMEDIATE: + case TIOCGPGRP: + break; + default: + return (EPERM); + } + } switch (cmd) { default: @@ -747,7 +786,8 @@ bpfioctl(dev, cmd, addr, flags, td) * Set link layer read filter. */ case BIOCSETF: - error = bpf_setf(d, (struct bpf_program *)addr); + case BIOCSETWF: + error = bpf_setf(d, (struct bpf_program *)addr, cmd); break; /* @@ -894,6 +934,9 @@ bpfioctl(dev, cmd, addr, flags, td) *(u_int *)addr = d->bd_hdrcmplt; break; + case BIOCLOCK: + d->bd_locked = 1; + break; /* * Set "header already complete" flag */ @@ -964,19 +1007,29 @@ bpfioctl(dev, cmd, addr, flags, td) * free it and replace it. Returns EINVAL for bogus requests. */ static int -bpf_setf(d, fp) +bpf_setf(d, fp, cmd) struct bpf_d *d; struct bpf_program *fp; + u_long cmd; { struct bpf_insn *fcode, *old; - u_int flen, size; + u_int wfilter, flen, size; + if (cmd == BIOCSETWF) { + old = d->bd_wfilter; + wfilter = 1; + } else { + wfilter = 0; + old = d->bd_rfilter; + } if (fp->bf_insns == NULL) { if (fp->bf_len != 0) return (EINVAL); BPFD_LOCK(d); - old = d->bd_filter; - d->bd_filter = NULL; + if (wfilter) + d->bd_wfilter = NULL; + else + d->bd_rfilter = NULL; reset_d(d); BPFD_UNLOCK(d); if (old != NULL) @@ -992,8 +1045,10 @@ bpf_setf(d, fp) if (copyin((caddr_t)fp->bf_insns, (caddr_t)fcode, size) == 0 && bpf_validate(fcode, (int)flen)) { BPFD_LOCK(d); - old = d->bd_filter; - d->bd_filter = fcode; + if (wfilter) + d->bd_wfilter = fcode; + else + d->bd_rfilter = fcode; reset_d(d); BPFD_UNLOCK(d); if (old != NULL) @@ -1185,7 +1240,7 @@ bpf_tap(bp, pkt, pktlen) LIST_FOREACH(d, &bp->bif_dlist, bd_next) { BPFD_LOCK(d); ++d->bd_rcount; - slen = bpf_filter(d->bd_filter, pkt, pktlen, pktlen); + slen = bpf_filter(d->bd_rfilter, pkt, pktlen, pktlen); if (slen != 0) { d->bd_fcount++; #ifdef MAC @@ -1255,7 +1310,7 @@ bpf_mtap(bp, m) continue; BPFD_LOCK(d); ++d->bd_rcount; - slen = bpf_filter(d->bd_filter, (u_char *)m, pktlen, 0); + slen = bpf_filter(d->bd_rfilter, (u_char *)m, pktlen, 0); if (slen != 0) { d->bd_fcount++; #ifdef MAC @@ -1308,7 +1363,7 @@ bpf_mtap2(bp, data, dlen, m) continue; BPFD_LOCK(d); ++d->bd_rcount; - slen = bpf_filter(d->bd_filter, (u_char *)&mb, pktlen, 0); + slen = bpf_filter(d->bd_rfilter, (u_char *)&mb, pktlen, 0); if (slen != 0) { d->bd_fcount++; #ifdef MAC @@ -1440,8 +1495,10 @@ bpf_freed(d) if (d->bd_fbuf != NULL) free(d->bd_fbuf, M_BPF); } - if (d->bd_filter) - free((caddr_t)d->bd_filter, M_BPF); + if (d->bd_rfilter) + free((caddr_t)d->bd_rfilter, M_BPF); + if (d->bd_wfilter) + free((caddr_t)d->bd_wfilter, M_BPF); mtx_destroy(&d->bd_mtx); } @@ -1668,6 +1725,7 @@ bpfstats_fill_xbpf(struct xbpf_d *d, struct bpf_d *bd) strlcpy(d->bd_ifname, bd->bd_bif->bif_ifp->if_xname, IFNAMSIZ); strlcpy(d->bd_pcomm, bd->bd_pcomm, MAXCOMLEN); + d->bd_locked = bd->bd_locked; } static int diff --git a/sys/net/bpf.h b/sys/net/bpf.h index 7759b16..189b30f 100644 --- a/sys/net/bpf.h +++ b/sys/net/bpf.h @@ -113,6 +113,8 @@ struct bpf_version { #define BIOCSSEESENT _IOW('B',119, u_int) #define BIOCSDLT _IOW('B',120, u_int) #define BIOCGDLTLIST _IOWR('B',121, struct bpf_dltlist) +#define BIOCLOCK _IO('B', 122) +#define BIOCSETWF _IOW('B',123, struct bpf_program) /* * Structure prepended to each packet. diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h index 63f8987..39a1340 100644 --- a/sys/net/bpfdesc.h +++ b/sys/net/bpfdesc.h @@ -69,7 +69,8 @@ struct bpf_d { struct bpf_if * bd_bif; /* interface descriptor */ u_long bd_rtout; /* Read timeout in 'ticks' */ - struct bpf_insn *bd_filter; /* filter code */ + struct bpf_insn *bd_rfilter; /* read filter code */ + struct bpf_insn *bd_wfilter; /* write filter code */ u_long bd_rcount; /* number of packets received */ u_long bd_dcount; /* number of packets dropped */ @@ -95,6 +96,7 @@ struct bpf_d { u_long bd_fcount; /* number of packets which matched filter */ pid_t bd_pid; /* PID which created descriptor */ char bd_pcomm[MAXCOMLEN + 1]; + int bd_locked; /* true if descriptor is locked */ }; /* Values for bd_state */ @@ -147,6 +149,7 @@ struct xbpf_d { pid_t bd_pid; char bd_ifname[IFNAMSIZ]; char bd_pcomm[MAXCOMLEN + 1]; + int bd_locked; }; #define BPFIF_LOCK(bif) mtx_lock(&(bif)->bif_mtx) |