diff options
author | Daniel Borkmann <dborkman@redhat.com> | 2013-03-15 10:41:48 +0100 |
---|---|---|
committer | Daniel Borkmann <dborkman@redhat.com> | 2013-03-15 10:41:48 +0100 |
commit | 1a9fbac03c684f29cff9ac44875bd9504a89f54e (patch) | |
tree | 1b2e40dbe5dc1899ef5b62c4325c9b94c9c450fc /bpf.c |
all: import netsniff-ng 0.5.8-rc0 source
We decided to get rid of the old Git history and start a new one for
several reasons:
*) Allow / enforce only high-quality commits (which was not the case
for many commits in the history), have a policy that is more close
to the one from the Linux kernel. With high quality commits, we
mean code that is logically split into commits and commit messages
that are signed-off and have a proper subject and message body.
We do not allow automatic Github merges anymore, since they are
total bullshit. However, we will either cherry-pick your patches
or pull them manually.
*) The old archive was about ~27MB for no particular good reason.
This basically derived from the bad decision that also some PDF
files where stored there. From this moment onwards, no binary
objects are allowed to be stored in this repository anymore.
The old archive is not wiped away from the Internet. You will still
be able to find it, e.g. on git.cryptoism.org etc.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Diffstat (limited to 'bpf.c')
-rw-r--r-- | bpf.c | 765 |
1 files changed, 765 insertions, 0 deletions
@@ -0,0 +1,765 @@ +/* + * netsniff-ng - the packet sniffing beast + * Copyright 2009 - 2012 Daniel Borkmann. + * Copyright 2009, 2010 Emmanuel Roullit. + * Copyright 1990-1996 The Regents of the University of + * California. All rights reserved. (3-clause BSD license) + * Subject to the GPL, version 2. + */ + +#include <stdint.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "bpf.h" +#include "xmalloc.h" +#include "xutils.h" +#include "die.h" + +#define EXTRACT_SHORT(packet) \ + ((unsigned short) ntohs(*(unsigned short *) packet)) +#define EXTRACT_LONG(packet) \ + (ntohl(*(unsigned long *) packet)) + +#ifndef BPF_MEMWORDS +# define BPF_MEMWORDS 16 +#endif + +#define BPF_LD_B (BPF_LD | BPF_B) +#define BPF_LD_H (BPF_LD | BPF_H) +#define BPF_LD_W (BPF_LD | BPF_W) +#define BPF_LDX_B (BPF_LDX | BPF_B) +#define BPF_LDX_W (BPF_LDX | BPF_W) +#define BPF_JMP_JA (BPF_JMP | BPF_JA) +#define BPF_JMP_JEQ (BPF_JMP | BPF_JEQ) +#define BPF_JMP_JGT (BPF_JMP | BPF_JGT) +#define BPF_JMP_JGE (BPF_JMP | BPF_JGE) +#define BPF_JMP_JSET (BPF_JMP | BPF_JSET) +#define BPF_ALU_ADD (BPF_ALU | BPF_ADD) +#define BPF_ALU_SUB (BPF_ALU | BPF_SUB) +#define BPF_ALU_MUL (BPF_ALU | BPF_MUL) +#define BPF_ALU_DIV (BPF_ALU | BPF_DIV) +#define BPF_ALU_MOD (BPF_ALU | BPF_MOD) +#define BPF_ALU_NEG (BPF_ALU | BPF_NEG) +#define BPF_ALU_AND (BPF_ALU | BPF_AND) +#define BPF_ALU_OR (BPF_ALU | BPF_OR) +#define BPF_ALU_XOR (BPF_ALU | BPF_XOR) +#define BPF_ALU_LSH (BPF_ALU | BPF_LSH) +#define BPF_ALU_RSH (BPF_ALU | BPF_RSH) +#define BPF_MISC_TAX (BPF_MISC | BPF_TAX) +#define BPF_MISC_TXA (BPF_MISC | BPF_TXA) + +static const char *op_table[] = { + [BPF_LD_B] = "ldb", + [BPF_LD_H] = "ldh", + [BPF_LD_W] = "ld", + [BPF_LDX] = "ldx", + [BPF_LDX_B] = "ldxb", + [BPF_ST] = "st", + [BPF_STX] = "stx", + [BPF_JMP_JA] = "ja", + [BPF_JMP_JEQ] = "jeq", + [BPF_JMP_JGT] = "jgt", + [BPF_JMP_JGE] = "jge", + [BPF_JMP_JSET] = "jset", + [BPF_ALU_ADD] = "add", + [BPF_ALU_SUB] = "sub", + [BPF_ALU_MUL] = "mul", + [BPF_ALU_DIV] = "div", + [BPF_ALU_MOD] = "mod", + [BPF_ALU_NEG] = "neg", + [BPF_ALU_AND] = "and", + [BPF_ALU_OR] = "or", + [BPF_ALU_XOR] = "xor", + [BPF_ALU_LSH] = "lsh", + [BPF_ALU_RSH] = "rsh", + [BPF_RET] = "ret", + [BPF_MISC_TAX] = "tax", + [BPF_MISC_TXA] = "txa", +}; + +void bpf_dump_op_table(void) +{ + int i; + for (i = 0; i < array_size(op_table); ++i) { + if (op_table[i]) + printf("%s\n", op_table[i]); + } +} + +static const char *bpf_dump_linux_k(uint32_t k) +{ + switch (k) { + default: + return "[%d]"; + case SKF_AD_OFF + SKF_AD_PROTOCOL: + return "#proto"; + case SKF_AD_OFF + SKF_AD_PKTTYPE: + return "#type"; + case SKF_AD_OFF + SKF_AD_IFINDEX: + return "#ifidx"; + case SKF_AD_OFF + SKF_AD_NLATTR: + return "#nla"; + case SKF_AD_OFF + SKF_AD_NLATTR_NEST: + return "#nlan"; + case SKF_AD_OFF + SKF_AD_MARK: + return "#mark"; + case SKF_AD_OFF + SKF_AD_QUEUE: + return "#queue"; + case SKF_AD_OFF + SKF_AD_HATYPE: + return "#hatype"; + case SKF_AD_OFF + SKF_AD_RXHASH: + return "#rxhash"; + case SKF_AD_OFF + SKF_AD_CPU: + return "#cpu"; + case SKF_AD_OFF + SKF_AD_VLAN_TAG: + return "#vlant"; + case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT: + return "#vlanp"; + } +} + +static char *__bpf_dump(const struct sock_filter bpf, int n) +{ + int v; + const char *fmt, *op; + static char image[256]; + char operand[64]; + + v = bpf.k; + switch (bpf.code) { + default: + op = "unimp"; + fmt = "0x%x"; + v = bpf.code; + break; + case BPF_RET | BPF_K: + op = op_table[BPF_RET]; + fmt = "#0x%x"; + break; + case BPF_RET | BPF_A: + op = op_table[BPF_RET]; + fmt = "a"; + break; + case BPF_RET | BPF_X: + op = op_table[BPF_RET]; + fmt = "x"; + break; + case BPF_LD_W | BPF_ABS: + op = op_table[BPF_LD_W]; + fmt = bpf_dump_linux_k(bpf.k); + break; + case BPF_LD_H | BPF_ABS: + op = op_table[BPF_LD_H]; + fmt = bpf_dump_linux_k(bpf.k); + break; + case BPF_LD_B | BPF_ABS: + op = op_table[BPF_LD_B]; + fmt = bpf_dump_linux_k(bpf.k); + break; + case BPF_LD_W | BPF_LEN: + op = op_table[BPF_LD_W]; + fmt = "#len"; + break; + case BPF_LD_W | BPF_IND: + op = op_table[BPF_LD_W]; + fmt = "[x + %d]"; + break; + case BPF_LD_H | BPF_IND: + op = op_table[BPF_LD_H]; + fmt = "[x + %d]"; + break; + case BPF_LD_B | BPF_IND: + op = op_table[BPF_LD_B]; + fmt = "[x + %d]"; + break; + case BPF_LD | BPF_IMM: + op = op_table[BPF_LD_W]; + fmt = "#0x%x"; + break; + case BPF_LDX | BPF_IMM: + op = op_table[BPF_LDX]; + fmt = "#0x%x"; + break; + case BPF_LDX_B | BPF_MSH: + op = op_table[BPF_LDX_B]; + fmt = "4*([%d]&0xf)"; + break; + case BPF_LD | BPF_MEM: + op = op_table[BPF_LD_W]; + fmt = "M[%d]"; + break; + case BPF_LDX | BPF_MEM: + op = op_table[BPF_LDX]; + fmt = "M[%d]"; + break; + case BPF_ST: + op = op_table[BPF_ST]; + fmt = "M[%d]"; + break; + case BPF_STX: + op = op_table[BPF_STX]; + fmt = "M[%d]"; + break; + case BPF_JMP_JA: + op = op_table[BPF_JMP_JA]; + fmt = "%d"; + v = n + 1 + bpf.k; + break; + case BPF_JMP_JGT | BPF_K: + op = op_table[BPF_JMP_JGT]; + fmt = "#0x%x"; + break; + case BPF_JMP_JGE | BPF_K: + op = op_table[BPF_JMP_JGE]; + fmt = "#0x%x"; + break; + case BPF_JMP_JEQ | BPF_K: + op = op_table[BPF_JMP_JEQ]; + fmt = "#0x%x"; + break; + case BPF_JMP_JSET | BPF_K: + op = op_table[BPF_JMP_JSET]; + fmt = "#0x%x"; + break; + case BPF_JMP_JGT | BPF_X: + op = op_table[BPF_JMP_JGT]; + fmt = "x"; + break; + case BPF_JMP_JGE | BPF_X: + op = op_table[BPF_JMP_JGE]; + fmt = "x"; + break; + case BPF_JMP_JEQ | BPF_X: + op = op_table[BPF_JMP_JEQ]; + fmt = "x"; + break; + case BPF_JMP_JSET | BPF_X: + op = op_table[BPF_JMP_JSET]; + fmt = "x"; + break; + case BPF_ALU_ADD | BPF_X: + op = op_table[BPF_ALU_ADD]; + fmt = "x"; + break; + case BPF_ALU_SUB | BPF_X: + op = op_table[BPF_ALU_SUB]; + fmt = "x"; + break; + case BPF_ALU_MUL | BPF_X: + op = op_table[BPF_ALU_MUL]; + fmt = "x"; + break; + case BPF_ALU_DIV | BPF_X: + op = op_table[BPF_ALU_DIV]; + fmt = "x"; + break; + case BPF_ALU_MOD | BPF_X: + op = op_table[BPF_ALU_MOD]; + fmt = "x"; + break; + case BPF_ALU_AND | BPF_X: + op = op_table[BPF_ALU_AND]; + fmt = "x"; + break; + case BPF_ALU_OR | BPF_X: + op = op_table[BPF_ALU_OR]; + fmt = "x"; + break; + case BPF_ALU_XOR | BPF_X: + op = op_table[BPF_ALU_XOR]; + fmt = "x"; + break; + case BPF_ALU_LSH | BPF_X: + op = op_table[BPF_ALU_LSH]; + fmt = "x"; + break; + case BPF_ALU_RSH | BPF_X: + op = op_table[BPF_ALU_RSH]; + fmt = "x"; + break; + case BPF_ALU_ADD | BPF_K: + op = op_table[BPF_ALU_ADD]; + fmt = "#%d"; + break; + case BPF_ALU_SUB | BPF_K: + op = op_table[BPF_ALU_SUB]; + fmt = "#%d"; + break; + case BPF_ALU_MUL | BPF_K: + op = op_table[BPF_ALU_MUL]; + fmt = "#%d"; + break; + case BPF_ALU_DIV | BPF_K: + op = op_table[BPF_ALU_DIV]; + fmt = "#%d"; + break; + case BPF_ALU_MOD | BPF_K: + op = op_table[BPF_ALU_MOD]; + fmt = "#%d"; + break; + case BPF_ALU_AND | BPF_K: + op = op_table[BPF_ALU_AND]; + fmt = "#0x%x"; + break; + case BPF_ALU_OR | BPF_K: + op = op_table[BPF_ALU_OR]; + fmt = "#0x%x"; + break; + case BPF_ALU_XOR | BPF_K: + op = op_table[BPF_ALU_XOR]; + fmt = "#0x%x"; + break; + case BPF_ALU_LSH | BPF_K: + op = op_table[BPF_ALU_LSH]; + fmt = "#%d"; + break; + case BPF_ALU_RSH | BPF_K: + op = op_table[BPF_ALU_RSH]; + fmt = "#%d"; + break; + case BPF_ALU_NEG: + op = op_table[BPF_ALU_NEG]; + fmt = ""; + break; + case BPF_MISC_TAX: + op = op_table[BPF_MISC_TAX]; + fmt = ""; + break; + case BPF_MISC_TXA: + op = op_table[BPF_MISC_TXA]; + fmt = ""; + break; + } + + slprintf_nocheck(operand, sizeof(operand), fmt, v); + slprintf_nocheck(image, sizeof(image), + (BPF_CLASS(bpf.code) == BPF_JMP && + BPF_OP(bpf.code) != BPF_JA) ? + " L%d: %s %s, L%d, L%d" : " L%d: %s %s", + n, op, operand, n + 1 + bpf.jt, n + 1 + bpf.jf); + return image; +} + +void bpf_dump_all(struct sock_fprog *bpf) +{ + int i; + for (i = 0; i < bpf->len; ++i) + printf("%s\n", __bpf_dump(bpf->filter[i], i)); +} + +void bpf_attach_to_sock(int sock, struct sock_fprog *bpf) +{ + int ret; + + if (bpf->filter[0].code == BPF_RET && + bpf->filter[0].k == 0xFFFFFFFF) + return; + + ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, + bpf, sizeof(*bpf)); + if (ret < 0) + panic("Cannot attach filter to socket!\n"); +} + +void bpf_detach_from_sock(int sock) +{ + int ret, empty = 0; + + ret = setsockopt(sock, SOL_SOCKET, SO_DETACH_FILTER, + &empty, sizeof(empty)); + if (ret < 0) + panic("Cannot detach filter from socket!\n"); +} + +int enable_kernel_bpf_jit_compiler(void) +{ + int fd; + ssize_t ret; + char *file = "/proc/sys/net/core/bpf_jit_enable"; + + fd = open(file, O_WRONLY); + if (fd < 0) + return -1; + + ret = write(fd, "1", strlen("1")); + + close(fd); + return ret; +} + +int __bpf_validate(const struct sock_fprog *bpf) +{ + uint32_t i, from; + const struct sock_filter *p; + + if (!bpf) + return 0; + if (bpf->len < 1) + return 0; + + for (i = 0; i < bpf->len; ++i) { + p = &bpf->filter[i]; + switch (BPF_CLASS(p->code)) { + /* Check that memory operations use valid addresses. */ + case BPF_LD: + case BPF_LDX: + switch (BPF_MODE(p->code)) { + case BPF_IMM: + break; + case BPF_ABS: + case BPF_IND: + case BPF_MSH: + /* There's no maximum packet data size + * in userland. The runtime packet length + * check suffices. + */ + break; + case BPF_MEM: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_LEN: + break; + default: + return 0; + } + break; + case BPF_ST: + case BPF_STX: + if (p->k >= BPF_MEMWORDS) + return 0; + break; + case BPF_ALU: + switch (BPF_OP(p->code)) { + case BPF_ADD: + case BPF_SUB: + case BPF_MUL: + case BPF_OR: + case BPF_XOR: + case BPF_AND: + case BPF_LSH: + case BPF_RSH: + case BPF_NEG: + break; + case BPF_DIV: + case BPF_MOD: + /* Check for constant division by 0 (undefined + * for div and mod). + */ + if (BPF_RVAL(p->code) == BPF_K && p->k == 0) + return 0; + break; + default: + return 0; + } + break; + case BPF_JMP: + /* Check that jumps are within the code block, + * and that unconditional branches don't go + * backwards as a result of an overflow. + * Unconditional branches have a 32-bit offset, + * so they could overflow; we check to make + * sure they don't. Conditional branches have + * an 8-bit offset, and the from address is <= + * BPF_MAXINSNS, and we assume that BPF_MAXINSNS + * is sufficiently small that adding 255 to it + * won't overflow. + * + * We know that len is <= BPF_MAXINSNS, and we + * assume that BPF_MAXINSNS is < the maximum size + * of a u_int, so that i + 1 doesn't overflow. + * + * For userland, we don't know that the from + * or len are <= BPF_MAXINSNS, but we know that + * from <= len, and, except on a 64-bit system, + * it's unlikely that len, if it truly reflects + * the size of the program we've been handed, + * will be anywhere near the maximum size of + * a u_int. We also don't check for backward + * branches, as we currently support them in + * userland for the protochain operation. + */ + from = i + 1; + switch (BPF_OP(p->code)) { + case BPF_JA: + if (from + p->k >= bpf->len) + return 0; + break; + case BPF_JEQ: + case BPF_JGT: + case BPF_JGE: + case BPF_JSET: + if (from + p->jt >= bpf->len || + from + p->jf >= bpf->len) + return 0; + break; + default: + return 0; + } + break; + case BPF_RET: + break; + case BPF_MISC: + break; + default: + return 0; + } + } + + return BPF_CLASS(bpf->filter[bpf->len - 1].code) == BPF_RET; +} + +uint32_t bpf_run_filter(const struct sock_fprog * fcode, uint8_t * packet, + size_t plen) +{ + /* XXX: caplen == len */ + uint32_t A, X; + uint32_t k; + struct sock_filter *bpf; + int32_t mem[BPF_MEMWORDS] = { 0, }; + + if (fcode == NULL || fcode->filter == NULL || fcode->len == 0) + return 0xFFFFFFFF; + + A = 0; + X = 0; + + bpf = fcode->filter; + --bpf; + while (1) { + ++bpf; + switch (bpf->code) { + default: + return 0; + case BPF_RET | BPF_K: + return (uint32_t) bpf->k; + case BPF_RET | BPF_A: + return (uint32_t) A; + case BPF_LD_W | BPF_ABS: + /* No Linux extensions supported here! */ + k = bpf->k; + if (k + sizeof(int32_t) > plen) + return 0; + A = EXTRACT_LONG(&packet[k]); + continue; + case BPF_LD_H | BPF_ABS: + /* No Linux extensions supported here! */ + k = bpf->k; + if (k + sizeof(short) > plen) + return 0; + A = EXTRACT_SHORT(&packet[k]); + continue; + case BPF_LD_B | BPF_ABS: + /* No Linux extensions supported here! */ + k = bpf->k; + if (k >= plen) + return 0; + A = packet[k]; + continue; + case BPF_LD_W | BPF_LEN: + A = plen; + continue; + case BPF_LDX_W | BPF_LEN: + X = plen; + continue; + case BPF_LD_W | BPF_IND: + k = X + bpf->k; + if (k + sizeof(int32_t) > plen) + return 0; + A = EXTRACT_LONG(&packet[k]); + continue; + case BPF_LD_H | BPF_IND: + k = X + bpf->k; + if (k + sizeof(short) > plen) + return 0; + A = EXTRACT_SHORT(&packet[k]); + continue; + case BPF_LD_B | BPF_IND: + k = X + bpf->k; + if (k >= plen) + return 0; + A = packet[k]; + continue; + case BPF_LDX_B | BPF_MSH: + k = bpf->k; + if (k >= plen) + return 0; + X = (packet[bpf->k] & 0xf) << 2; + continue; + case BPF_LD | BPF_IMM: + A = bpf->k; + continue; + case BPF_LDX | BPF_IMM: + X = bpf->k; + continue; + case BPF_LD | BPF_MEM: + A = mem[bpf->k]; + continue; + case BPF_LDX | BPF_MEM: + X = mem[bpf->k]; + continue; + case BPF_ST: + mem[bpf->k] = A; + continue; + case BPF_STX: + mem[bpf->k] = X; + continue; + case BPF_JMP_JA: + bpf += bpf->k; + continue; + case BPF_JMP_JGT | BPF_K: + bpf += (A > bpf->k) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JGE | BPF_K: + bpf += (A >= bpf->k) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JEQ | BPF_K: + bpf += (A == bpf->k) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JSET | BPF_K: + bpf += (A & bpf->k) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JGT | BPF_X: + bpf += (A > X) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JGE | BPF_X: + bpf += (A >= X) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JEQ | BPF_X: + bpf += (A == X) ? bpf->jt : bpf->jf; + continue; + case BPF_JMP_JSET | BPF_X: + bpf += (A & X) ? bpf->jt : bpf->jf; + continue; + case BPF_ALU_ADD | BPF_X: + A += X; + continue; + case BPF_ALU_SUB | BPF_X: + A -= X; + continue; + case BPF_ALU_MUL | BPF_X: + A *= X; + continue; + case BPF_ALU_DIV | BPF_X: + if (X == 0) + return 0; + A /= X; + continue; + case BPF_ALU_MOD | BPF_X: + if (X == 0) + return 0; + A %= X; + continue; + case BPF_ALU_AND | BPF_X: + A &= X; + continue; + case BPF_ALU_OR | BPF_X: + A |= X; + continue; + case BPF_ALU_XOR | BPF_X: + A ^= X; + continue; + case BPF_ALU_LSH | BPF_X: + A <<= X; + continue; + case BPF_ALU_RSH | BPF_X: + A >>= X; + continue; + case BPF_ALU_ADD | BPF_K: + A += bpf->k; + continue; + case BPF_ALU_SUB | BPF_K: + A -= bpf->k; + continue; + case BPF_ALU_MUL | BPF_K: + A *= bpf->k; + continue; + case BPF_ALU_DIV | BPF_K: + A /= bpf->k; + continue; + case BPF_ALU_MOD | BPF_K: + A %= bpf->k; + continue; + case BPF_ALU_AND | BPF_K: + A &= bpf->k; + continue; + case BPF_ALU_OR | BPF_K: + A |= bpf->k; + continue; + case BPF_ALU_XOR | BPF_K: + A ^= bpf->k; + continue; + case BPF_ALU_LSH | BPF_K: + A <<= bpf->k; + continue; + case BPF_ALU_RSH | BPF_K: + A >>= bpf->k; + continue; + case BPF_ALU_NEG: + A = -A; + continue; + case BPF_MISC_TAX: + X = A; + continue; + case BPF_MISC_TXA: + A = X; + continue; + } + } +} + +void bpf_parse_rules(char *rulefile, struct sock_fprog *bpf, uint32_t link_type) +{ + int ret; + char buff[256]; + struct sock_filter sf_single = { 0x06, 0, 0, 0xFFFFFFFF }; + FILE *fp; + + if (rulefile == NULL) { + bpf->len = 1; + bpf->filter = xmalloc(sizeof(sf_single)); + fmemcpy(&bpf->filter[0], &sf_single, sizeof(sf_single)); + return; + } + + fp = fopen(rulefile, "r"); + if (!fp) { + bpf_try_compile(rulefile, bpf, link_type); + return; + } + + fmemset(buff, 0, sizeof(buff)); + while (fgets(buff, sizeof(buff), fp) != NULL) { + buff[sizeof(buff) - 1] = 0; + if (buff[0] != '{') { + fmemset(buff, 0, sizeof(buff)); + continue; + } + + fmemset(&sf_single, 0, sizeof(sf_single)); + ret = sscanf(buff, "{ 0x%x, %u, %u, 0x%08x },", + (unsigned int *) &sf_single.code, + (unsigned int *) &sf_single.jt, + (unsigned int *) &sf_single.jf, + (unsigned int *) &sf_single.k); + if (ret != 4) + panic("BPF syntax error!\n"); + + bpf->len++; + bpf->filter = xrealloc(bpf->filter, 1, + bpf->len * sizeof(sf_single)); + + fmemcpy(&bpf->filter[bpf->len - 1], &sf_single, + sizeof(sf_single)); + fmemset(buff, 0, sizeof(buff)); + } + + fclose(fp); + + if (__bpf_validate(bpf) == 0) + panic("This is not a valid BPF program!\n"); +} |