Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
CC=gcc
CC?=gcc

CFLAGS=-Wall -Werror -ggdb
LDFLAGS=-lpcap
CFLAGS+=-Wall -Werror -ggdb
LDFLAGS?=-lpcap -lpthread
NAME=bpfcountd
PREFIX?=/usr/local

CONFDIR?=${PREFIX}/etc/bpfcountd

bpfcountd: main.o list.o usock.o filters.o util.o
$(CC) main.o list.o usock.o filters.o util.o -o ${NAME} ${LDFLAGS}
$(CC) ${CFLAGS} main.o list.o usock.o filters.o util.o -o ${NAME} ${LDFLAGS}

all: test bpfcountd

Expand Down
170 changes: 145 additions & 25 deletions filters.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,173 @@

#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h> // for epoll_ctl(), struct epoll_event
#include <unistd.h>
#include <pthread.h>

#include "util.h"


void filters_init(filters_ctx *ctx, pcap_t* pcap_ctx) {
void filters_init(filters_ctx *ctx) {
ctx->filters = list_new();
ctx->pcap_ctx = pcap_ctx;
ctx->count = 0;
}

void *filters_finish_thread(void *args) {
struct filter *filter = args;

close(filter->fd);
pcap_close(filter->pcap_ctx);
filter->pcap_ctx = NULL;

return NULL;
}

void filters_finish(filters_ctx *ctx) {
pthread_t *id = calloc(ctx->count, sizeof(*id));
int i = 0, j = 0;

// parallelize shutdown, pcap_close() is slow unfortunately...
if (id) {
list_foreach(ctx->filters, f) {
struct filter *tmp = list_data(f, struct filter);

pthread_create(&id[i++], NULL, &filters_finish_thread, tmp);
}
}

// free the filters
list_foreach(ctx->filters, f) {
struct filter *tmp = list_data(f, struct filter);
list_foreach(ctx->filters, f2) {
struct filter *tmp = list_data(f2, struct filter);

pthread_join(id[j++], NULL);

// thread creation might have failed
if (tmp->pcap_ctx)
filters_finish_thread(tmp);

pcap_freecode(tmp->bpf);
free(tmp->bpf);
free(tmp);
}

free(id);
list_free(ctx->filters);
}

void filters_add(filters_ctx *ctx, const char *id, const char* bpf_str) {
static void filters_bpfstr_unwind(filters_ctx *ctx, char *bpf_str)
{
char token[1024 + strlen("${}")];

list_foreach(ctx->filters, f) {
struct filter *tmp = list_data(f, struct filter);

snprintf(token, sizeof(token), "${%s}", tmp->id);
strnrepl(token, tmp->bpf_str, bpf_str, 4096);
}
}

static void filters_bpfstr_unwind_finish(filters_ctx *ctx)
{
list_foreach(ctx->filters, f)
free(list_data(f, struct filter)->bpf_str);
}

static void filters_prepare_pcap(struct filter *filter, const char* device, int epoll_fd) {
struct bpf_program bpf;
struct epoll_event event;
char errbuf[PCAP_ERRBUF_SIZE];

memset(&errbuf, 0, sizeof(errbuf));

filter->pcap_ctx = pcap_create(device, errbuf);
// TODO: there could be a warning in errbuf even if handle != NULL
if (!filter->pcap_ctx) {
fprintf(stderr, "Couldn't open device %s\n", errbuf);
exit(1);
}

if (pcap_set_snaplen(filter->pcap_ctx, 0)) {
fprintf(stderr, "Error at setting zero snaplen\n");
exit(1);
}

if (pcap_set_buffer_size(filter->pcap_ctx, BUFSIZ)) {
fprintf(stderr, "Error at setting pcap buffer size\n");
exit(1);
}

if (pcap_set_timeout(filter->pcap_ctx, 1000)) {
fprintf(stderr, "Error at setting pcap timeout\n");
exit(1);
}

if (pcap_setnonblock(filter->pcap_ctx, 1, errbuf) == PCAP_ERROR) {
fprintf(stderr, "Can't set pcap handler nonblocking.\n");
exit(1);
}

if (pcap_activate(filter->pcap_ctx)) {
fprintf(stderr, "Can't activate pcap handler.\n");
exit(1);
}

if (pcap_compile(filter->pcap_ctx, &bpf, filter->bpf_str, 0, PCAP_NETMASK_UNKNOWN) == -1) {
fprintf(stderr, "Error at compiling bpf \"%s\": %s\n", filter->bpf_str, pcap_geterr(filter->pcap_ctx));
exit(1);
}

if (pcap_setfilter(filter->pcap_ctx, &bpf)) {
pcap_freecode(&bpf);
fprintf(stderr, "Can't set pcap filter\n");
exit(1);
}

pcap_freecode(&bpf);

filter->fd = pcap_get_selectable_fd(filter->pcap_ctx);
if (filter->fd == PCAP_ERROR) {
fprintf(stderr, "Can't get file descriptor from pcap handler.");
exit(1);
}

memset(&event, 0, sizeof(event));
event.events = EPOLLIN;
event.data.ptr = filter;

if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, filter->fd, &event)) {
fprintf(stderr, "Can't add pcap to epoll.\n");
exit(1);
}
}



void filters_add(filters_ctx *ctx, const char *id, char *bpf_str, const char* device, int epoll_fd) {
struct filter *instance = malloc(sizeof(*instance));

// TODO: document maximum len of id
strncpy(instance->id, id, 1023);
instance->id[1023] = '\0';
instance->bpf = malloc(sizeof(struct bpf_program));
instance->bpf_str = bpf_str;

instance->packets_count = 0;
instance->bytes_count = 0;

if (pcap_compile(ctx->pcap_ctx, instance->bpf, bpf_str, 0, PCAP_NETMASK_UNKNOWN) == -1) {
fprintf(stderr, "Error at compiling bpf \"%s\": %s\n", bpf_str, pcap_geterr(ctx->pcap_ctx));
exit(1);
}
filters_prepare_pcap(instance, device, epoll_fd);

list_insert(ctx->filters, instance);
ctx->count++;
}

void filters_load(filters_ctx *ctx, const char *filterfile_path, const char *mac_addr) {
void filters_load(filters_ctx *ctx, const char *filterfile_path, const char *mac_addr, const char* device, int epoll_fd) {
// TODO: test with /dev/random as input

FILE *fp = fopen(filterfile_path, "r");
char *line = NULL;
size_t read = 0;
int line_no = 0;

fprintf(stderr, "Device: %s\n", device);

if (fp == NULL) {
fprintf(stderr, "Error while opening the filterfile '%s': ", filterfile_path);
perror("");
Expand Down Expand Up @@ -79,28 +196,31 @@ void filters_load(filters_ctx *ctx, const char *filterfile_path, const char *mac
//
// TODO: here is dump!!
char *bpf = malloc(4096);
strncpy(bpf, bpf_tmp, 4096);
strncpy(bpf, bpf_tmp, 4095);
strnrepl("$MAC", mac_addr, bpf, 4096);
filters_bpfstr_unwind(ctx, bpf);

fprintf(stderr, "id: %s; bpf: \"%s\";\n", id, bpf);

filters_add(ctx, id, bpf);

// the bpf is not needed anymore
free(bpf);
filters_add(ctx, id, bpf, device, epoll_fd);
}

filters_bpfstr_unwind_finish(ctx);
free(line);
fclose(fp);
}

void filters_process(filters_ctx *ctx, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
list_foreach(ctx->filters, f) {
struct filter *tmp = list_data(f, struct filter);
void filters_process(u_char *ptr, const struct pcap_pkthdr *pkthdr, const u_char *packet) {
struct filter *filter = (struct filter *)ptr;

if (pcap_offline_filter(tmp->bpf, pkthdr, packet)) {
tmp->packets_count += 1;
tmp->bytes_count += pkthdr->len;
}
filter->packets_count += 1;
filter->bytes_count += pkthdr->len;
}

void filters_break(filters_ctx *filters_ctx) {
list_foreach(filters_ctx->filters, f) {
struct filter *filter = list_data(f, struct filter);

pcap_breakloop(filter->pcap_ctx);
}
}
14 changes: 9 additions & 5 deletions filters.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@

struct filter {
char id[1024];
struct bpf_program *bpf;
char *bpf_str;
unsigned long long packets_count;
unsigned long long bytes_count;
pcap_t *pcap_ctx;
int fd;
};

typedef struct {
pcap_t *pcap_ctx;
struct list *filters;
int count;
} filters_ctx;

void filters_init(filters_ctx *ctx, pcap_t* pcap_ctx);
void filters_init(filters_ctx *ctx);
void filters_finish(filters_ctx *ctx);
void filters_add(filters_ctx *ctx, const char *id, const char* bpf_str);
void filters_load(filters_ctx *ctx, const char *filterfile_path, const char* mac_addr);
void filters_add(filters_ctx *ctx, const char *id, char *bpf_str, const char* device, int epoll_fd);
void filters_load(filters_ctx *ctx, const char *filterfile_path, const char* mac_addr, const char* device, int epoll_fd);

void filters_process(filters_ctx *ctx, const struct pcap_pkthdr *pkthdr, const u_char *packet);
void filters_process(u_char *ptr, const struct pcap_pkthdr *pkthdr, const u_char *packet);
void filters_break(filters_ctx *filters_ctx);

#endif
Loading