#include "use_bpf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <sys/ttycom.h>
#include <sys/filedesc.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/vnode.h>
#include <net/if.h>
#include <net/bpf.h>
#include <net/bpfdesc.h>
#include <net/netmsg2.h>
#include <net/netisr2.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <netproto/802_11/ieee80211_dragonfly.h>
#include <sys/devfs.h>
MALLOC_DEFINE(M_BPF, "BPF", "BPF data");
DEVFS_DEFINE_CLONE_BITMAP(bpf);
#if NBPF <= 1
#define BPF_PREALLOCATED_UNITS 4
#else
#define BPF_PREALLOCATED_UNITS NBPF
#endif
#if NBPF > 0
struct netmsg_bpf_output {
struct netmsg_base base;
struct mbuf *nm_mbuf;
struct ifnet *nm_ifp;
struct sockaddr *nm_dst;
boolean_t nm_feedback;
};
static int bpf_bufsize = BPF_DEFAULTBUFSIZE;
SYSCTL_INT(_debug, OID_AUTO, bpf_bufsize, CTLFLAG_RW,
&bpf_bufsize, 0, "Current size of bpf buffer");
int bpf_maxbufsize = BPF_MAXBUFSIZE;
SYSCTL_INT(_debug, OID_AUTO, bpf_maxbufsize, CTLFLAG_RW,
&bpf_maxbufsize, 0, "Maximum size of bpf buffer");
static struct bpf_if *bpf_iflist;
static struct lwkt_token bpf_token = LWKT_TOKEN_INITIALIZER(bpf_token);
static int bpf_allocbufs(struct bpf_d *);
static void bpf_attachd(struct bpf_d *d, struct bpf_if *bp);
static void bpf_detachd(struct bpf_d *d);
static void bpf_resetd(struct bpf_d *);
static void bpf_freed(struct bpf_d *);
static void bpf_mcopy(volatile const void *, volatile void *, size_t);
static int bpf_movein(struct uio *, int, struct mbuf **,
struct sockaddr *, int *, struct bpf_insn *);
static int bpf_setif(struct bpf_d *, struct ifreq *);
static void bpf_timed_out(void *);
static void bpf_wakeup(struct bpf_d *);
static void catchpacket(struct bpf_d *, u_char *, u_int, u_int,
void (*)(volatile const void *,
volatile void *, size_t),
const struct timeval *);
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 bpf_drvinit(void *unused);
static void bpf_filter_detach(struct knote *kn);
static int bpf_filter_read(struct knote *kn, long hint);
static d_open_t bpfopen;
static d_clone_t bpfclone;
static d_close_t bpfclose;
static d_read_t bpfread;
static d_write_t bpfwrite;
static d_ioctl_t bpfioctl;
static d_kqfilter_t bpfkqfilter;
#define CDEV_MAJOR 23
static struct dev_ops bpf_ops = {
{ "bpf", 0, D_MPSAFE },
.d_open = bpfopen,
.d_close = bpfclose,
.d_read = bpfread,
.d_write = bpfwrite,
.d_ioctl = bpfioctl,
.d_kqfilter = bpfkqfilter
};
static int
bpf_movein(struct uio *uio, int linktype, struct mbuf **mp,
struct sockaddr *sockp, int *datlen, struct bpf_insn *wfilter)
{
const struct ieee80211_bpf_params *p;
struct mbuf *m;
int error;
int len;
int hlen;
int slen;
*datlen = 0;
*mp = NULL;
switch (linktype) {
case DLT_SLIP:
sockp->sa_family = AF_INET;
hlen = 0;
break;
case DLT_EN10MB:
sockp->sa_family = AF_UNSPEC;
hlen = sizeof(struct ether_header);
break;
case DLT_RAW:
case DLT_NULL:
sockp->sa_family = AF_UNSPEC;
hlen = 0;
break;
case DLT_ATM_RFC1483:
sockp->sa_family = AF_UNSPEC;
hlen = 12;
break;
case DLT_PPP:
sockp->sa_family = AF_UNSPEC;
hlen = 4;
break;
case DLT_IEEE802_11:
sockp->sa_family = AF_IEEE80211;
hlen = 0;
break;
case DLT_IEEE802_11_RADIO:
sockp->sa_family = AF_IEEE80211;
sockp->sa_len = 12;
hlen = sizeof(struct ieee80211_bpf_params);
break;
default:
return(EIO);
}
len = uio->uio_resid;
*datlen = len - hlen;
if ((unsigned)len > MCLBYTES)
return(EIO);
m = m_getl(len, M_WAITOK, MT_DATA, M_PKTHDR, NULL);
m->m_pkthdr.len = m->m_len = len;
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;
}
if (hlen != 0) {
if (sockp->sa_family == AF_IEEE80211) {
p = mtod(m, const struct ieee80211_bpf_params *);
hlen = p->ibp_len;
if (hlen > sizeof(sockp->sa_data)) {
error = EINVAL;
goto bad;
}
}
bcopy(m->m_data, sockp->sa_data, hlen);
m->m_pkthdr.len -= hlen;
m->m_len -= hlen;
m->m_data += hlen;
}
return (0);
bad:
m_freem(m);
return(error);
}
static void
bpf_attachd(struct bpf_d *d, struct bpf_if *bp)
{
lwkt_gettoken(&bpf_token);
d->bd_bif = bp;
SLIST_INSERT_HEAD(&bp->bif_dlist, d, bd_next);
*bp->bif_driverp = bp;
EVENTHANDLER_INVOKE(bpf_track, bp->bif_ifp, bp->bif_dlt, 1);
lwkt_reltoken(&bpf_token);
}
static void
bpf_detachd(struct bpf_d *d)
{
int error;
struct bpf_if *bp;
struct ifnet *ifp;
lwkt_gettoken(&bpf_token);
bp = d->bd_bif;
ifp = bp->bif_ifp;
SLIST_REMOVE(&bp->bif_dlist, d, bpf_d, bd_next);
if (SLIST_EMPTY(&bp->bif_dlist)) {
*bp->bif_driverp = NULL;
}
d->bd_bif = NULL;
EVENTHANDLER_INVOKE(bpf_track, ifp, bp->bif_dlt, 0);
if (d->bd_promisc) {
d->bd_promisc = 0;
error = ifpromisc(ifp, 0);
if (error != 0 && error != ENXIO) {
if_printf(ifp, "bpf_detach: ifpromisc failed(%d)\n",
error);
}
}
lwkt_reltoken(&bpf_token);
}
static int
bpfopen(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bpf_d *d;
lwkt_gettoken(&bpf_token);
if (ap->a_cred->cr_prison) {
lwkt_reltoken(&bpf_token);
return(EPERM);
}
d = dev->si_drv1;
if (d != NULL) {
lwkt_reltoken(&bpf_token);
return(EBUSY);
}
d = kmalloc(sizeof *d, M_BPF, M_WAITOK | M_ZERO);
dev->si_drv1 = d;
d->bd_bufsize = bpf_bufsize;
d->bd_sig = SIGIO;
d->bd_seesent = 1;
d->bd_feedback = 0;
callout_init(&d->bd_callout);
lwkt_reltoken(&bpf_token);
return(0);
}
static int
bpfclone(struct dev_clone_args *ap)
{
int unit;
unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(bpf), 0);
ap->a_dev = make_only_dev(&bpf_ops, unit, 0, 0, 0600, "bpf%d", unit);
return 0;
}
static int
bpfclose(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bpf_d *d = dev->si_drv1;
int unit;
lwkt_gettoken(&bpf_token);
funsetown(&d->bd_sigio);
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
d->bd_state = BPF_IDLE;
if (d->bd_bif != NULL)
bpf_detachd(d);
bpf_freed(d);
dev->si_drv1 = NULL;
unit = dev->si_uminor;
if (unit >= BPF_PREALLOCATED_UNITS) {
destroy_dev(dev);
devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(bpf), unit);
}
kfree(d, M_BPF);
lwkt_reltoken(&bpf_token);
return(0);
}
#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 = NULL;
static int
bpfread(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bpf_d *d = dev->si_drv1;
int timed_out;
int error;
lwkt_gettoken(&bpf_token);
if (ap->a_uio->uio_resid != d->bd_bufsize) {
lwkt_reltoken(&bpf_token);
return(EINVAL);
}
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
timed_out = (d->bd_state == BPF_TIMED_OUT);
d->bd_state = BPF_IDLE;
while (d->bd_hbuf == NULL) {
if ((d->bd_immediate || (ap->a_ioflag & IO_NDELAY) || timed_out)
&& d->bd_slen != 0) {
ROTATE_BUFFERS(d);
break;
}
if (d->bd_bif == NULL) {
lwkt_reltoken(&bpf_token);
return(ENXIO);
}
if (ap->a_ioflag & IO_NDELAY) {
lwkt_reltoken(&bpf_token);
return(EWOULDBLOCK);
}
error = tsleep(d, PCATCH, "bpf", d->bd_rtout);
if (error == EINTR || error == ERESTART) {
lwkt_reltoken(&bpf_token);
return(error);
}
if (error == EWOULDBLOCK) {
if (d->bd_hbuf)
break;
if (d->bd_slen == 0) {
lwkt_reltoken(&bpf_token);
return(0);
}
ROTATE_BUFFERS(d);
break;
}
}
error = uiomove(d->bd_hbuf, d->bd_hlen, ap->a_uio);
d->bd_fbuf = d->bd_hbuf;
d->bd_hbuf = NULL;
d->bd_hlen = 0;
lwkt_reltoken(&bpf_token);
return(error);
}
static void
bpf_wakeup(struct bpf_d *d)
{
if (d->bd_state == BPF_WAITING) {
callout_stop(&d->bd_callout);
d->bd_state = BPF_IDLE;
}
wakeup(d);
if (d->bd_async && d->bd_sig && d->bd_sigio)
pgsigio(d->bd_sigio, d->bd_sig, 0);
KNOTE(&d->bd_kq.ki_note, 0);
}
static void
bpf_timed_out(void *arg)
{
struct bpf_d *d = (struct bpf_d *)arg;
if (d->bd_state == BPF_WAITING) {
d->bd_state = BPF_TIMED_OUT;
if (d->bd_slen != 0)
bpf_wakeup(d);
}
}
static void
bpf_output_dispatch(netmsg_t msg)
{
struct netmsg_bpf_output *bmsg = (struct netmsg_bpf_output *)msg;
struct ifnet *ifp = bmsg->nm_ifp;
struct mbuf *mc = NULL;
int error;
if (bmsg->nm_feedback) {
mc = m_dup(bmsg->nm_mbuf, M_NOWAIT);
if (mc != NULL)
mc->m_pkthdr.rcvif = ifp;
}
error = ifp->if_output(ifp, bmsg->nm_mbuf, bmsg->nm_dst, NULL);
lwkt_replymsg(&msg->lmsg, error);
if (mc != NULL) {
if (error == 0) {
mc->m_flags &= ~M_HASH;
(*ifp->if_input)(ifp, mc, NULL, -1);
} else {
m_freem(mc);
}
}
}
static int
bpfwrite(struct dev_write_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bpf_d *d = dev->si_drv1;
struct ifnet *ifp;
struct mbuf *m;
int error, ret;
struct sockaddr dst;
int datlen;
struct netmsg_bpf_output bmsg;
lwkt_gettoken(&bpf_token);
if (d->bd_bif == NULL) {
lwkt_reltoken(&bpf_token);
return(ENXIO);
}
ifp = d->bd_bif->bif_ifp;
if (ap->a_uio->uio_resid == 0) {
lwkt_reltoken(&bpf_token);
return(0);
}
error = bpf_movein(ap->a_uio, (int)d->bd_bif->bif_dlt, &m,
&dst, &datlen, d->bd_wfilter);
if (error) {
lwkt_reltoken(&bpf_token);
return(error);
}
if (datlen > ifp->if_mtu) {
m_freem(m);
lwkt_reltoken(&bpf_token);
return(EMSGSIZE);
}
if (d->bd_hdrcmplt)
dst.sa_family = pseudo_AF_HDRCMPLT;
netmsg_init(&bmsg.base, NULL, &curthread->td_msgport,
0, bpf_output_dispatch);
bmsg.nm_mbuf = m;
bmsg.nm_ifp = ifp;
bmsg.nm_dst = &dst;
if (d->bd_feedback)
bmsg.nm_feedback = TRUE;
else
bmsg.nm_feedback = FALSE;
ret = lwkt_domsg(netisr_cpuport(0), &bmsg.base.lmsg, 0);
lwkt_reltoken(&bpf_token);
return ret;
}
static void
bpf_resetd(struct bpf_d *d)
{
if (d->bd_hbuf) {
d->bd_fbuf = d->bd_hbuf;
d->bd_hbuf = NULL;
}
d->bd_slen = 0;
d->bd_hlen = 0;
d->bd_rcount = 0;
d->bd_dcount = 0;
}
static int
bpfioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bpf_d *d = dev->si_drv1;
int error = 0;
lwkt_gettoken(&bpf_token);
if (d->bd_state == BPF_WAITING)
callout_stop(&d->bd_callout);
d->bd_state = BPF_IDLE;
if (d->bd_locked == 1) {
switch (ap->a_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:
lwkt_reltoken(&bpf_token);
return (EPERM);
}
}
switch (ap->a_cmd) {
default:
error = EINVAL;
break;
case FIONREAD:
{
int n;
n = d->bd_slen;
if (d->bd_hbuf)
n += d->bd_hlen;
*(int *)ap->a_data = n;
break;
}
case SIOCGIFADDR:
{
struct ifnet *ifp;
if (d->bd_bif == NULL) {
error = EINVAL;
} else {
ifp = d->bd_bif->bif_ifp;
ifnet_serialize_all(ifp);
error = ifp->if_ioctl(ifp, ap->a_cmd,
ap->a_data, ap->a_cred);
ifnet_deserialize_all(ifp);
}
break;
}
case BIOCGBLEN:
*(u_int *)ap->a_data = d->bd_bufsize;
break;
case BIOCSBLEN:
if (d->bd_bif != NULL) {
error = EINVAL;
} else {
u_int size = *(u_int *)ap->a_data;
if (size > bpf_maxbufsize)
*(u_int *)ap->a_data = size = bpf_maxbufsize;
else if (size < BPF_MINBUFSIZE)
*(u_int *)ap->a_data = size = BPF_MINBUFSIZE;
d->bd_bufsize = size;
}
break;
case BIOCSETF:
case BIOCSETWF:
error = bpf_setf(d, (struct bpf_program *)ap->a_data,
ap->a_cmd);
break;
case BIOCFLUSH:
bpf_resetd(d);
break;
case BIOCPROMISC:
if (d->bd_bif == NULL) {
error = EINVAL;
break;
}
if (d->bd_promisc == 0) {
error = ifpromisc(d->bd_bif->bif_ifp, 1);
if (error == 0)
d->bd_promisc = 1;
}
break;
case BIOCGDLT:
if (d->bd_bif == NULL)
error = EINVAL;
else
*(u_int *)ap->a_data = d->bd_bif->bif_dlt;
break;
case BIOCGDLTLIST:
if (d->bd_bif == NULL) {
error = EINVAL;
} else {
error = bpf_getdltlist(d,
(struct bpf_dltlist *)ap->a_data);
}
break;
case BIOCSDLT:
if (d->bd_bif == NULL)
error = EINVAL;
else
error = bpf_setdlt(d, *(u_int *)ap->a_data);
break;
case BIOCGETIF:
if (d->bd_bif == NULL) {
error = EINVAL;
} else {
struct ifnet *const ifp = d->bd_bif->bif_ifp;
struct ifreq *const ifr = (struct ifreq *)ap->a_data;
strlcpy(ifr->ifr_name, ifp->if_xname,
sizeof ifr->ifr_name);
}
break;
case BIOCSETIF:
error = bpf_setif(d, (struct ifreq *)ap->a_data);
break;
case BIOCSRTIMEOUT:
{
struct timeval *tv = (struct timeval *)ap->a_data;
if ((error = itimerfix(tv)) == 0)
d->bd_rtout = tvtohz_low(tv);
break;
}
case BIOCGRTIMEOUT:
{
struct timeval *tv = (struct timeval *)ap->a_data;
tv->tv_sec = d->bd_rtout / hz;
tv->tv_usec = (d->bd_rtout % hz) * ustick;
break;
}
case BIOCGSTATS:
{
struct bpf_stat *bs = (struct bpf_stat *)ap->a_data;
bs->bs_recv = d->bd_rcount;
bs->bs_drop = d->bd_dcount;
break;
}
case BIOCIMMEDIATE:
d->bd_immediate = *(u_int *)ap->a_data;
break;
case BIOCVERSION:
{
struct bpf_version *bv = (struct bpf_version *)ap->a_data;
bv->bv_major = BPF_MAJOR_VERSION;
bv->bv_minor = BPF_MINOR_VERSION;
break;
}
case BIOCGHDRCMPLT:
*(u_int *)ap->a_data = d->bd_hdrcmplt;
break;
case BIOCSHDRCMPLT:
d->bd_hdrcmplt = *(u_int *)ap->a_data ? 1 : 0;
break;
case BIOCGSEESENT:
*(u_int *)ap->a_data = d->bd_seesent;
break;
case BIOCSSEESENT:
d->bd_seesent = *(u_int *)ap->a_data;
break;
case FIOASYNC:
d->bd_async = *(int *)ap->a_data;
break;
case BIOCSFEEDBACK:
d->bd_feedback = *(int *)ap->a_data;
break;
case BIOCGFEEDBACK:
*(u_int *)ap->a_data = d->bd_feedback;
break;
case FIOSETOWN:
error = fsetown(*(int *)ap->a_data, &d->bd_sigio);
break;
case FIOGETOWN:
*(int *)ap->a_data = fgetown(&d->bd_sigio);
break;
case TIOCSPGRP:
error = fsetown(-(*(int *)ap->a_data), &d->bd_sigio);
break;
case TIOCGPGRP:
*(int *)ap->a_data = -fgetown(&d->bd_sigio);
break;
case BIOCSRSIG:
{
u_int sig;
sig = *(u_int *)ap->a_data;
if (sig >= NSIG)
error = EINVAL;
else
d->bd_sig = sig;
break;
}
case BIOCGRSIG:
*(u_int *)ap->a_data = d->bd_sig;
break;
case BIOCLOCK:
d->bd_locked = 1;
break;
}
lwkt_reltoken(&bpf_token);
return(error);
}
static int
bpf_setf(struct bpf_d *d, struct bpf_program *fp, u_long cmd)
{
struct bpf_insn *fcode, *old;
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);
if (wfilter)
d->bd_wfilter = NULL;
else
d->bd_rfilter = NULL;
bpf_resetd(d);
if (old != NULL)
kfree(old, M_BPF);
return(0);
}
flen = fp->bf_len;
if (flen > BPF_MAXINSNS)
return(EINVAL);
size = flen * sizeof *fp->bf_insns;
fcode = (struct bpf_insn *)kmalloc(size, M_BPF, M_WAITOK);
if (copyin(fp->bf_insns, fcode, size) == 0 &&
bpf_validate(fcode, (int)flen)) {
if (wfilter)
d->bd_wfilter = fcode;
else
d->bd_rfilter = fcode;
bpf_resetd(d);
if (old != NULL)
kfree(old, M_BPF);
return(0);
}
kfree(fcode, M_BPF);
return(EINVAL);
}
static int
bpf_setif(struct bpf_d *d, struct ifreq *ifr)
{
struct bpf_if *bp;
int error;
struct ifnet *theywant;
ifnet_lock();
theywant = ifunit(ifr->ifr_name);
if (theywant == NULL) {
ifnet_unlock();
return(ENXIO);
}
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
struct ifnet *ifp = bp->bif_ifp;
if (ifp == NULL || ifp != theywant)
continue;
if (bp->bif_driverp != &ifp->if_bpf)
continue;
if (d->bd_sbuf == NULL) {
error = bpf_allocbufs(d);
if (error != 0) {
ifnet_unlock();
return(error);
}
}
if (bp != d->bd_bif) {
if (d->bd_bif != NULL) {
bpf_detachd(d);
}
bpf_attachd(d, bp);
}
bpf_resetd(d);
ifnet_unlock();
return(0);
}
ifnet_unlock();
return(ENXIO);
}
static struct filterops bpf_read_filtops =
{ FILTEROP_ISFD, NULL, bpf_filter_detach, bpf_filter_read };
static int
bpfkqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct klist *klist;
struct bpf_d *d;
lwkt_gettoken(&bpf_token);
d = dev->si_drv1;
if (d->bd_bif == NULL) {
ap->a_result = 1;
lwkt_reltoken(&bpf_token);
return (0);
}
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &bpf_read_filtops;
kn->kn_hook = (caddr_t)d;
break;
default:
ap->a_result = EOPNOTSUPP;
lwkt_reltoken(&bpf_token);
return (0);
}
klist = &d->bd_kq.ki_note;
knote_insert(klist, kn);
lwkt_reltoken(&bpf_token);
return (0);
}
static void
bpf_filter_detach(struct knote *kn)
{
struct klist *klist;
struct bpf_d *d;
d = (struct bpf_d *)kn->kn_hook;
klist = &d->bd_kq.ki_note;
knote_remove(klist, kn);
}
static int
bpf_filter_read(struct knote *kn, long hint)
{
struct bpf_d *d;
int ready = 0;
d = (struct bpf_d *)kn->kn_hook;
if (d->bd_hlen != 0 ||
((d->bd_immediate || d->bd_state == BPF_TIMED_OUT) &&
d->bd_slen != 0)) {
ready = 1;
} else {
if (d->bd_rtout > 0 && d->bd_state == BPF_IDLE) {
callout_reset(&d->bd_callout, d->bd_rtout,
bpf_timed_out, d);
d->bd_state = BPF_WAITING;
}
}
return (ready);
}
void
bpf_tap(struct bpf_if *bp, u_char *pkt, u_int pktlen)
{
struct bpf_d *d;
struct timeval tv;
int gottime = 0;
u_int slen;
lwkt_gettoken(&bpf_token);
if (bp == NULL) {
lwkt_reltoken(&bpf_token);
return;
}
SLIST_FOREACH(d, &bp->bif_dlist, bd_next) {
++d->bd_rcount;
slen = bpf_filter(d->bd_rfilter, pkt, pktlen, pktlen);
if (slen != 0) {
if (!gottime) {
microtime(&tv);
gottime = 1;
}
catchpacket(d, pkt, pktlen, slen, _bcopy, &tv);
}
}
lwkt_reltoken(&bpf_token);
}
static void
bpf_mcopy(volatile const void *src_arg, volatile void *dst_arg, size_t len)
{
volatile const struct mbuf *m;
u_int count;
volatile u_char *dst;
m = src_arg;
dst = dst_arg;
while (len > 0) {
if (m == NULL)
panic("bpf_mcopy");
count = min(m->m_len, len);
bcopy(mtod(m, void *), dst, count);
m = m->m_next;
dst += count;
len -= count;
}
}
void
bpf_mtap(struct bpf_if *bp, struct mbuf *m)
{
struct bpf_d *d;
u_int pktlen, slen;
struct timeval tv;
int gottime = 0;
lwkt_gettoken(&bpf_token);
if (bp == NULL) {
lwkt_reltoken(&bpf_token);
return;
}
if (SLIST_EMPTY(&bp->bif_dlist)) {
lwkt_reltoken(&bpf_token);
return;
}
pktlen = m_lengthm(m, NULL);
SLIST_FOREACH(d, &bp->bif_dlist, bd_next) {
if (!d->bd_seesent && (m->m_pkthdr.rcvif == NULL))
continue;
++d->bd_rcount;
slen = bpf_filter(d->bd_rfilter, (u_char *)m, pktlen, 0);
if (slen != 0) {
if (!gottime) {
microtime(&tv);
gottime = 1;
}
catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcopy,
&tv);
}
}
lwkt_reltoken(&bpf_token);
}
void
bpf_mtap_hdr(struct bpf_if *arg, caddr_t data, u_int dlen, struct mbuf *m,
u_int direction)
{
struct m_hdr mh;
mh.mh_flags = 0;
mh.mh_next = m;
mh.mh_len = dlen;
mh.mh_data = data;
bpf_mtap(arg, (struct mbuf *) &mh);
}
void
bpf_mtap_family(struct bpf_if *bp, struct mbuf *m, sa_family_t family)
{
u_int family4;
KKASSERT(family != AF_UNSPEC);
family4 = (u_int)family;
bpf_ptap(bp, m, &family4, sizeof(family4));
}
void
bpf_ptap(struct bpf_if *bp, struct mbuf *m, const void *data, u_int dlen)
{
struct mbuf mb;
mb.m_next = m;
mb.m_data = __DECONST(void *, data);
mb.m_len = dlen;
mb.m_pkthdr.rcvif = m->m_pkthdr.rcvif;
bpf_mtap(bp, &mb);
}
static void
catchpacket(struct bpf_d *d, u_char *pkt, u_int pktlen, u_int snaplen,
void (*cpfn)(volatile const void *, volatile void *, size_t),
const struct timeval *tv)
{
struct bpf_hdr *hp;
int totlen, curlen;
int hdrlen = d->bd_bif->bif_hdrlen;
int wakeup = 0;
totlen = hdrlen + min(snaplen, pktlen);
if (totlen > d->bd_bufsize)
totlen = d->bd_bufsize;
curlen = BPF_WORDALIGN(d->bd_slen);
if (curlen + totlen > d->bd_bufsize) {
if (d->bd_fbuf == NULL) {
++d->bd_dcount;
return;
}
ROTATE_BUFFERS(d);
wakeup = 1;
curlen = 0;
} else if (d->bd_immediate || d->bd_state == BPF_TIMED_OUT) {
wakeup = 1;
}
hp = (struct bpf_hdr *)(d->bd_sbuf + curlen);
hp->bh_tstamp = *tv;
hp->bh_datalen = pktlen;
hp->bh_hdrlen = hdrlen;
(*cpfn)(pkt, (u_char *)hp + hdrlen, (hp->bh_caplen = totlen - hdrlen));
d->bd_slen = curlen + totlen;
if (wakeup)
bpf_wakeup(d);
}
static int
bpf_allocbufs(struct bpf_d *d)
{
d->bd_fbuf = kmalloc(d->bd_bufsize, M_BPF, M_WAITOK);
d->bd_sbuf = kmalloc(d->bd_bufsize, M_BPF, M_WAITOK);
d->bd_slen = 0;
d->bd_hlen = 0;
return(0);
}
static void
bpf_freed(struct bpf_d *d)
{
if (d->bd_sbuf != NULL) {
kfree(d->bd_sbuf, M_BPF);
if (d->bd_hbuf != NULL)
kfree(d->bd_hbuf, M_BPF);
if (d->bd_fbuf != NULL)
kfree(d->bd_fbuf, M_BPF);
}
if (d->bd_rfilter)
kfree(d->bd_rfilter, M_BPF);
if (d->bd_wfilter)
kfree(d->bd_wfilter, M_BPF);
}
void
bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen)
{
bpfattach_dlt(ifp, dlt, hdrlen, &ifp->if_bpf);
}
void
bpfattach_dlt(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp)
{
struct bpf_if *bp;
bp = kmalloc(sizeof *bp, M_BPF, M_WAITOK | M_ZERO);
lwkt_gettoken(&bpf_token);
SLIST_INIT(&bp->bif_dlist);
bp->bif_ifp = ifp;
bp->bif_dlt = dlt;
bp->bif_driverp = driverp;
*bp->bif_driverp = NULL;
bp->bif_next = bpf_iflist;
bpf_iflist = bp;
bp->bif_hdrlen = BPF_WORDALIGN(hdrlen + SIZEOF_BPF_HDR) - hdrlen;
lwkt_reltoken(&bpf_token);
if (bootverbose)
if_printf(ifp, "bpf attached\n");
}
void
bpfdetach(struct ifnet *ifp)
{
struct bpf_if *bp, *bp_prev;
struct bpf_d *d;
lwkt_gettoken(&bpf_token);
bp_prev = NULL;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (ifp == bp->bif_ifp)
break;
bp_prev = bp;
}
if (bp->bif_ifp == NULL) {
lwkt_reltoken(&bpf_token);
kprintf("bpfdetach: %s was not attached\n", ifp->if_xname);
return;
}
while ((d = SLIST_FIRST(&bp->bif_dlist)) != NULL) {
bpf_detachd(d);
bpf_wakeup(d);
}
if (bp_prev != NULL)
bp_prev->bif_next = bp->bif_next;
else
bpf_iflist = bp->bif_next;
kfree(bp, M_BPF);
lwkt_reltoken(&bpf_token);
}
static int
bpf_getdltlist(struct bpf_d *d, struct bpf_dltlist *bfl)
{
int n, error;
struct ifnet *ifp;
struct bpf_if *bp;
ifp = d->bd_bif->bif_ifp;
n = 0;
error = 0;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (bp->bif_ifp != ifp)
continue;
if (bfl->bfl_list != NULL) {
if (n >= bfl->bfl_len) {
return (ENOMEM);
}
error = copyout(&bp->bif_dlt,
bfl->bfl_list + n, sizeof(u_int));
}
n++;
}
bfl->bfl_len = n;
return(error);
}
static int
bpf_setdlt(struct bpf_d *d, u_int dlt)
{
int error, opromisc;
struct ifnet *ifp;
struct bpf_if *bp;
if (d->bd_bif->bif_dlt == dlt)
return (0);
ifp = d->bd_bif->bif_ifp;
for (bp = bpf_iflist; bp != NULL; bp = bp->bif_next) {
if (bp->bif_ifp == ifp && bp->bif_dlt == dlt)
break;
}
if (bp != NULL) {
opromisc = d->bd_promisc;
bpf_detachd(d);
bpf_attachd(d, bp);
bpf_resetd(d);
if (opromisc) {
error = ifpromisc(bp->bif_ifp, 1);
if (error) {
if_printf(bp->bif_ifp,
"bpf_setdlt: ifpromisc failed (%d)\n",
error);
} else {
d->bd_promisc = 1;
}
}
}
return(bp == NULL ? EINVAL : 0);
}
void
bpf_gettoken(void)
{
lwkt_gettoken(&bpf_token);
}
void
bpf_reltoken(void)
{
lwkt_reltoken(&bpf_token);
}
static void
bpf_drvinit(void *unused)
{
int i;
make_autoclone_dev(&bpf_ops, &DEVFS_CLONE_BITMAP(bpf),
bpfclone, 0, 0, 0600, "bpf");
for (i = 0; i < BPF_PREALLOCATED_UNITS; i++) {
make_dev(&bpf_ops, i, 0, 0, 0600, "bpf%d", i);
devfs_clone_bitmap_set(&DEVFS_CLONE_BITMAP(bpf), i);
}
}
static void
bpf_drvuninit(void *unused)
{
devfs_clone_handler_del("bpf");
dev_ops_remove_all(&bpf_ops);
devfs_clone_bitmap_uninit(&DEVFS_CLONE_BITMAP(bpf));
}
SYSINIT(bpfdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR, bpf_drvinit, NULL);
SYSUNINIT(bpfdev, SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,bpf_drvuninit, NULL);
#else
void
bpf_tap(struct bpf_if *bp, u_char *pkt, u_int pktlen)
{
}
void
bpf_mtap(struct bpf_if *bp, struct mbuf *m)
{
}
void
bpf_ptap(struct bpf_if *bp, struct mbuf *m, const void *data, u_int dlen)
{
}
void
bpfattach(struct ifnet *ifp, u_int dlt, u_int hdrlen)
{
}
void
bpfattach_dlt(struct ifnet *ifp, u_int dlt, u_int hdrlen, struct bpf_if **driverp)
{
}
void
bpfdetach(struct ifnet *ifp)
{
}
u_int
bpf_filter(const struct bpf_insn *pc, u_char *p, u_int wirelen, u_int buflen)
{
return -1;
}
void
bpf_gettoken(void)
{
}
void
bpf_reltoken(void)
{
}
#endif