diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index d97ac33668..f95f7156c6 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -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 { diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 24e6caf1c9..a0cf79a68f 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -72,6 +72,10 @@ #include #include #include + +#include +#include +#include #endif #include @@ -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); } diff --git a/src/source-af-packet.h b/src/source-af-packet.h index 487ed23f45..40c1d92c1b 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -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; diff --git a/suricata.yaml.in b/suricata.yaml.in index 316238db03..7cfe24f1fc 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -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