]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
af-packet: add support for BPF filter.
authorEric Leblond <eric@regit.org>
Mon, 11 Jun 2012 14:06:13 +0000 (16:06 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 29 Jun 2012 11:10:02 +0000 (13:10 +0200)
This patch adds support for BPF in AF_PACKET running
mode. The command line syntax is the same as the one
used of PF_RING.
The method is the same too: The pcap_compile__nopcap()
function is used to build the BPF filter. It is then
injected into the kernel with a setsockopt() call. If
the adding of the BPF fail, suricata exit.

src/runmode-af-packet.c
src/source-af-packet.c
src/source-af-packet.h
suricata.yaml.in

index d97ac336685b79a046b700dea8d6dcbaa0ad0bed..f95f7156c6797c38861c8329450d6123af7bfe2a 100644 (file)
@@ -109,6 +109,7 @@ void *ParseAFPConfig(const char *iface)
     char *tmpctype;
     intmax_t value;
     int boolval;
+    char *bpf_filter = NULL;
 
     if (aconf == NULL) {
         return NULL;
@@ -130,6 +131,15 @@ void *ParseAFPConfig(const char *iface)
     aconf->checksum_mode = CHECKSUM_VALIDATION_KERNEL;
     aconf->DerefFunc = AFPDerefConfig;
     aconf->flags = 0;
+    aconf->bpf_filter = NULL;
+
+    if (ConfGet("bpf-filter", &bpf_filter) == 1) {
+        if (strlen(bpf_filter) > 0) {
+            aconf->bpf_filter = bpf_filter;
+            SCLogInfo("Going to use command-line provided bpf filter '%s'",
+                       aconf->bpf_filter);
+        }
+    }
 
     /* Find initial node */
     af_packet_node = ConfGetNode("af-packet");
@@ -196,6 +206,17 @@ void *ParseAFPConfig(const char *iface)
         return NULL;
     }
 
+    /*load af_packet bpf filter*/
+    /* command line value has precedence */
+    if (ConfGet("bpf-filter", &bpf_filter) != 1) {
+        if (ConfGetChildValue(if_root, "bpf-filter", &bpf_filter) == 1) {
+            if (strlen(bpf_filter) > 0) {
+                aconf->bpf_filter = bpf_filter;
+                SCLogInfo("Going to use bpf filter %s", aconf->bpf_filter);
+            }
+        }
+    }
+
     if ((ConfGetChildValueInt(if_root, "buffer-size", &value)) == 1) {
         aconf->buffer_size = value;
     } else {
index 24e6caf1c91c74e8e7cecf1d25e711e3e56fd725..a0cf79a68f13b08238c7b7ab9194eb73c55a0e4f 100644 (file)
 #include <linux/if_ether.h>
 #include <linux/if_packet.h>
 #include <linux/if_arp.h>
+
+#include <pcap/pcap.h>
+#include <pcap/bpf.h>
+#include <linux/filter.h>
 #endif
 
 #include <sys/mman.h>
@@ -131,6 +135,9 @@ TmEcode NoAFPSupportExit(ThreadVars *tv, void *initdata, void **data)
 
 #define POLL_TIMEOUT 100
 
+/** protect pfring_set_bpf_filter, as it is not thread safe */
+static SCMutex afpacket_bpf_set_filter_lock = PTHREAD_MUTEX_INITIALIZER;
+
 enum {
     AFP_READ_OK,
     AFP_READ_FAILURE,
@@ -170,6 +177,9 @@ typedef struct AFPThreadVars_
     char iface[AFP_IFACE_NAME_LENGTH];
     LiveDevice *livedev;
 
+    /* Filter */
+    char *bpf_filter;
+
     /* socket buffer size */
     int buffer_size;
     int promisc;
@@ -199,6 +209,8 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot);
 TmEcode DecodeAFPThreadInit(ThreadVars *, void *, void **);
 TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
 
+TmEcode AFPSetBPFFilter(AFPThreadVars *ptv);
+
 /**
  * \brief Registration Function for RecieveAFP.
  * \todo Unit tests are needed for this module.
@@ -851,11 +863,64 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose)
             ptv->cooked = 1;
     }
 
+    TmEcode rc;
+    rc = AFPSetBPFFilter(ptv);
+    if (rc == TM_ECODE_FAILED) {
+        SCLogError(SC_ERR_AFP_CREATE, "Set AF_PACKET bpf filter \"%s\" failed.", ptv->bpf_filter);
+        return -1;
+    }
+
     /* Init is ok */
     ptv->afp_state = AFP_STATE_UP;
     return 0;
 }
 
+TmEcode AFPSetBPFFilter(AFPThreadVars *ptv)
+{
+    struct bpf_program filter;
+    struct sock_fprog  fcode;
+    int rc;
+
+    if (!ptv->bpf_filter)
+        return TM_ECODE_OK;
+
+    SCMutexLock(&afpacket_bpf_set_filter_lock);
+
+    SCLogInfo("Using BPF '%s' on iface '%s'",
+              ptv->bpf_filter,
+              ptv->iface);
+    if (pcap_compile_nopcap(default_packet_size,  /* snaplen_arg */
+                ptv->datalink,    /* linktype_arg */
+                &filter,       /* program */
+                ptv->bpf_filter, /* const char *buf */
+                0,             /* optimize */
+                0              /* mask */
+                ) == -1) {
+        SCLogError(SC_ERR_AFP_CREATE, "Filter compilation failed.");
+        SCMutexUnlock(&afpacket_bpf_set_filter_lock);
+        return TM_ECODE_FAILED;
+    }
+    SCMutexUnlock(&afpacket_bpf_set_filter_lock);
+
+    if (filter.bf_insns == NULL) {
+        SCLogError(SC_ERR_AFP_CREATE, "Filter badly setup.");
+        return TM_ECODE_FAILED;
+    }
+
+    fcode.len    = filter.bf_len;
+    fcode.filter = (struct sock_filter*)filter.bf_insns;
+
+    rc = setsockopt(ptv->socket, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof(fcode));
+
+    if(rc == -1) {
+        SCLogError(SC_ERR_AFP_CREATE, "Failed to attach filter: %s", strerror(errno));
+        return TM_ECODE_FAILED;
+    }
+
+    SCMutexUnlock(&afpacket_bpf_set_filter_lock);
+    return TM_ECODE_OK;
+}
+
 
 /**
  * \brief Init function for ReceiveAFP.
@@ -914,6 +979,10 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) {
 #endif
     ptv->flags = afpconfig->flags;
 
+    if (afpconfig->bpf_filter) {
+        ptv->bpf_filter = afpconfig->bpf_filter;
+    }
+
     char *active_runmode = RunmodeGetActive();
 
     if (active_runmode && !strcmp("workers", active_runmode)) {
@@ -941,7 +1010,6 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) {
     ptv->datalen = T_DATA_SIZE;
 #undef T_DATA_SIZE
 
-
     *data = (void *)ptv;
 
     afpconfig->DerefFunc(afpconfig);
@@ -987,6 +1055,8 @@ TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) {
     }
     ptv->datalen = 0;
 
+    ptv->bpf_filter = NULL;
+
     close(ptv->socket);
     SCReturnInt(TM_ECODE_OK);
 }
index 487ed23f453ea281586348f5276ebba620c133a9..40c1d92c1b6ee8121dae81821e3ad224b227954f 100644 (file)
@@ -60,6 +60,7 @@ typedef struct AFPIfaceConfig_
     /* misc use flags including ring mode */
     int flags;
     ChecksumValidationMode checksum_mode;
+    char *bpf_filter;
     SC_ATOMIC_DECLARE(unsigned int, ref);
     void (*DerefFunc)(void *);
 } AFPIfaceConfig;
index 316238db030c4f16962a6ca0db31acf3eaea6074..7cfe24f1fc3d5fee7cfff01f65b1a351473f5b52 100644 (file)
@@ -233,6 +233,8 @@ af-packet:
     #  checksum off-loading is used.
     # Warning: 'checksum-validation' must be set to yes to have any validation
     #checksum-checks: kernel
+    # BPF filter to apply to this interface. The pcap filter syntax apply here.
+    #bpf-filter: port 80 or udp
   - interface: eth1
     threads: 1
     cluster-id: 98