diff --git a/configure.in b/configure.in index 82777730c5..d42a6f7d26 100644 --- a/configure.in +++ b/configure.in @@ -413,6 +413,7 @@ case $host in *) AC_CHECK_LIB(netfilter_queue, nfq_open,, NFQ="no",) AC_CHECK_LIB([netfilter_queue], [nfq_set_queue_maxlen],AC_DEFINE_UNQUOTED([HAVE_NFQ_MAXLEN],[1],[Found queue max length support in netfilter_queue]) ,,[-lnfnetlink]) + AC_CHECK_LIB([netfilter_queue], [nfq_set_verdict2],AC_DEFINE_UNQUOTED([HAVE_NFQ_SET_VERDICT2],[1],[Found nfq_set_verdict2 function in netfilter_queue]) ,,[-lnfnetlink]) ;; diff --git a/src/source-nfq.c b/src/source-nfq.c index e652d0d008..5d1acb7239 100644 --- a/src/source-nfq.c +++ b/src/source-nfq.c @@ -32,6 +32,8 @@ #include "decode.h" #include "packet-queue.h" #include "threads.h" +#include "conf.h" +#include "conf-yaml-loader.h" #include "threadvars.h" #include "tm-queuehandlers.h" #include "tm-modules.h" @@ -122,6 +124,14 @@ TmEcode VerdictNFQThreadDeinit(ThreadVars *, void *); TmEcode DecodeNFQ(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); TmEcode DecodeNFQThreadInit(ThreadVars *, void *, void **); +typedef struct NFQCnf_ { + int repeat_mode; + uint32_t mark; + uint32_t mask; +} NFQCnf; + +NFQCnf nfq_config; + void TmModuleReceiveNFQRegister (void) { /* XXX create a general NFQ setup function */ memset(&nfq_g, 0, sizeof(nfq_g)); @@ -153,6 +163,41 @@ void TmModuleDecodeNFQRegister (void) { tmm_modules[TMM_DECODENFQ].RegisterTests = NULL; } +/** \brief To initialize the NFQ global configuration data + * + * \param quiet It tells the mode of operation, if it is TRUE nothing will + * be get printed. + */ +void NFQInitConfig(char quiet) +{ + intmax_t value = 0; + + SCLogDebug("Initializing NFQ"); + + memset(&nfq_config, 0, sizeof(nfq_config)); + if ((ConfGetBool("nfq.repeat_mode", &nfq_config.repeat_mode)) == 0) { + nfq_config.repeat_mode = FALSE; + } + + if ((ConfGetInt("nfq.mark", &value)) == 1) { + nfq_config.mark = (uint32_t)value; + } + + if ((ConfGetInt("nfq.mask", &value)) == 1) { + nfq_config.mask = (uint32_t)value; + } + + if (!quiet) { + if (nfq_config.repeat_mode == TRUE) { + SCLogInfo("NFQ running in REPEAT mode with mark %"PRIu32"/%"PRIu32, + nfq_config.mark, nfq_config.mask); + } else { + SCLogInfo("NFQ running in standard ACCEPT/DROP mode"); + } + } + +} + void NFQSetupPkt (Packet *p, void *data) { struct nfq_data *tb = (struct nfq_data *)data; @@ -656,20 +701,48 @@ void NFQSetVerdict(Packet *p) { t->dropped++; #endif /* COUNTERS */ } else { - verdict = NF_ACCEPT; + if (nfq_config.repeat_mode == FALSE) { + verdict = NF_ACCEPT; + } else { + verdict = NF_REPEAT; + } #ifdef COUNTERS t->accepted++; #endif /* COUNTERS */ } SCMutexLock(&t->mutex_qh); - if (p->flags & PKT_STREAM_MODIFIED) { - ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, GET_PKT_LEN(p), GET_PKT_DATA(p)); + if (nfq_config.repeat_mode == FALSE) { + if (p->flags & PKT_STREAM_MODIFIED) { + ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, GET_PKT_LEN(p), GET_PKT_DATA(p)); #ifdef COUNTERS - t->replaced++; + t->replaced++; #endif /* COUNTERS */ + } else { + ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, 0, NULL); + } } else { - ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, 0, NULL); +#ifdef HAVE_NFQ_SET_VERDICT2 + if (p->flags & PKT_STREAM_MODIFIED) { + ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, + (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask), + GET_PKT_LEN(p), GET_PKT_DATA(p)); + } else { + ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, + (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask), + 0, NULL); + } +#else /* fall back to old function */ + if (p->flags & PKT_STREAM_MODIFIED) { + ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, + htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)), + GET_PKT_LEN(p), GET_PKT_DATA(p)); + } else { + ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, + htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)), + 0, NULL); + } +#endif } SCMutexUnlock(&t->mutex_qh); diff --git a/src/source-nfq.h b/src/source-nfq.h index f4918338bd..8b5073ac18 100644 --- a/src/source-nfq.h +++ b/src/source-nfq.h @@ -96,6 +96,7 @@ typedef struct NFQGlobalVars_ char unbind; } NFQGlobalVars; +void NFQInitConfig(char quiet); int NFQRegisterQueue(char *queue); int NFQGetQueueCount(void); void *NFQGetQueue(int number); diff --git a/src/suricata.c b/src/suricata.c index 2688ef08df..e01fce2c24 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -836,6 +836,11 @@ int main(int argc, char **argv) } SCLogDebug("Default packet size set to %"PRIiMAX, default_packet_size); +#ifdef NFQ + if (run_mode == MODE_NFQ) + NFQInitConfig(FALSE); +#endif + /* Since our config is now loaded we can finish configurating the * logging module. */ SCLogLoadConfig(); diff --git a/suricata.yaml b/suricata.yaml index 8d99a7367f..c7ddcf44d3 100644 --- a/suricata.yaml +++ b/suricata.yaml @@ -96,6 +96,16 @@ outputs: filename: stats.log interval: 8 +# When running in NFQ inline mode, it is possible to use a simulated +# non-terminal NFQUEUE verdict. +# This permit to do send all needed packet to suricata via this a rule: +# iptables -I FORWARD -m mark ! --mark $MARK/$MASK -j NFQUEUE +# And below, you can have your standard filtering ruleset. +nfq: +# repeat_mode: true +# mark: 1 +# mask: 1 + defrag: max-frags: 65535 prealloc: yes