From 1138c3724d1e2334985166405053291381e030c8 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 25 Jan 2013 15:26:17 +0100 Subject: [PATCH] netsniff-ng: also support tcpdump-like filters This is done either via: - netsniff-ng -i eth0 tcp and port 80 - netsniff-ng -i eth0 -f "tcp and port 80" -s -o dump.pcap We need to bind against libpcap from now on, but only to use its filter compiler, no need to reinvent the wheel. Also, people are familiar with the tcpdump syntax expression. Signed-off-by: Daniel Borkmann --- INSTALL | 1 + src/Makefile | 3 +-- src/bpf.c | 55 ++++++++++++++++++++++++++++++++++++++++++------ src/bpf.h | 4 ++-- src/bpf_parser.y | 2 +- src/netsniff-ng.c | 36 +++++++++++++++++++++---------- src/netsniff-ng/Makefile | 1 + 7 files changed, 80 insertions(+), 22 deletions(-) diff --git a/INSTALL b/INSTALL index 58a61837..a922efa5 100644 --- a/INSTALL +++ b/INSTALL @@ -20,6 +20,7 @@ What libraries are required? - libGeoIP >=1.4.8 (astraceroute, flowtop) - libnacl (curvetun) - libnetfilter-conntrack (flowtop) + - libpcap (netsniff-ng, for tcpdump-like filters) - liburcu (flowtop) - libnl3 (netsniff-ng, trafgen) diff --git a/src/Makefile b/src/Makefile index d4d17c8f..d570bac5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -228,7 +228,7 @@ $(foreach tool,$(TOOLS),$(eval $(call TOOL_templ,$(tool)))) %:: ; -netsniff-ng: ALL_CFLAGS += -I$(INCDIR)/libnl3/ -D__WITH_PROTOS +netsniff-ng: ALL_CFLAGS += -I$(INCDIR)/libnl3/ -D__WITH_PROTOS -D__WITH_TCPDUMP_LIKE_FILTER trafgen: ALL_CFLAGS += -I.. -I$(INCDIR)/libnl3/ -D__WITH_PROTOS bpfc: ALL_CFLAGS += -I.. curvetun: ALL_CFLAGS += -I ${NACL_INC_DIR} @@ -251,7 +251,6 @@ netsniff-ng_install_custom flowtop_install_custom: $(Q)$(foreach file,$(NCONF_FILES),$(call INST,configs/$(file),$(ETCDIRE));) astraceroute_install_custom: $(Q)$(call INST,configs/whois.conf,$(ETCDIRE)) - $(Q)$(call INSTX,../contrib/scripts/astraceroute6,$(SBINDIR)) $(TOOLS): WFLAGS += $(WFLAGS_EXTRA) $(TOOLS): diff --git a/src/bpf.c b/src/bpf.c index d5f810cb..24b428bf 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -125,7 +125,7 @@ static const char *bpf_dump_linux_k(uint32_t k) } } -static char *bpf_dump(const struct sock_filter bpf, int n) +static char *__bpf_dump(const struct sock_filter bpf, int n) { int v; const char *fmt, *op; @@ -351,7 +351,7 @@ 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)); + printf("%s\n", __bpf_dump(bpf->filter[i], i)); } void bpf_attach_to_sock(int sock, struct sock_fprog *bpf) @@ -394,7 +394,7 @@ int enable_kernel_bpf_jit_compiler(void) return ret; } -int bpf_validate(const struct sock_fprog *bpf) +int __bpf_validate(const struct sock_fprog *bpf) { uint32_t i, from; const struct sock_filter *p; @@ -715,7 +715,11 @@ uint32_t bpf_run_filter(const struct sock_fprog * fcode, uint8_t * packet, } } -void bpf_parse_rules(char *rulefile, struct sock_fprog *bpf) +#ifdef __WITH_TCPDUMP_LIKE_FILTER +# include +#endif + +void bpf_parse_rules(char *dev, char *rulefile, struct sock_fprog *bpf) { int ret; char buff[256]; @@ -731,7 +735,11 @@ void bpf_parse_rules(char *rulefile, struct sock_fprog *bpf) fp = fopen(rulefile, "r"); if (!fp) - panic("Cannot read BPF rule file!\n"); +#ifdef __WITH_TCPDUMP_LIKE_FILTER + goto try_compile_str; +#else + panic("Cannot open file %s!\n", rulefile); +#endif fmemset(buff, 0, sizeof(buff)); while (fgets(buff, sizeof(buff), fp) != NULL) { @@ -761,6 +769,41 @@ void bpf_parse_rules(char *rulefile, struct sock_fprog *bpf) fclose(fp); - if (bpf_validate(bpf) == 0) + if (__bpf_validate(bpf) == 0) panic("This is not a valid BPF program!\n"); + + return; +#ifdef __WITH_TCPDUMP_LIKE_FILTER +try_compile_str: +{ + int i; + struct bpf_program bpfp; + struct pcap *fd; + + /* For users, who want to have a tcpdump-sytle filter syntax */ + fd = pcap_open_live(dev, 60, 0, 1000, NULL); + if (!fd) + panic("Cannot open any device!\n"); + + ret = pcap_compile(fd, &bpfp, rulefile, 1, PCAP_NETMASK_UNKNOWN); + if (ret < 0) + panic("Cannot compile filter %s: %s\n", rulefile, pcap_geterr(fd)); + + pcap_close(fd); + + bpf->len = bpfp.bf_len; + bpf->filter = xrealloc(bpf->filter, 1, + bpf->len * sizeof(sf_single)); + + for (i = 0; i < bpf->len; ++i) { + bpf->filter[i].code = bpfp.bf_insns[i].code; + bpf->filter[i].jt = bpfp.bf_insns[i].jt; + bpf->filter[i].jf = bpfp.bf_insns[i].jf; + bpf->filter[i].k = bpfp.bf_insns[i].k; + + if (bpf->filter[i].code == 0x06 && bpf->filter[i].k > 0) + bpf->filter[i].k = 0xFFFFFFFF; + } +} +#endif } diff --git a/src/bpf.h b/src/bpf.h index 1937fb3e..acbf8f32 100644 --- a/src/bpf.h +++ b/src/bpf.h @@ -16,13 +16,13 @@ extern void bpf_dump_op_table(void); extern void bpf_dump_all(struct sock_fprog *bpf); -extern int bpf_validate(const struct sock_fprog *bpf); +extern int __bpf_validate(const struct sock_fprog *bpf); extern uint32_t bpf_run_filter(const struct sock_fprog *bpf, uint8_t *packet, size_t plen); extern void bpf_attach_to_sock(int sock, struct sock_fprog *bpf); extern void bpf_detach_from_sock(int sock); extern int enable_kernel_bpf_jit_compiler(void); -extern void bpf_parse_rules(char *rulefile, struct sock_fprog *bpf); +extern void bpf_parse_rules(char *dev, char *rulefile, struct sock_fprog *bpf); static inline void bpf_release(struct sock_fprog *bpf) { diff --git a/src/bpf_parser.y b/src/bpf_parser.y index 3a8af66e..865d4ac0 100644 --- a/src/bpf_parser.y +++ b/src/bpf_parser.y @@ -632,7 +632,7 @@ int compile_filter(char *file, int verbose, int bypass) fflush(stdout); } - if (bpf_validate(&res) == 0) { + if (__bpf_validate(&res) == 0) { if (verbose) whine("Semantic error! BPF validation " "failed!\n"); diff --git a/src/netsniff-ng.c b/src/netsniff-ng.c index c60beae1..b0e2c257 100644 --- a/src/netsniff-ng.c +++ b/src/netsniff-ng.c @@ -61,11 +61,8 @@ struct ctx { int cpu, rfraw, dump, print_mode, dump_dir, jumbo_support, packet_type, verbose; unsigned long kpull, dump_interval, reserve_size, tx_bytes, tx_packets; bool randomize, promiscuous, enforce; - enum pcap_ops_groups pcap; - enum dump_mode dump_mode; - uid_t uid; - gid_t gid; - uint32_t link_type; + enum pcap_ops_groups pcap; enum dump_mode dump_mode; + uid_t uid; gid_t gid; uint32_t link_type; }; volatile sig_atomic_t sigint = 0; @@ -224,7 +221,7 @@ static void pcap_to_xmit(struct ctx *ctx) size = ring_size(ctx->device_out, ctx->reserve_size); - bpf_parse_rules(ctx->filter, &bpf_ops); + bpf_parse_rules(ctx->device_out, ctx->filter, &bpf_ops); set_packet_loss_discard(tx_sock); set_sockopt_hwtimestamp(tx_sock, ctx->device_out); @@ -388,7 +385,7 @@ static void receive_to_xmit(struct ctx *ctx) enable_kernel_bpf_jit_compiler(); - bpf_parse_rules(ctx->filter, &bpf_ops); + bpf_parse_rules(ctx->device_in, ctx->filter, &bpf_ops); bpf_attach_to_sock(rx_sock, &bpf_ops); setup_rx_ring_layout(rx_sock, &rx_ring, size_in, ctx->jumbo_support); @@ -583,7 +580,7 @@ static void read_pcap(struct ctx *ctx) fmemset(&fm, 0, sizeof(fm)); fmemset(&bpf_ops, 0, sizeof(bpf_ops)); - bpf_parse_rules(ctx->filter, &bpf_ops); + bpf_parse_rules("any", ctx->filter, &bpf_ops); dissector_init_all(ctx->print_mode); @@ -851,7 +848,7 @@ static void recv_only_or_dump(struct ctx *ctx) enable_kernel_bpf_jit_compiler(); - bpf_parse_rules(ctx->filter, &bpf_ops); + bpf_parse_rules(ctx->device_in, ctx->filter, &bpf_ops); bpf_attach_to_sock(sock, &bpf_ops); set_sockopt_hwtimestamp(sock, ctx->device_in); @@ -1027,11 +1024,11 @@ static void help(void) { printf("\nnetsniff-ng %s, the packet sniffing beast\n", VERSION_STRING); puts("http://www.netsniff-ng.org\n\n" - "Usage: netsniff-ng [options]\n" + "Usage: netsniff-ng [options] [filter-expression]\n" "Options:\n" " -i|-d|--dev|--in Input source as netdev or pcap\n" " -o|--out Output sink as netdev, pcap, directory, trafgen file\n" - " -f|--filter Use BPF filter file from bpfc\n" + " -f|--filter Use BPF filter file from bpfc or tcpdump-like expression\n" " -t|--type Only handle packets of defined type:\n" " host|broadcast|multicast|others|outgoing\n" " -F|--interval Dump interval in time or size if -o is a directory\n" @@ -1331,6 +1328,23 @@ int main(int argc, char **argv) } } + if (!ctx.filter && optind != argc) { + int ret; + off_t offset = 0; + + for (i = optind; i < argc; ++i) { + size_t alen = strlen(argv[i]) + 2; + size_t flen = ctx.filter ? strlen(ctx.filter) : 0; + + ctx.filter = xrealloc(ctx.filter, 1, flen + alen); + ret = slprintf(ctx.filter + offset, strlen(argv[i]) + 2, "%s ", argv[i]); + if (ret < 0) + panic("Cannot concatenate filter string!\n"); + else + offset += ret; + } + } + if (!ctx.device_in) ctx.device_in = xstrdup("any"); diff --git a/src/netsniff-ng/Makefile b/src/netsniff-ng/Makefile index acd3289e..2f0bad5a 100644 --- a/src/netsniff-ng/Makefile +++ b/src/netsniff-ng/Makefile @@ -1,5 +1,6 @@ netsniff-ng-libs = -lnl-genl-3 \ -lnl-3 \ + -lpcap \ -lpthread netsniff-ng-objs = hash.o \ -- 2.11.4.GIT