aboutsummaryrefslogtreecommitdiffstats
path: root/wiretap/bpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'wiretap/bpf.c')
-rw-r--r--wiretap/bpf.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/wiretap/bpf.c b/wiretap/bpf.c
new file mode 100644
index 0000000000..59033f93a0
--- /dev/null
+++ b/wiretap/bpf.c
@@ -0,0 +1,372 @@
+/*
+ * bpf.c
+ * -----
+ * Creates and handles the BPF code produced by wiretap.
+ *
+ * Gilbert Ramirez
+ */
+
+#ifndef __G_LIB_H__
+#include <glib.h>
+#endif
+
+#include <netinet/in.h>
+
+#include "wtap.h"
+#include "rt-compile.h"
+#include "rt-global.h"
+#include "bpf-engine.h"
+#include "bpf.h"
+
+
+static GList *bpf_code_just_parsed = NULL;
+static struct bpf_instruction *bpf_record = NULL;
+
+static int
+bpf_clean_jump(GList *L, int i_this, int jmp, int num_bpf_instructions,
+ int i_ret_success, int i_ret_failure);
+static void
+bpf_pass1(GList *L);
+
+static GList*
+bpf_mk_bytecmp(int ftype, int rel_opcode, guint8 *bytes);
+
+static void
+bpf_optimize(GList *L);
+
+static int
+bpf_attach(wtap *wth);
+
+static void
+bpf_attach_record(gpointer bpf_code, gpointer junk);
+
+static int
+offline_attach(wtap *wth);
+
+
+/* sets function pointers in rt-grammar.y to point to the BPF-related
+ * functions */
+void
+wtap_filter_bpf_init(void)
+{
+ mk_bytecmp = bpf_mk_bytecmp;
+ mk_optimize = bpf_optimize;
+ mk_attach = bpf_attach;
+}
+
+/* almost the same as bpf_init... */
+void
+wtap_filter_offline_init(wtap *wth)
+{
+ int fi; /* filter index */
+
+ mk_bytecmp = bpf_mk_bytecmp;
+ mk_optimize = bpf_optimize;
+ mk_attach = offline_attach;
+
+ wtap_filter_offline_clear(wth);
+
+ /* make the offline filter array */
+ wth->filter.offline = g_malloc(sizeof(int*) * WTAP_NUM_ENCAP_TYPES);
+ wth->filter_type = WTAP_FILTER_OFFLINE;
+ wth->offline_filter_lengths = g_malloc(sizeof(int) * WTAP_NUM_ENCAP_TYPES);
+
+ for (fi = 0; fi < WTAP_NUM_ENCAP_TYPES; fi++) {
+ wth->filter.offline[fi] = NULL;
+ }
+}
+
+/* Removes an offline filter from a wtap struct, and frees memory used
+ * by that filter */
+void
+wtap_filter_offline_clear(wtap *wth)
+{
+ int fi; /* filter index */
+
+ if (wth->filter.offline) {
+ for (fi = 0; fi < WTAP_NUM_ENCAP_TYPES; fi++) {
+ if (wth->filter.offline[fi])
+ g_free(wth->filter.offline[fi]);
+ }
+ g_free(wth->filter.offline);
+ g_free(wth->offline_filter_lengths);
+ }
+ wth->filter_type = WTAP_FILTER_NONE;
+}
+
+/* Allocate a new bpf_code_unit structure and initialize the BPF instruction
+ * codes to the values passed by the caller. */
+static struct bpf_code_unit *
+bpf_code_unit_alloc(guint8 label, guint16 code, guint8 jt, guint8 jf, guint32 k)
+{
+ struct bpf_code_unit *bpf;
+
+ bpf = g_malloc(sizeof(struct bpf_code_unit));
+ bpf->line_label = label;
+ bpf->bpf.code = code;
+ bpf->bpf.jt = jt;
+ bpf->bpf.jf = jf;
+ bpf->bpf.k = k;
+
+ /*g_print("{ %d { 0x%02x, %d, %d, 0x%08x }},\n",
+ label, code, jt, jf, k);*/
+ return bpf;
+}
+
+
+/* Finds ftype in the bytecmp_table, the relation, and the n-string
+byte array, and creates BPF that will check those bytes */
+static GList*
+bpf_mk_bytecmp(int ftype, int rel_opcode, guint8 *bytes)
+{
+ GList *L;
+ struct bpf_code_unit *bpf;
+ int len_to_cmp, offset, endpoint, label;
+ bytecmp_info *b;
+
+ L = g_list_alloc();
+
+ /* find the field in the table */
+ b = lookup_bytecmp(ftype);
+
+ /* How many bytes do we have to compare, and where? */
+ len_to_cmp = b->length;
+ offset = b->offset;
+ endpoint = len_to_cmp + offset;
+ /*g_print("len_to_cmp=%d, offset=%d, endpoint=%d\n",
+ len_to_cmp, offset, endpoint);
+ g_print("bytes: (%d) %02X:%02X:%02X\n",
+ bytes[0], bytes[1], bytes[2], bytes[3]);*/
+
+ label = NEXT_BLOCK;
+ /* loop until we have written instructions to compare
+ all bytes */
+ while (len_to_cmp) {
+
+ if (len_to_cmp >= 4) {
+ bpf = bpf_code_unit_alloc(label,
+ BPF_LD|BPF_W|BPF_ABS,
+ 0, 0, endpoint - 4);
+ g_list_append(L, bpf);
+ label = NO_LABEL;
+
+ endpoint -= 4;
+ bpf = bpf_code_unit_alloc(NO_LABEL,
+ BPF_JMP|BPF_JEQ,
+ (len_to_cmp == 4 ? END_OF_PROGRAM_SUCCESS : 0),
+ NEXT_BLOCK,
+ htonl(*(guint32*)&bytes[len_to_cmp-3]));
+ g_list_append(L, bpf);
+
+ len_to_cmp -= 4;
+ }
+ else if (len_to_cmp == 3) {
+ bpf = bpf_code_unit_alloc(label,
+ BPF_LD|BPF_W|BPF_ABS,
+ 0, 0, endpoint - 3);
+ g_list_append(L, bpf);
+ label = NO_LABEL;
+ endpoint -= 3;
+
+ bpf = bpf_code_unit_alloc(NO_LABEL,
+ BPF_ALU|BPF_AND,
+ 0, 0,
+ htonl(0xffffff));
+ g_list_append(L, bpf);
+
+ bpf = bpf_code_unit_alloc(NO_LABEL,
+ BPF_JMP|BPF_JEQ,
+ (len_to_cmp == 3 ? END_OF_PROGRAM_SUCCESS : 0),
+ NEXT_BLOCK,
+ htonl(*(guint32*)&bytes[len_to_cmp-2]) & 0xffffff00);
+ g_list_append(L, bpf);
+
+ len_to_cmp -= 3;
+ }
+ else if (len_to_cmp == 2) {
+ bpf = bpf_code_unit_alloc(label,
+ BPF_LD|BPF_H|BPF_ABS,
+ 0, 0, endpoint - 2);
+ g_list_append(L, bpf);
+ label = NO_LABEL;
+
+ endpoint -= 2;
+ bpf = bpf_code_unit_alloc(NO_LABEL,
+ BPF_JMP|BPF_JEQ,
+ (len_to_cmp == 2 ? END_OF_PROGRAM_SUCCESS : 0),
+ NEXT_BLOCK,
+ (guint32)htons(*(guint16*)&bytes[len_to_cmp-1]));
+ g_list_append(L, bpf);
+
+ len_to_cmp -= 2;
+ }
+ else if (len_to_cmp == 1) {
+ bpf = bpf_code_unit_alloc(label,
+ BPF_LD|BPF_B|BPF_ABS,
+ 0, 0, endpoint - 1);
+ g_list_append(L, bpf);
+ label = NO_LABEL;
+
+ endpoint--;
+ bpf = bpf_code_unit_alloc(NO_LABEL,
+ BPF_JMP|BPF_JEQ,
+ END_OF_PROGRAM_SUCCESS, NEXT_BLOCK,
+ bytes[len_to_cmp]);
+ g_list_append(L, bpf);
+ len_to_cmp--;
+ }
+ }
+
+
+ L = g_list_remove(L, 0);
+ return L;
+}
+
+
+static void
+bpf_optimize(GList *L)
+{
+ bpf_pass1(L);
+ bpf_code_just_parsed = L;
+}
+
+/* after the BPF code is constructed from the parser, this step is run. During
+ * pass1 we:
+ *
+ * 1. Clean up the jump variables
+ */
+static void
+bpf_pass1(GList *L)
+{
+ struct bpf_code_unit *bpf;
+ int num_bpf_instructions;
+ int i_ret_success;
+ int i_ret_failure;
+ int i;
+
+ /* Attach a SUCCESS return to the end of the BPF code */
+ bpf = bpf_code_unit_alloc(END_OF_PROGRAM_SUCCESS, BPF_RET, 0, 0, 0xffff);
+ g_list_append(L, bpf);
+
+ /* Attach a FAILURE return to the end of the BPF code */
+ bpf = bpf_code_unit_alloc(END_OF_PROGRAM_FAILURE, BPF_RET, 0, 0, 0);
+ g_list_append(L, bpf);
+
+ num_bpf_instructions = g_list_length(L);
+ i_ret_success = num_bpf_instructions - 2;
+ i_ret_failure = num_bpf_instructions - 1;
+
+ for(i = 0; i < num_bpf_instructions; i++) {
+ bpf = (struct bpf_code_unit*) g_list_nth_data(L, i);
+ if (!bpf)
+ continue;
+
+ /* Check for Jump to end failure/success */
+ if (bpf->bpf.code & BPF_JMP) {
+
+ bpf->bpf.jt = bpf_clean_jump(L, i, bpf->bpf.jt, num_bpf_instructions,
+ i_ret_success, i_ret_failure);
+
+ bpf->bpf.jf = bpf_clean_jump(L, i, bpf->bpf.jf, num_bpf_instructions,
+ i_ret_success, i_ret_failure);
+ }
+ }
+}
+
+static int
+bpf_clean_jump(GList *L, int i_this, int jmp, int num_bpf_instructions,
+ int i_ret_success, int i_ret_failure)
+{
+ int i;
+ struct bpf_code_unit *bpf;
+
+ switch(jmp) {
+ case END_OF_PROGRAM_SUCCESS:
+ return i_ret_success - i_this - 1;
+
+ case END_OF_PROGRAM_FAILURE:
+ return i_ret_failure - i_this - 1;
+
+ case NEXT_BLOCK:
+ for (i = i_this + 1; i < num_bpf_instructions; i++) {
+ bpf = (struct bpf_code_unit*) g_list_nth_data(L, i);
+ if (!bpf)
+ continue;
+ if (bpf->line_label == NEXT_BLOCK) {
+ return i - i_this - 1;
+ }
+ }
+ /* failed to find NEXT_BLOCK.... chose FAILURE */
+ return i_ret_failure - i_this - 1;
+
+ /* default: nothing */
+ }
+ return jmp;
+}
+
+
+
+/* Takes code from bpf_code_just_parsed and attaches it to wth
+ * returns 1 if sucessfull, 0 if not */
+static int bpf_attach(wtap *wth)
+{
+ if (wth->filter.bpf)
+ g_free(wth->filter.bpf);
+
+ /* filter_length will be number of bpf_block records */
+ wth->filter_length = g_list_length(bpf_code_just_parsed) - 1;
+
+ wth->filter.bpf = g_malloc(wth->filter_length *
+ sizeof(struct bpf_instruction));
+ wth->filter_type = WTAP_FILTER_BPF;
+
+ bpf_record = wth->filter.bpf;
+
+ g_list_foreach(bpf_code_just_parsed, bpf_attach_record, NULL);
+
+ if (bpf_chk_filter(wth->filter.bpf, wth->filter_length) == 0)
+ return 1;
+ else
+ return 0;
+
+}
+
+void bpf_attach_record(gpointer bpf_code, gpointer junk)
+{
+ struct bpf_code_unit *bpf_c = (struct bpf_code_unit*) bpf_code;
+
+ struct bpf_instruction *bpf_i;
+
+ if (!bpf_c)
+ return;
+
+ bpf_i = &(bpf_c->bpf);
+ memcpy(bpf_record, bpf_i, sizeof(struct bpf_instruction));
+ bpf_record++;
+}
+
+
+/* Takes code from bpf_code_just_parsed and attachs it to wth.
+ * returns 1 if sucessfull, 0 if not */
+static int offline_attach(wtap *wth)
+{
+ /* filter_length will be number of bpf_instruction records */
+ wth->offline_filter_lengths[comp_encap_type] =
+ g_list_length(bpf_code_just_parsed);
+
+ /* Make space for this filter */
+ wth->filter.offline[comp_encap_type] =
+ g_malloc(wth->offline_filter_lengths[comp_encap_type]
+ * sizeof(struct bpf_instruction));
+
+ bpf_record = wth->filter.offline[comp_encap_type];
+
+ g_list_foreach(bpf_code_just_parsed, bpf_attach_record, NULL);
+
+ if (bpf_chk_filter(wth->filter.offline[comp_encap_type],
+ wth->offline_filter_lengths[comp_encap_type]) == 0)
+ return 1;
+ else
+ return 0;
+}
+