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])
AC_CHECK_LIB([netfilter_queue], [nfq_set_queue_flags],AC_DEFINE_UNQUOTED([HAVE_NFQ_SET_QUEUE_FLAGS],[1],[Found nfq_set_queue_flags function in netfilter_queue]) ,,[-lnfnetlink])
+ AC_CHECK_LIB([netfilter_queue], [nfq_set_verdict_batch],AC_DEFINE_UNQUOTED([HAVE_NFQ_SET_VERDICT_BATCH],[1],[Found nfq_set_verdict_batch function in netfilter_queue]) ,,[-lnfnetlink])
# check if the argument to nfq_get_payload is signed or unsigned
AC_MSG_CHECKING([for signed nfq_get_payload payload argument])
uint32_t mask;
uint32_t next_queue;
uint32_t flags;
+ uint8_t batchcount;
} NFQCnf;
NFQCnf nfq_config;
nfq_config.flags |= NFQ_FLAG_FAIL_OPEN;
#else
SCLogError(SC_ERR_NFQ_NOSUPPORT,
- "nfq.fail-open set but NFQ library has no support for it.");
+ "nfq.%s set but NFQ library has no support for it.", "fail-open");
#endif
}
nfq_config.next_queue = ((uint32_t)value) << 16;
}
+ if ((ConfGetInt("nfq.batchcount", &value)) == 1) {
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (value > 255) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "nfq.batchcount cannot exceed 255.");
+ value = 255;
+ }
+ if (value > 1)
+ nfq_config.batchcount = (uint8_t) (value - 1);
+#else
+ SCLogError(SC_ERR_NFQ_NOSUPPORT,
+ "nfq.%s set but NFQ library has no support for it.", "batchcount");
+ exit(EXIT_FAILURE);
+#endif
+ }
+
if (!quiet) {
switch (nfq_config.mode) {
case NFQ_ACCEPT_MODE:
}
+static uint8_t NFQVerdictCacheLen(NFQQueueVars *t)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ return t->verdict_cache.len;
+#else
+ return 0;
+#endif
+}
+
+static void NFQVerdictCacheFlush(NFQQueueVars *t)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ int ret;
+ int iter = 0;
+
+ do {
+ if (t->verdict_cache.mark_valid)
+ ret = nfq_set_verdict_batch2(t->qh,
+ t->verdict_cache.packet_id,
+ t->verdict_cache.verdict,
+ t->verdict_cache.mark);
+ else
+ ret = nfq_set_verdict_batch(t->qh,
+ t->verdict_cache.packet_id,
+ t->verdict_cache.verdict);
+ } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
+
+ if (ret < 0) {
+ SCLogWarning(SC_ERR_NFQ_SET_VERDICT, "nfq_set_verdict_batch failed");
+ } else {
+ t->verdict_cache.len = 0;
+ t->verdict_cache.mark_valid = 0;
+ }
+#endif
+}
+
+static int NFQVerdictCacheAdd(NFQQueueVars *t, Packet *p, uint32_t verdict)
+{
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (t->verdict_cache.maxlen == 0)
+ return -1;
+
+ if (p->flags & PKT_STREAM_MODIFIED || verdict == NF_DROP)
+ goto flush;
+
+ if (p->flags & PKT_MARK_MODIFIED) {
+ if (!t->verdict_cache.mark_valid) {
+ if (t->verdict_cache.len)
+ goto flush;
+ t->verdict_cache.mark_valid = 1;
+ t->verdict_cache.mark = p->nfq_v.mark;
+ } else if (t->verdict_cache.mark != p->nfq_v.mark) {
+ goto flush;
+ }
+ } else if (t->verdict_cache.mark_valid) {
+ goto flush;
+ }
+
+ if (t->verdict_cache.len == 0) {
+ t->verdict_cache.verdict = verdict;
+ } else if (t->verdict_cache.verdict != verdict)
+ goto flush;
+
+ /* same verdict, mark not set or identical -> can cache */
+ t->verdict_cache.packet_id = p->nfq_v.id;
+
+ if (t->verdict_cache.len >= t->verdict_cache.maxlen)
+ NFQVerdictCacheFlush(t);
+ else
+ t->verdict_cache.len++;
+ return 0;
+ flush:
+ /* can't cache. Flush current cache and signal caller it should send single verdict */
+ if (NFQVerdictCacheLen(t) > 0)
+ NFQVerdictCacheFlush(t);
+#endif
+ return -1;
+}
+
static inline void NFQMutexInit(NFQQueueVars *nq)
{
char *active_runmode = RunmodeGetActive();
}
#endif
+#ifdef HAVE_NFQ_SET_VERDICT_BATCH
+ if (runmode_workers) {
+ nfq_q->verdict_cache.maxlen = nfq_config.batchcount;
+ } else if (nfq_config.batchcount) {
+ SCLogError(SC_ERR_INVALID_ARGUMENT, "nfq.batchcount is only valid in workers runmode.");
+ }
+#endif
+
/* set a timeout to the socket so we can check for a signal
* in case we don't get packets for a longer period. */
tv.tv_sec = 1;
#ifndef OS_WIN32
void NFQRecvPkt(NFQQueueVars *t, NFQThreadVars *tv) {
int rv, ret;
+ int flag = NFQVerdictCacheLen(t) ? MSG_DONTWAIT : 0;
/* XXX what happens on rv == 0? */
- rv = recv(t->fd, tv->data, tv->datalen, 0);
+ rv = recv(t->fd, tv->data, tv->datalen, flag);
if (rv < 0) {
if (errno == EINTR || errno == EWOULDBLOCK) {
/* no error on timeout */
+ if (flag)
+ NFQVerdictCacheFlush(t);
} else {
#ifdef COUNTERS
NFQMutexLock(t);
#endif /* COUNTERS */
}
+ ret = NFQVerdictCacheAdd(t, p, verdict);
+ if (ret == 0) {
+ NFQMutexUnlock(t);
+ return TM_ECODE_OK;
+ }
+
do {
switch (nfq_config.mode) {
default: