bpf_filter is hard to read.
the difficulty is that it looks like you give it packets in vanilla
memory buffers (ie, a pointer and a length) to read out of, but
packets in the kernel are in mbufs. so if you pass a buffer with a
zero length, the bpf filter code when built in the kernel magically
figures out to do mbuf operations instead.
i was going to change it to get rid of the buffer mode of operation
and explicitely use operations on mbufs all the time, but it turns
out bpf_filter is built as part of libpcap, and libpcap only knows
about vanilla buffers.
so i ended up with this.
this moves the guts of bpf_filter into a new _bpf_filter function
that takes an opaque void pointer to a "thing that has packet data
in it", and a set of function pointers that can do reads against
that opaque thing.
bpf_filter.c includes an implementation of these reads for buffers,
which provides the bpf_filter api that libpcap expects.
bpf.c provides an implementation of these reads for mbufs, and a
bpf_mfilter function that you can call to use it.
thoughts?
tests would be appreciated.
Index: sys/net/bpf.c
===================================================================
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.136
diff -u -p -r1.136 bpf.c
--- sys/net/bpf.c 29 Mar 2016 10:38:27 -0000 1.136
+++ sys/net/bpf.c 30 Mar 2016 05:13:44 -0000
@@ -1160,7 +1152,7 @@ bpf_tap(caddr_t arg, u_char *pkt, u_int
bf = srp_enter(&d->bd_rfilter);
if (bf != NULL)
fcode = bf->bf_insns;
- slen = bpf_filter(fcode, pkt, pktlen, 0);
+ slen = bpf_filter(fcode, pkt, pktlen, pktlen);
srp_leave(&d->bd_rfilter, bf);
}
@@ -1254,7 +1244,7 @@ _bpf_mtap(caddr_t arg, struct mbuf *m, u
bf = srp_enter(&d->bd_rfilter);
if (bf != NULL)
fcode = bf->bf_insns;
- slen = bpf_filter(fcode, (u_char *)m, pktlen, 0);
+ slen = bpf_mfilter(fcode, m, pktlen);
srp_leave(&d->bd_rfilter, bf);
}
@@ -1748,4 +1738,104 @@ bpf_insn_dtor(void *null, void *f)
free(insns, M_DEVBUF, bf->bf_len * sizeof(*insns));
free(bf, M_DEVBUF, sizeof(*bf));
+}
+
+u_int32_t bpf_mbuf_ldw(const void *, u_int32_t, int *);
+u_int32_t bpf_mbuf_ldh(const void *, u_int32_t, int *);
+u_int32_t bpf_mbuf_ldb(const void *, u_int32_t, int *);
+
+int bpf_mbuf_copy(const struct mbuf *, u_int32_t,
+ void *, u_int32_t);
+
+const struct bpf_ops bpf_mbuf_ops = {
+ bpf_mbuf_ldw,
+ bpf_mbuf_ldh,
+ bpf_mbuf_ldb,
+};
+
+int
+bpf_mbuf_copy(const struct mbuf *m, u_int32_t off, void *buf, u_int32_t len)
+{
+ u_int8_t *cp = buf;
+ u_int32_t count;
+
+ while (off >= m->m_len) {
+ off -= m->m_len;
+
+ m = m->m_next;
+ if (m == NULL)
+ return (-1);
+ }
+
+ for (;;) {
+ count = min(m->m_len - off, len);
+
+ memcpy(cp, m->m_data + off, count);
+ len -= count;
+
+ if (len == 0)
+ return (0);
+
+ m = m->m_next;
+ if (m == NULL)
+ break;
+
+ cp += count;
+ off = 0;
+ }
+
+ return (-1);
+}
+
+u_int32_t
+bpf_mbuf_ldw(const void *m0, u_int32_t k, int *err)
+{
+ u_int32_t v;
+
+ if (bpf_mbuf_copy(m0, k, &v, sizeof(v)) != 0) {
+ *err = 1;
+ return (0);
+ }
+
+ *err = 0;
+ return ntohl(v);
+}
+
+u_int32_t
+bpf_mbuf_ldh(const void *m0, u_int32_t k, int *err)
+{
+ u_int16_t v;
+
+ if (bpf_mbuf_copy(m0, k, &v, sizeof(v)) != 0) {
+ *err = 1;
+ return (0);
+ }
+
+ *err = 0;
+ return ntohs(v);
+}
+
+u_int32_t
+bpf_mbuf_ldb(const void *m0, u_int32_t k, int *err)
+{
+ const struct mbuf *m = m0;
+
+ while (k >= m->m_len) {
+ k -= m->m_len;
+
+ m = m->m_next;
+ if (m == NULL) {
+ *err = 1;
+ return (0);
+ }
+ }
+
+ *err = 0;
+ return (m->m_data[k]);
+}
+
+u_int
+bpf_mfilter(const struct bpf_insn *pc, const struct mbuf *m, u_int wirelen)
+{
+ return _bpf_filter(pc, &bpf_mbuf_ops, m, wirelen);
}
Index: sys/net/bpf.h
===================================================================
RCS file: /cvs/src/sys/net/bpf.h,v
retrieving revision 1.51
diff -u -p -r1.51 bpf.h
--- sys/net/bpf.h 29 Mar 2016 10:38:27 -0000 1.51
+++ sys/net/bpf.h 30 Mar 2016 05:13:44 -0000
@@ -265,13 +265,28 @@ struct bpf_dltlist {
};
/*
+ * Load operations for _bpf_filter to use against the packet pointer.
+ */
+struct bpf_ops {
+ u_int32_t (*ldw)(const void *, u_int32_t, int *);
+ u_int32_t (*ldh)(const void *, u_int32_t, int *);
+ u_int32_t (*ldb)(const void *, u_int32_t, int *);
+};
+
+/*
* Macros for insn array initializers.
*/
#define BPF_STMT(code, k) { (u_int16_t)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (u_int16_t)(code), jt, jf, k }
+u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int);
+
+u_int _bpf_filter(const struct bpf_insn *, const struct bpf_ops *,
+ const void *, u_int);
+
#ifdef _KERNEL
struct ifnet;
+struct mbuf;
int bpf_validate(struct bpf_insn *, int);
int bpf_tap(caddr_t, u_char *, u_int, u_int);
@@ -283,7 +298,8 @@ int bpf_mtap_ether(caddr_t, struct mbuf
void bpfattach(caddr_t *, struct ifnet *, u_int, u_int);
void bpfdetach(struct ifnet *);
void bpfilterattach(int);
-u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int);
+
+u_int bpf_mfilter(const struct bpf_insn *, const struct mbuf *, u_int);
#endif /* _KERNEL */
/*
Index: sys/net/bpf_filter.c
===================================================================
RCS file: /cvs/src/sys/net/bpf_filter.c,v
retrieving revision 1.27
diff -u -p -r1.27 bpf_filter.c
--- sys/net/bpf_filter.c 13 May 2015 10:42:46 -0000 1.27
+++ sys/net/bpf_filter.c 30 Mar 2016 05:13:44 -0000
@@ -49,99 +49,75 @@
#endif
#include <sys/endian.h>
-#ifdef __STRICT_ALIGNMENT
-#define BPF_ALIGN
-#endif
-
-#ifndef BPF_ALIGN
-#define EXTRACT_SHORT(p) ((u_int16_t)ntohs(*(u_int16_t *)p))
-#define EXTRACT_LONG(p) (ntohl(*(u_int32_t *)p))
-#else
-#define EXTRACT_SHORT(p)\
- ((u_int16_t)\
- ((u_int16_t)*((u_char *)p+0)<<8|\
- (u_int16_t)*((u_char *)p+1)<<0))
-#define EXTRACT_LONG(p)\
- ((u_int32_t)*((u_char *)p+0)<<24|\
- (u_int32_t)*((u_char *)p+1)<<16|\
- (u_int32_t)*((u_char *)p+2)<<8|\
- (u_int32_t)*((u_char *)p+3)<<0)
-#endif
#ifdef _KERNEL
-#include <sys/mbuf.h>
-#define MINDEX(len, m, k) \
-{ \
- len = m->m_len; \
- while (k >= len) { \
- k -= len; \
- m = m->m_next; \
- if (m == NULL) \
- return 0; \
- len = m->m_len; \
- } \
-}
-
extern int bpf_maxbufsize;
+#endif
+
+#include <net/bpf.h>
-int bpf_m_xword(struct mbuf *, u_int32_t, int *);
-int bpf_m_xhalf(struct mbuf *, u_int32_t, int *);
+struct bpf_mem {
+ const u_char *pkt;
+ u_int len;
+};
+
+u_int32_t bpf_mem_ldw(const void *, u_int32_t, int *);
+u_int32_t bpf_mem_ldh(const void *, u_int32_t, int *);
+u_int32_t bpf_mem_ldb(const void *, u_int32_t, int *);
+
+const struct bpf_ops bpf_mem_ops = {
+ bpf_mem_ldw,
+ bpf_mem_ldh,
+ bpf_mem_ldb,
+};
-int
-bpf_m_xword(struct mbuf *m, u_int32_t k, int *err)
+u_int32_t
+bpf_mem_ldw(const void *mem, u_int32_t k, int *err)
{
- int len;
- u_char *cp, *np;
- struct mbuf *m0;
+ const struct bpf_mem *bm = mem;
+ u_int32_t v;
*err = 1;
- MINDEX(len, m, k);
- cp = mtod(m, u_char *) + k;
- if (len >= k + 4) {
- *err = 0;
- return EXTRACT_LONG(cp);
- }
- m0 = m->m_next;
- if (m0 == NULL || m0->m_len + len - k < 4)
- return 0;
- *err = 0;
- np = mtod(m0, u_char *);
- switch (len - k) {
- case 1:
- return (cp[0] << 24) | (np[0] << 16) | (np[1] << 8) | np[2];
+ if (k + sizeof(v) > bm->len)
+ return (0);
- case 2:
- return (cp[0] << 24) | (cp[1] << 16) | (np[0] << 8) | np[1];
+ memcpy(&v, bm->pkt + k, sizeof(v));
- default:
- return (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | np[0];
- }
+ *err = 0;
+ return ntohl(v);
}
-int
-bpf_m_xhalf(struct mbuf *m, u_int32_t k, int *err)
+u_int32_t
+bpf_mem_ldh(const void *mem, u_int32_t k, int *err)
{
- int len;
- u_char *cp;
- struct mbuf *m0;
+ const struct bpf_mem *bm = mem;
+ u_int16_t v;
*err = 1;
- MINDEX(len, m, k);
- cp = mtod(m, u_char *) + k;
- if (len >= k + 2) {
- *err = 0;
- return EXTRACT_SHORT(cp);
- }
- m0 = m->m_next;
- if (m0 == NULL)
- return 0;
+
+ if (k + sizeof(v) > bm->len)
+ return (0);
+
+ memcpy(&v, bm->pkt + k, sizeof(v));
+
*err = 0;
- return (cp[0] << 8) | mtod(m0, u_char *)[0];
+ return ntohs(v);
}
-#endif
-#include <net/bpf.h>
+u_int32_t
+bpf_mem_ldb(const void *mem, u_int32_t k, int *err)
+{
+ const struct bpf_mem *bm = mem;
+
+ *err = 1;
+
+ if (k >= bm->len)
+ return (0);
+
+ *err = 0;
+ return bm->pkt[k];
+}
/*
* Execute the filter program starting at pc on the packet p
@@ -149,19 +125,34 @@ bpf_m_xhalf(struct mbuf *m, u_int32_t k,
* buflen is the amount of data present
*/
u_int
-bpf_filter(struct bpf_insn *pc, u_char *p, u_int wirelen, u_int buflen)
+bpf_filter(struct bpf_insn *pc, u_char *pkt, u_int wirelen, u_int buflen)
+{
+ struct bpf_mem bm;
+
+ bm.pkt = pkt;
+ bm.len = buflen;
+
+ return _bpf_filter(pc, &bpf_mem_ops, &bm, wirelen);
+}
+
+u_int
+_bpf_filter(const struct bpf_insn *pc, const struct bpf_ops *ops,
+ const void *pkt, u_int wirelen)
{
u_int32_t A = 0, X = 0;
u_int32_t k;
int32_t mem[BPF_MEMWORDS];
+ int err;
bzero(mem, sizeof(mem));
- if (pc == 0)
+ if (pc == NULL) {
/*
* No filter means accept all.
*/
return (u_int)-1;
+ }
+
--pc;
while (1) {
++pc;
@@ -180,61 +171,21 @@ bpf_filter(struct bpf_insn *pc, u_char *
return (u_int)A;
case BPF_LD|BPF_W|BPF_ABS:
- k = pc->k;
- if (k > buflen || sizeof(int32_t) > buflen - k) {
-#ifdef _KERNEL
- int merr;
-
- if (buflen != 0)
- return 0;
- A = bpf_m_xword((struct mbuf *)p, k, &merr);
- if (merr != 0)
- return 0;
- continue;
-#else
+ A = ops->ldw(pkt, pc->k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = EXTRACT_LONG(&p[k]);
continue;
case BPF_LD|BPF_H|BPF_ABS:
- k = pc->k;
- if (k > buflen || sizeof(int16_t) > buflen - k) {
-#ifdef _KERNEL
- int merr;
-
- if (buflen != 0)
- return 0;
- A = bpf_m_xhalf((struct mbuf *)p, k, &merr);
- if (merr != 0)
- return 0;
- continue;
-#else
+ A = ops->ldh(pkt, pc->k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = EXTRACT_SHORT(&p[k]);
continue;
case BPF_LD|BPF_B|BPF_ABS:
- k = pc->k;
- if (k >= buflen) {
-#ifdef _KERNEL
- struct mbuf *m;
- int len;
-
- if (buflen != 0)
- return 0;
- m = (struct mbuf *)p;
- MINDEX(len, m, k);
- A = mtod(m, u_char *)[k];
- continue;
-#else
+ A = ops->ldb(pkt, pc->k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = p[k];
continue;
case BPF_LD|BPF_W|BPF_LEN:
@@ -247,80 +198,31 @@ bpf_filter(struct bpf_insn *pc, u_char *
case BPF_LD|BPF_W|BPF_IND:
k = X + pc->k;
- if (k > buflen || sizeof(int32_t) > buflen - k) {
-#ifdef _KERNEL
- int merr;
-
- if (buflen != 0)
- return 0;
- A = bpf_m_xword((struct mbuf *)p, k, &merr);
- if (merr != 0)
- return 0;
- continue;
-#else
+ A = ops->ldw(pkt, k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = EXTRACT_LONG(&p[k]);
continue;
case BPF_LD|BPF_H|BPF_IND:
k = X + pc->k;
- if (k > buflen || sizeof(int16_t) > buflen - k) {
-#ifdef _KERNEL
- int merr;
-
- if (buflen != 0)
- return 0;
- A = bpf_m_xhalf((struct mbuf *)p, k, &merr);
- if (merr != 0)
- return 0;
- continue;
-#else
+ A = ops->ldh(pkt, k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = EXTRACT_SHORT(&p[k]);
continue;
case BPF_LD|BPF_B|BPF_IND:
k = X + pc->k;
- if (k >= buflen) {
-#ifdef _KERNEL
- struct mbuf *m;
- int len;
-
- if (buflen != 0)
- return 0;
- m = (struct mbuf *)p;
- MINDEX(len, m, k);
- A = mtod(m, u_char *)[k];
- continue;
-#else
+ A = ops->ldb(pkt, k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- A = p[k];
continue;
case BPF_LDX|BPF_MSH|BPF_B:
- k = pc->k;
- if (k >= buflen) {
-#ifdef _KERNEL
- struct mbuf *m;
- int len;
-
- if (buflen != 0)
- return 0;
- m = (struct mbuf *)p;
- MINDEX(len, m, k);
- X = (mtod(m, u_char *)[k] & 0xf) << 2;
- continue;
-#else
+ X = ops->ldb(pkt, pc->k, &err);
+ if (err != 0)
return 0;
-#endif
- }
- X = (p[pc->k] & 0xf) << 2;
+ X &= 0xf;
+ X <<= 2;
continue;
case BPF_LD|BPF_IMM:
Index: sys/net/bpfdesc.h
===================================================================
RCS file: /cvs/src/sys/net/bpfdesc.h,v
retrieving revision 1.29
diff -u -p -r1.29 bpfdesc.h
--- sys/net/bpfdesc.h 3 Dec 2015 16:27:32 -0000 1.29
+++ sys/net/bpfdesc.h 30 Mar 2016 05:13:44 -0000
@@ -80,7 +80,6 @@ struct bpf_d {
u_char bd_locked; /* true if descriptor is locked */
u_char bd_fildrop; /* true if filtered packets will be
dropped */
u_char bd_dirfilt; /* direction filter */
- u_int bd_queue; /* the queue the user wants to watch (0
== all) */
int bd_hdrcmplt; /* false to fill in src lladdr
automatically */
int bd_async; /* non-zero if packet reception should
generate signal */
int bd_sig; /* signal to send upon packet reception
*/
Index: lib/libpcap/pcap.h
===================================================================
RCS file: /cvs/src/lib/libpcap/pcap.h,v
retrieving revision 1.16
diff -u -p -r1.16 pcap.h
--- lib/libpcap/pcap.h 11 Apr 2014 04:08:58 -0000 1.16
+++ lib/libpcap/pcap.h 30 Mar 2016 05:13:44 -0000
@@ -226,8 +226,6 @@ void pcap_freealldevs(pcap_if_t *);
const char *pcap_lib_version(void);
-/* XXX this guy lives in the bpf tree */
-u_int bpf_filter(struct bpf_insn *, u_char *, u_int, u_int);
char *bpf_image(struct bpf_insn *, int);
int pcap_get_selectable_fd(pcap_t *);