utils: nfbpf_compile

A BPF compiler to convert tcpdump expressions to the decimal format
accepted by the libxt_bpf.

Generate a file and pass that to iptables:

  nfbpf_compile RAW 'udp dst port 9000' > test.bpf
  iptables -A OUTPUT -m bpf --bytecode-file test.bpf -j LOG

Or pass the output directly to iptables using backticks:

  iptables -A INPUT -m bpf --bytecode \
      "`./nfbpf_compile RAW 'udp dst port 9000'" -j LOG

This utility depends on libpcap. The library is only compiled if the option
--enable-bpf-compiler is explicitly passed to ./configure and libpcap is
found.

Pablo has mangled the original patch to rename the utility to
nfbpf_compile. Also modified the output to match exactly what
-m bpf --bytecode needs.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
diff --git a/Makefile.am b/Makefile.am
index 6400ba4..c38d360 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -10,9 +10,7 @@
 if ENABLE_LIBIPQ
 SUBDIRS         += libipq
 endif
-if HAVE_LIBNFNETLINK
 SUBDIRS         += utils
-endif
 # Depends on libxtables:
 SUBDIRS         += extensions
 # Depends on extensions/libext.a:
diff --git a/configure.ac b/configure.ac
index 2ba5cec..0620466 100644
--- a/configure.ac
+++ b/configure.ac
@@ -50,6 +50,9 @@
 	[enable_devel="$enableval"], [enable_devel="yes"])
 AC_ARG_ENABLE([libipq],
 	AS_HELP_STRING([--enable-libipq], [Build and install libipq]))
+AC_ARG_ENABLE([bpf-compiler],
+	AS_HELP_STRING([--enable-bpf-compiler], [Build bpf compiler]),
+	[enable_bpfc="yes"])
 AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
 	[Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
 	[pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
@@ -88,6 +91,7 @@
 AM_CONDITIONAL([ENABLE_LARGEFILE], [test "$enable_largefile" = "yes"])
 AM_CONDITIONAL([ENABLE_DEVEL], [test "$enable_devel" = "yes"])
 AM_CONDITIONAL([ENABLE_LIBIPQ], [test "$enable_libipq" = "yes"])
+AM_CONDITIONAL([ENABLE_BPFC], [test "$enable_bpfc" = "yes"])
 
 PKG_CHECK_MODULES([libnfnetlink], [libnfnetlink >= 1.0],
 	[nfnetlink=1], [nfnetlink=0])
diff --git a/utils/Makefile.am b/utils/Makefile.am
index f1bbfc5..bfafd1d 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -4,7 +4,17 @@
 AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_builddir}/include \
               -I${top_srcdir}/include ${libnfnetlink_CFLAGS}
 
-sbin_PROGRAMS = nfnl_osf
-pkgdata_DATA = pf.os
+sbin_PROGRAMS =
+pkgdata_DATA =
+
+if HAVE_LIBNFNETLINK
+sbin_PROGRAMS += nfnl_osf
+pkgdata_DATA += pf.os
 
 nfnl_osf_LDADD = -lnfnetlink
+endif
+
+if ENABLE_BPFC
+sbin_PROGRAMS += nfbpf_compile
+nfbpf_compile_LDADD = -lpcap
+endif
diff --git a/utils/nfbpf_compile.c b/utils/nfbpf_compile.c
new file mode 100644
index 0000000..2c46c7b
--- /dev/null
+++ b/utils/nfbpf_compile.c
@@ -0,0 +1,55 @@
+/*
+ * BPF program compilation tool
+ *
+ * Generates decimal output, similar to `tcpdump -ddd ...`.
+ * Unlike tcpdump, will generate for any given link layer type.
+ *
+ * Written by Willem de Bruijn (willemb@google.com)
+ * Copyright Google, Inc. 2013
+ * Licensed under the GNU General Public License version 2 (GPLv2)
+*/
+
+#include <pcap.h>
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+	struct bpf_program program;
+	struct bpf_insn *ins;
+	int i, dlt = DLT_RAW;
+
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "Usage:    %s [link] '<program>'\n\n"
+				"          link is a pcap linklayer type:\n"
+				"          one of EN10MB, RAW, SLIP, ...\n\n"
+				"Examples: %s RAW 'tcp and greater 100'\n"
+				"          %s EN10MB 'ip proto 47'\n'",
+				argv[0], argv[0], argv[0]);
+		return 1;
+	}
+
+	if (argc == 3) {
+		dlt = pcap_datalink_name_to_val(argv[1]);
+		if (dlt == -1) {
+			fprintf(stderr, "Unknown datalinktype: %s\n", argv[1]);
+			return 1;
+		}
+	}
+
+	if (pcap_compile_nopcap(65535, dlt, &program, argv[argc - 1], 1,
+				PCAP_NETMASK_UNKNOWN)) {
+		fprintf(stderr, "Compilation error\n");
+		return 1;
+	}
+
+	printf("%d,", program.bf_len);
+	ins = program.bf_insns;
+	for (i = 0; i < program.bf_len-1; ++ins, ++i)
+		printf("%u %u %u %u,", ins->code, ins->jt, ins->jf, ins->k);
+
+	printf("%u %u %u %u\n", ins->code, ins->jt, ins->jf, ins->k);
+
+	pcap_freecode(&program);
+	return 0;
+}
+