pppd: Implement support for precompiled pcap filters

This patch implements support for precompiled pcap filters which is useful to
support dial-on-demand on memory constrained embedded devices without having
to link the full libpcap into pppd to generate the filters during runtime.

Two new options are introduced; "precompiled-pass-filter" specifies a pre-
compiled filter file containing rules to match packets which should be passed,
"precompiled-active-filter" specifies a filter file containing rules to match
packets which are treated as active.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>

--- a/pppd/Makefile.linux
+++ b/pppd/Makefile.linux
@@ -51,6 +51,9 @@ MPPE=y
 # and that the kernel driver support PPP packet filtering.
 #FILTER=y
 
+# Support for precompiled filters
+PRECOMPILED_FILTER=y
+
 # Uncomment the next line to enable multilink PPP (enabled by default)
 # Linux distributions: Please leave multilink ENABLED in your builds
 # of pppd!
@@ -214,6 +217,14 @@ LIBS    += -lpcap -L$(STAGING_DIR)/usr/l
 CFLAGS  += -DPPP_FILTER -I$(STAGING_DIR)/usr/include
 endif
 
+ifdef PRECOMPILED_FILTER
+PPPDSRCS += pcap_pcc.c
+HEADERS  += pcap_pcc.h
+PPPDOBJS += pcap_pcc.o
+LIBS	+= $(STAGING_DIR)/usr/lib/libpcap.a
+CFLAGS	+= -DPPP_FILTER -DPPP_PRECOMPILED_FILTER -I$(STAGING_DIR)/usr/include
+endif
+
 ifdef HAVE_INET6
      PPPDSRCS += ipv6cp.c eui64.c
      HEADERS  += ipv6cp.h eui64.h
--- a/pppd/options.c
+++ b/pppd/options.c
@@ -56,6 +56,7 @@
 
 #ifdef PPP_FILTER
 #include <pcap.h>
+#include <pcap-bpf.h>
 /*
  * There have been 3 or 4 different names for this in libpcap CVS, but
  * this seems to be what they have settled on...
@@ -168,6 +169,13 @@ static int setlogfile(char **);
 static int loadplugin(char **);
 #endif
 
+#ifdef PPP_PRECOMPILED_FILTER
+#include "pcap_pcc.h"
+static int setprecompiledpassfilter(char **);
+static int setprecompiledactivefilter(char **);
+#undef PPP_FILTER
+#endif
+
 #ifdef PPP_FILTER
 static int setpassfilter(char **);
 static int setactivefilter(char **);
@@ -360,6 +368,14 @@ option_t general_options[] = {
       "set filter for active pkts", OPT_PRIO },
 #endif
 
+#ifdef PPP_PRECOMPILED_FILTER
+    { "precompiled-pass-filter", 1, setprecompiledpassfilter,
+      "set precompiled filter for packets to pass", OPT_PRIO },
+
+    { "precompiled-active-filter", 1, setprecompiledactivefilter,
+      "set precompiled filter for active pkts", OPT_PRIO },
+#endif
+
 #ifdef MAXOCTETS
     { "maxoctets", o_int, &maxoctets,
       "Set connection traffic limit",
@@ -1468,6 +1484,27 @@ callfile(char **argv)
     return ok;
 }
 
+#ifdef PPP_PRECOMPILED_FILTER
+/*
+ * setprecompiledpassfilter - Set the pass filter for packets using a
+ * precompiled expression
+ */
+static int
+setprecompiledpassfilter(char **argv)
+{
+    return pcap_pre_compiled (*argv, &pass_filter);
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setprecompiledactivefilter(char **argv)
+{
+    return pcap_pre_compiled (*argv, &active_filter);
+}
+#endif
+
 #ifdef PPP_FILTER
 /*
  * setpassfilter - Set the pass filter for packets
--- /dev/null
+++ b/pppd/pcap_pcc.c
@@ -0,0 +1,74 @@
+#include <pcap.h>
+#include <pcap-bpf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "pppd.h"
+
+int pcap_pre_compiled (char * fname, struct bpf_program *p)
+{
+    char buf[128];
+    int line = 0, size = 0, index=0, ret=1;
+    FILE *f = fopen (fname, "r");
+    if (!f)
+    {
+       option_error("error opening precompiled active-filter '%s': %s",
+                    fname, strerror (errno));
+       return 0;
+    }
+    while (fgets (buf, 127, f))
+    {
+       line++;
+       if (*buf == '#')
+           continue;
+       if (size)
+       {
+           /*
+             struct bpf_insn {
+             u_short   code;
+             u_char    jt;
+             u_char    jf;
+             bpf_int32 k;
+             }
+           */
+           struct bpf_insn * insn = & p->bf_insns[index];
+           unsigned code, jt, jf, k;
+           if (sscanf (buf, "%u %u %u %u", &code, &jt, &jf, &k) != 4)
+           {
+               goto err;
+           }
+           insn->code = code;
+           insn->jt = jt;
+           insn->jf = jf;
+           insn->k  = k;
+           index++;
+       }
+       else
+       {
+           if (sscanf (buf, "%u", &size) != 1)
+           {
+               goto err;
+           }
+           p->bf_len = size;
+           p->bf_insns = (struct bpf_insn *) 
+               malloc (size * sizeof (struct bpf_insn));
+       }
+    } 
+    if (size != index)
+    {
+       option_error("error in precompiled active-filter,"
+                    " expected %d expressions, got %dn",
+                    size, index);
+       ret = 0;
+    }
+    fclose(f);
+    return ret;
+
+err:
+  option_error("error in precompiled active-filter"
+              " expression line %s:%d (wrong size)\n", 
+              fname, line);
+  fclose (f);
+  return 0;
+}
--- /dev/null
+++ b/pppd/pcap_pcc.h
@@ -0,0 +1,7 @@
+#ifndef PCAP_PCC_H
+#define PCAP_PCC_H
+
+#include <pcap.h>
+
+int pcap_pre_compiled (char * fname, struct bpf_program *p);
+#endif /* PCAP_PCC_H */
