summaryrefslogtreecommitdiff
path: root/bpf/net/bpf_filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'bpf/net/bpf_filter.c')
-rw-r--r--bpf/net/bpf_filter.c120
1 files changed, 92 insertions, 28 deletions
diff --git a/bpf/net/bpf_filter.c b/bpf/net/bpf_filter.c
index ad689e16a912..01a1b64e7197 100644
--- a/bpf/net/bpf_filter.c
+++ b/bpf/net/bpf_filter.c
@@ -42,11 +42,11 @@
#include "config.h"
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <pcap-stdinc.h>
-#else /* WIN32 */
+#else /* _WIN32 */
#if HAVE_INTTYPES_H
#include <inttypes.h>
@@ -73,7 +73,7 @@
# define MLEN(m) ((m)->m_len)
#endif /* defined(__hpux) || SOLARIS */
-#endif /* WIN32 */
+#endif /* _WIN32 */
#include <pcap/bpf.h>
@@ -99,7 +99,7 @@
#endif
#ifndef LBL_ALIGN
-#ifndef WIN32
+#ifndef _WIN32
#include <netinet/in.h>
#endif
@@ -195,23 +195,41 @@ m_xhalf(m, k, err)
}
#endif
+#ifdef __linux__
+#include <linux/types.h>
+#include <linux/if_packet.h>
+#include <linux/filter.h>
+#endif
+
+enum {
+ BPF_S_ANC_NONE,
+ BPF_S_ANC_VLAN_TAG,
+ BPF_S_ANC_VLAN_TAG_PRESENT,
+};
+
/*
* Execute the filter program starting at pc on the packet p
* wirelen is the length of the original packet
* buflen is the amount of data present
+ * aux_data is auxiliary data, currently used only when interpreting
+ * filters intended for the Linux kernel in cases where the kernel
+ * rejects the filter; it contains VLAN tag information
* For the kernel, p is assumed to be a pointer to an mbuf if buflen is 0,
* in all other cases, p is a pointer to a buffer and buflen is its size.
+ *
+ * Thanks to Ani Sinha <ani@arista.com> for providing initial implementation
*/
u_int
-bpf_filter(pc, p, wirelen, buflen)
+bpf_filter_with_aux_data(pc, p, wirelen, buflen, aux_data)
register const struct bpf_insn *pc;
register const u_char *p;
u_int wirelen;
register u_int buflen;
+ register const struct bpf_aux_data *aux_data;
{
register u_int32 A, X;
- register int k;
- int32 mem[BPF_MEMWORDS];
+ register bpf_u_int32 k;
+ u_int32 mem[BPF_MEMWORDS];
#if defined(KERNEL) || defined(_KERNEL)
struct mbuf *m, *n;
int merr, len;
@@ -250,7 +268,7 @@ bpf_filter(pc, p, wirelen, buflen)
case BPF_LD|BPF_W|BPF_ABS:
k = pc->k;
- if (k + sizeof(int32) > buflen) {
+ if (k > buflen || sizeof(int32_t) > buflen - k) {
#if defined(KERNEL) || defined(_KERNEL)
if (m == NULL)
return 0;
@@ -267,7 +285,7 @@ bpf_filter(pc, p, wirelen, buflen)
case BPF_LD|BPF_H|BPF_ABS:
k = pc->k;
- if (k + sizeof(short) > buflen) {
+ if (k > buflen || sizeof(int16_t) > buflen - k) {
#if defined(KERNEL) || defined(_KERNEL)
if (m == NULL)
return 0;
@@ -283,22 +301,50 @@ bpf_filter(pc, p, wirelen, buflen)
continue;
case BPF_LD|BPF_B|BPF_ABS:
- k = pc->k;
- if (k >= buflen) {
+ {
+#if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT)
+ int code = BPF_S_ANC_NONE;
+#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \
+ code = BPF_S_ANC_##CODE; \
+ if (!aux_data) \
+ return 0; \
+ break;
+
+ switch (pc->k) {
+ ANCILLARY(VLAN_TAG);
+ ANCILLARY(VLAN_TAG_PRESENT);
+ default :
+#endif
+ k = pc->k;
+ if (k >= buflen) {
#if defined(KERNEL) || defined(_KERNEL)
- if (m == NULL)
- return 0;
- n = m;
- MINDEX(len, n, k);
- A = mtod(n, u_char *)[k];
- continue;
+ if (m == NULL)
+ return 0;
+ n = m;
+ MINDEX(len, n, k);
+ A = mtod(n, u_char *)[k];
+ continue;
#else
- return 0;
+ return 0;
#endif
+ }
+ A = p[k];
+#if defined(SKF_AD_VLAN_TAG) && defined(SKF_AD_VLAN_TAG_PRESENT)
+ }
+ switch (code) {
+ case BPF_S_ANC_VLAN_TAG:
+ if (aux_data)
+ A = aux_data->vlan_tag;
+ break;
+
+ case BPF_S_ANC_VLAN_TAG_PRESENT:
+ if (aux_data)
+ A = aux_data->vlan_tag_present;
+ break;
+ }
+#endif
+ continue;
}
- A = p[k];
- continue;
-
case BPF_LD|BPF_W|BPF_LEN:
A = wirelen;
continue;
@@ -309,7 +355,8 @@ bpf_filter(pc, p, wirelen, buflen)
case BPF_LD|BPF_W|BPF_IND:
k = X + pc->k;
- if (k + sizeof(int32) > buflen) {
+ if (pc->k > buflen || X > buflen - pc->k ||
+ sizeof(int32_t) > buflen - k) {
#if defined(KERNEL) || defined(_KERNEL)
if (m == NULL)
return 0;
@@ -326,7 +373,8 @@ bpf_filter(pc, p, wirelen, buflen)
case BPF_LD|BPF_H|BPF_IND:
k = X + pc->k;
- if (k + sizeof(short) > buflen) {
+ if (X > buflen || pc->k > buflen - X ||
+ sizeof(int16_t) > buflen - k) {
#if defined(KERNEL) || defined(_KERNEL)
if (m == NULL)
return 0;
@@ -343,7 +391,7 @@ bpf_filter(pc, p, wirelen, buflen)
case BPF_LD|BPF_B|BPF_IND:
k = X + pc->k;
- if (k >= buflen) {
+ if (pc->k >= buflen || X >= buflen - pc->k) {
#if defined(KERNEL) || defined(_KERNEL)
if (m == NULL)
return 0;
@@ -531,7 +579,12 @@ bpf_filter(pc, p, wirelen, buflen)
continue;
case BPF_ALU|BPF_NEG:
- A = -A;
+ /*
+ * Most BPF arithmetic is unsigned, but negation
+ * can't be unsigned; throw some casts to
+ * specify what we're trying to do.
+ */
+ A = (u_int32)(-(int32)A);
continue;
case BPF_MISC|BPF_TAX:
@@ -545,6 +598,17 @@ bpf_filter(pc, p, wirelen, buflen)
}
}
+u_int
+bpf_filter(pc, p, wirelen, buflen)
+ register const struct bpf_insn *pc;
+ register const u_char *p;
+ u_int wirelen;
+ register u_int buflen;
+{
+ return bpf_filter_with_aux_data(pc, p, wirelen, buflen, NULL);
+}
+
+
/*
* Return true if the 'fcode' is a valid filter program.
* The constraints are that each jump be forward and to a valid
@@ -574,7 +638,7 @@ bpf_validate(f, len)
return 0;
#endif
- for (i = 0; i < len; ++i) {
+ for (i = 0; i < (u_int)len; ++i) {
p = &f[i];
switch (BPF_CLASS(p->code)) {
/*
@@ -675,7 +739,7 @@ bpf_validate(f, len)
#if defined(KERNEL) || defined(_KERNEL)
if (from + p->k < from || from + p->k >= len)
#else
- if (from + p->k >= len)
+ if (from + p->k >= (u_int)len)
#endif
return 0;
break;
@@ -683,7 +747,7 @@ bpf_validate(f, len)
case BPF_JGT:
case BPF_JGE:
case BPF_JSET:
- if (from + p->jt >= len || from + p->jf >= len)
+ if (from + p->jt >= (u_int)len || from + p->jf >= (u_int)len)
return 0;
break;
default: