diff options
Diffstat (limited to 'bpf/net/bpf_filter.c')
-rw-r--r-- | bpf/net/bpf_filter.c | 120 |
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: |