You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/source-nfq.c

1324 lines
39 KiB
C

/* Copyright (C) 2007-2014 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
* \author Eric Leblond <eric@regit.org>
*
* Netfilter's netfilter_queue support for reading packets from the
* kernel and setting verdicts back to it (inline mode).
* Supported on Linux and Windows.
*
* \todo test if Receive and Verdict if both are present
*/
#include "suricata-common.h"
#include "suricata.h"
#include "decode.h"
#include "packet-queue.h"
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
15 years ago
#include "threads.h"
Add per packet profiling. Per packet profiling uses tick based accounting. It has 2 outputs, a summary and a csv file that contains per packet stats. Stats per packet include: 1) total ticks spent 2) ticks spent per individual thread module 3) "threading overhead" which is simply calculated by subtracting (2) of (1). A number of changes were made to integrate the new code in a clean way: a number of generic enums are now placed in tm-threads-common.h so we can include them from any part of the engine. Code depends on --enable-profiling just like the rule profiling code. New yaml parameters: profiling: # packet profiling packets: # Profiling can be disabled here, but it will still have a # performance impact if compiled in. enabled: yes filename: packet_stats.log append: yes # per packet csv output csv: # Output can be disabled here, but it will still have a # performance impact if compiled in. enabled: no filename: packet_stats.csv Example output of summary stats: IP ver Proto cnt min max avg ------ ----- ------ ------ ---------- ------- IPv4 6 19436 11448 5404365 32993 IPv4 256 4 11511 49968 30575 Per Thread module stats: Thread Module IP ver Proto cnt min max avg ------------------------ ------ ----- ------ ------ ---------- ------- TMM_DECODEPCAPFILE IPv4 6 19434 1242 47889 1770 TMM_DETECT IPv4 6 19436 1107 137241 1504 TMM_ALERTFASTLOG IPv4 6 19436 90 1323 155 TMM_ALERTUNIFIED2ALERT IPv4 6 19436 108 1359 138 TMM_ALERTDEBUGLOG IPv4 6 19436 90 1134 154 TMM_LOGHTTPLOG IPv4 6 19436 414 5392089 7944 TMM_STREAMTCP IPv4 6 19434 828 1299159 19438 The proto 256 is a counter for handling of pseudo/tunnel packets. Example output of csv: pcap_cnt,ipver,ipproto,total,TMM_DECODENFQ,TMM_VERDICTNFQ,TMM_RECEIVENFQ,TMM_RECEIVEPCAP,TMM_RECEIVEPCAPFILE,TMM_DECODEPCAP,TMM_DECODEPCAPFILE,TMM_RECEIVEPFRING,TMM_DECODEPFRING,TMM_DETECT,TMM_ALERTFASTLOG,TMM_ALERTFASTLOG4,TMM_ALERTFASTLOG6,TMM_ALERTUNIFIEDLOG,TMM_ALERTUNIFIEDALERT,TMM_ALERTUNIFIED2ALERT,TMM_ALERTPRELUDE,TMM_ALERTDEBUGLOG,TMM_ALERTSYSLOG,TMM_LOGDROPLOG,TMM_ALERTSYSLOG4,TMM_ALERTSYSLOG6,TMM_RESPONDREJECT,TMM_LOGHTTPLOG,TMM_LOGHTTPLOG4,TMM_LOGHTTPLOG6,TMM_PCAPLOG,TMM_STREAMTCP,TMM_DECODEIPFW,TMM_VERDICTIPFW,TMM_RECEIVEIPFW,TMM_RECEIVEERFFILE,TMM_DECODEERFFILE,TMM_RECEIVEERFDAG,TMM_DECODEERFDAG,threading 1,4,6,172008,0,0,0,0,0,0,47889,0,0,48582,1323,0,0,0,0,1359,0,1134,0,0,0,0,0,8028,0,0,0,49356,0,0,0,0,0,0,0,14337 First line of the file contains labels. 2 example gnuplot scripts added to plot the data.
15 years ago
#include "threadvars.h"
#include "tm-threads.h"
#include "tm-queuehandlers.h"
#include "tmqh-packetpool.h"
#include "conf.h"
#include "config.h"
#include "conf-yaml-loader.h"
#include "source-nfq-prototypes.h"
#include "action-globals.h"
#include "util-debug.h"
#include "util-error.h"
17 years ago
#include "util-byte.h"
#include "util-privs.h"
#include "util-device.h"
#include "runmodes.h"
#include "source-nfq.h"
#ifndef NFQ
/** Handle the case where no NFQ support is compiled in.
*
*/
TmEcode NoNFQSupportExit(ThreadVars *, void *, void **);
void TmModuleReceiveNFQRegister (void)
{
tmm_modules[TMM_RECEIVENFQ].name = "ReceiveNFQ";
tmm_modules[TMM_RECEIVENFQ].ThreadInit = NoNFQSupportExit;
tmm_modules[TMM_RECEIVENFQ].Func = NULL;
tmm_modules[TMM_RECEIVENFQ].ThreadExitPrintStats = NULL;
tmm_modules[TMM_RECEIVENFQ].ThreadDeinit = NULL;
tmm_modules[TMM_RECEIVENFQ].RegisterTests = NULL;
tmm_modules[TMM_RECEIVENFQ].cap_flags = SC_CAP_NET_ADMIN;
tmm_modules[TMM_RECEIVENFQ].flags = TM_FLAG_RECEIVE_TM;
}
void TmModuleVerdictNFQRegister (void)
{
tmm_modules[TMM_VERDICTNFQ].name = "VerdictNFQ";
tmm_modules[TMM_VERDICTNFQ].ThreadInit = NoNFQSupportExit;
tmm_modules[TMM_VERDICTNFQ].Func = NULL;
tmm_modules[TMM_VERDICTNFQ].ThreadExitPrintStats = NULL;
tmm_modules[TMM_VERDICTNFQ].ThreadDeinit = NULL;
tmm_modules[TMM_VERDICTNFQ].RegisterTests = NULL;
tmm_modules[TMM_VERDICTNFQ].cap_flags = SC_CAP_NET_ADMIN;
}
void TmModuleDecodeNFQRegister (void)
{
tmm_modules[TMM_DECODENFQ].name = "DecodeNFQ";
tmm_modules[TMM_DECODENFQ].ThreadInit = NoNFQSupportExit;
tmm_modules[TMM_DECODENFQ].Func = NULL;
tmm_modules[TMM_DECODENFQ].ThreadExitPrintStats = NULL;
tmm_modules[TMM_DECODENFQ].ThreadDeinit = NULL;
tmm_modules[TMM_DECODENFQ].RegisterTests = NULL;
tmm_modules[TMM_DECODENFQ].cap_flags = 0;
tmm_modules[TMM_DECODENFQ].flags = TM_FLAG_DECODE_TM;
}
TmEcode NoNFQSupportExit(ThreadVars *tv, void *initdata, void **data)
{
SCLogError(SC_ERR_NFQ_NOSUPPORT,"Error creating thread %s: you do not have support for nfqueue "
"enabled please recompile with --enable-nfqueue", tv->name);
exit(EXIT_FAILURE);
}
#else /* implied we do have NFQ support */
extern int max_pending_packets;
#define MAX_ALREADY_TREATED 5
#define NFQ_VERDICT_RETRY_TIME 3
static int already_seen_warning;
static int runmode_workers;
#define NFQ_BURST_FACTOR 4
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
//#define NFQ_DFT_QUEUE_LEN NFQ_BURST_FACTOR * MAX_PENDING
//#define NFQ_NF_BUFSIZE 1500 * NFQ_DFT_QUEUE_LEN
typedef struct NFQThreadVars_
{
uint16_t nfq_index;
ThreadVars *tv;
TmSlot *slot;
char *data; /** Per function and thread data */
int datalen; /** Length of per function and thread data */
CaptureStats stats;
} NFQThreadVars;
/* shared vars for all for nfq queues and threads */
static NFQGlobalVars nfq_g;
static NFQThreadVars nfq_t[NFQ_MAX_QUEUE];
static NFQQueueVars nfq_q[NFQ_MAX_QUEUE];
static uint16_t receive_queue_num = 0;
static SCMutex nfq_init_lock;
TmEcode ReceiveNFQLoop(ThreadVars *tv, void *data, void *slot);
TmEcode ReceiveNFQThreadInit(ThreadVars *, void *, void **);
TmEcode ReceiveNFQThreadDeinit(ThreadVars *, void *);
void ReceiveNFQThreadExitStats(ThreadVars *, void *);
TmEcode VerdictNFQ(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode VerdictNFQThreadInit(ThreadVars *, void *, void **);
TmEcode VerdictNFQThreadDeinit(ThreadVars *, void *);
TmEcode DecodeNFQ(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode DecodeNFQThreadInit(ThreadVars *, void *, void **);
TmEcode DecodeNFQThreadDeinit(ThreadVars *tv, void *data);
TmEcode NFQSetVerdict(Packet *p);
typedef enum NFQMode_ {
NFQ_ACCEPT_MODE,
NFQ_REPEAT_MODE,
NFQ_ROUTE_MODE,
} NFQMode;
#define NFQ_FLAG_FAIL_OPEN (1 << 0)
typedef struct NFQCnf_ {
NFQMode mode;
uint32_t mark;
uint32_t mask;
uint32_t bypass_mark;
uint32_t bypass_mask;
uint32_t next_queue;
uint32_t flags;
uint8_t batchcount;
} NFQCnf;
NFQCnf nfq_config;
void TmModuleReceiveNFQRegister (void)
{
/* XXX create a general NFQ setup function */
memset(&nfq_g, 0, sizeof(nfq_g));
SCMutexInit(&nfq_init_lock, NULL);
tmm_modules[TMM_RECEIVENFQ].name = "ReceiveNFQ";
tmm_modules[TMM_RECEIVENFQ].ThreadInit = ReceiveNFQThreadInit;
tmm_modules[TMM_RECEIVENFQ].Func = NULL;
tmm_modules[TMM_RECEIVENFQ].PktAcqLoop = ReceiveNFQLoop;
tmm_modules[TMM_RECEIVENFQ].PktAcqBreakLoop = NULL;
tmm_modules[TMM_RECEIVENFQ].ThreadExitPrintStats = ReceiveNFQThreadExitStats;
tmm_modules[TMM_RECEIVENFQ].ThreadDeinit = ReceiveNFQThreadDeinit;
tmm_modules[TMM_RECEIVENFQ].RegisterTests = NULL;
tmm_modules[TMM_RECEIVENFQ].flags = TM_FLAG_RECEIVE_TM;
}
void TmModuleVerdictNFQRegister (void)
{
tmm_modules[TMM_VERDICTNFQ].name = "VerdictNFQ";
tmm_modules[TMM_VERDICTNFQ].ThreadInit = VerdictNFQThreadInit;
tmm_modules[TMM_VERDICTNFQ].Func = VerdictNFQ;
tmm_modules[TMM_VERDICTNFQ].ThreadExitPrintStats = NULL;
tmm_modules[TMM_VERDICTNFQ].ThreadDeinit = VerdictNFQThreadDeinit;
tmm_modules[TMM_VERDICTNFQ].RegisterTests = NULL;
}
void TmModuleDecodeNFQRegister (void)
{
tmm_modules[TMM_DECODENFQ].name = "DecodeNFQ";
tmm_modules[TMM_DECODENFQ].ThreadInit = DecodeNFQThreadInit;
tmm_modules[TMM_DECODENFQ].Func = DecodeNFQ;
tmm_modules[TMM_DECODENFQ].ThreadExitPrintStats = NULL;
tmm_modules[TMM_DECODENFQ].ThreadDeinit = DecodeNFQThreadDeinit;
tmm_modules[TMM_DECODENFQ].RegisterTests = NULL;
tmm_modules[TMM_DECODENFQ].flags = TM_FLAG_DECODE_TM;
}
/** \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;
char* nfq_mode = NULL;
int boolval;
SCLogDebug("Initializing NFQ");
memset(&nfq_config, 0, sizeof(nfq_config));
if ((ConfGet("nfq.mode", &nfq_mode)) == 0) {
nfq_config.mode = NFQ_ACCEPT_MODE;
} else {
if (!strcmp("accept", nfq_mode)) {
nfq_config.mode = NFQ_ACCEPT_MODE;
} else if (!strcmp("repeat", nfq_mode)) {
nfq_config.mode = NFQ_REPEAT_MODE;
} else if (!strcmp("route", nfq_mode)) {
nfq_config.mode = NFQ_ROUTE_MODE;
} else {
SCLogError(SC_ERR_INVALID_ARGUMENT, "Unknown nfq.mode");
exit(EXIT_FAILURE);
}
}
(void)ConfGetBool("nfq.fail-open", (int *)&boolval);
if (boolval) {
#ifdef HAVE_NFQ_SET_QUEUE_FLAGS
SCLogInfo("Enabling fail-open on queue");
nfq_config.flags |= NFQ_FLAG_FAIL_OPEN;
#else
SCLogError(SC_ERR_NFQ_NOSUPPORT,
"nfq.%s set but NFQ library has no support for it.", "fail-open");
#endif
}
if ((ConfGetInt("nfq.repeat-mark", &value)) == 1) {
nfq_config.mark = (uint32_t)value;
}
if ((ConfGetInt("nfq.repeat-mask", &value)) == 1) {
nfq_config.mask = (uint32_t)value;
}
if ((ConfGetInt("nfq.bypass-mark", &value)) == 1) {
nfq_config.bypass_mark = (uint32_t)value;
}
if ((ConfGetInt("nfq.bypass-mask", &value)) == 1) {
nfq_config.bypass_mask = (uint32_t)value;
}
if ((ConfGetInt("nfq.route-queue", &value)) == 1) {
nfq_config.next_queue = ((uint32_t)value) << 16;
}
if ((ConfGetInt("nfq.batchcount", &value)) == 1) {
#ifdef HAVE_NFQ_SET_VERDICT_BATCH
if (value > 255) {
SCLogWarning(SC_ERR_INVALID_ARGUMENT, "nfq.batchcount cannot exceed 255.");
value = 255;
}
if (value > 1)
nfq_config.batchcount = (uint8_t) (value - 1);
#else
SCLogWarning(SC_ERR_NFQ_NOSUPPORT,
"nfq.%s set but NFQ library has no support for it.", "batchcount");
#endif
}
if (!quiet) {
switch (nfq_config.mode) {
case NFQ_ACCEPT_MODE:
SCLogInfo("NFQ running in standard ACCEPT/DROP mode");
break;
case NFQ_REPEAT_MODE:
SCLogInfo("NFQ running in REPEAT mode with mark %"PRIu32"/%"PRIu32,
nfq_config.mark, nfq_config.mask);
break;
case NFQ_ROUTE_MODE:
SCLogInfo("NFQ running in route mode with next queue %"PRIu32,
nfq_config.next_queue >> 16);
break;
}
}
}
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: %s",
strerror(errno));
} 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();
if (active_runmode && !strcmp("workers", active_runmode)) {
nq->use_mutex = 0;
runmode_workers = 1;
SCLogInfo("NFQ running in 'workers' runmode, will not use mutex.");
} else {
nq->use_mutex = 1;
runmode_workers = 0;
SCMutexInit(&nq->mutex_qh, NULL);
}
}
#define NFQMutexLock(nq) do { \
if ((nq)->use_mutex) \
SCMutexLock(&(nq)->mutex_qh); \
} while (0)
#define NFQMutexUnlock(nq) do { \
if ((nq)->use_mutex) \
SCMutexUnlock(&(nq)->mutex_qh); \
} while (0)
/**
* \brief Read data from nfq message and setup Packet
*
* \note
* In case of error, this function verdict the packet
* to avoid skb to get stuck in kernel.
*/
int NFQSetupPkt (Packet *p, struct nfq_q_handle *qh, void *data)
{
struct nfq_data *tb = (struct nfq_data *)data;
int ret;
char *pktdata;
struct nfqnl_msg_packet_hdr *ph;
ph = nfq_get_msg_packet_hdr(tb);
if (ph != NULL) {
p->nfq_v.id = ntohl(ph->packet_id);
//p->nfq_v.hw_protocol = ntohs(p->nfq_v.ph->hw_protocol);
p->nfq_v.hw_protocol = ph->hw_protocol;
}
/* coverity[missing_lock] */
p->nfq_v.mark = nfq_get_nfmark(tb);
if (nfq_config.mode == NFQ_REPEAT_MODE) {
if ((nfq_config.mark & nfq_config.mask) ==
(p->nfq_v.mark & nfq_config.mask)) {
int iter = 0;
if (already_seen_warning < MAX_ALREADY_TREATED)
SCLogInfo("Packet seems already treated by suricata");
already_seen_warning++;
do {
ret = nfq_set_verdict(qh, p->nfq_v.id, NF_ACCEPT, 0, NULL);
} while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
if (ret < 0) {
SCLogWarning(SC_ERR_NFQ_SET_VERDICT,
"nfq_set_verdict of %p failed %" PRId32 ": %s",
p, ret, strerror(errno));
}
return -1 ;
}
}
p->nfq_v.ifi = nfq_get_indev(tb);
p->nfq_v.ifo = nfq_get_outdev(tb);
p->nfq_v.verdicted = 0;
#ifdef NFQ_GET_PAYLOAD_SIGNED
ret = nfq_get_payload(tb, &pktdata);
#else
ret = nfq_get_payload(tb, (unsigned char **) &pktdata);
#endif /* NFQ_GET_PAYLOAD_SIGNED */
if (ret > 0) {
/* nfq_get_payload returns a pointer to a part of memory
* that is not preserved over the lifetime of our packet.
* So we need to copy it. */
if (ret > 65536) {
/* Will not be able to copy data ! Set length to 0
* to trigger an error in packet decoding.
* This is unlikely to happen */
SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "NFQ sent too big packet");
SET_PKT_LEN(p, 0);
} else if (runmode_workers) {
PacketSetData(p, (uint8_t *)pktdata, ret);
} else {
PacketCopyData(p, (uint8_t *)pktdata, ret);
}
} else if (ret == -1) {
/* unable to get pointer to data, ensure packet length is zero.
* This will trigger an error in packet decoding */
SET_PKT_LEN(p, 0);
}
ret = nfq_get_timestamp(tb, &p->ts);
if (ret != 0 || p->ts.tv_sec == 0) {
memset (&p->ts, 0, sizeof(struct timeval));
gettimeofday(&p->ts, NULL);
}
p->datalink = DLT_RAW;
return 0;
}
static void NFQReleasePacket(Packet *p)
{
if (unlikely(!p->nfq_v.verdicted)) {
PACKET_UPDATE_ACTION(p, ACTION_DROP);
NFQSetVerdict(p);
}
PacketFreeOrRelease(p);
}
/**
* \brief bypass callback function for NFQ
*
* \param p a Packet to use information from to trigger bypass
* \return 1 if bypass is successful, 0 if not
*/
static int NFQBypassCallback(Packet *p)
{
if (IS_TUNNEL_PKT(p)) {
/* real tunnels may have multiple flows inside them, so bypass can't
* work for those. Rebuilt packets from IP fragments are fine. */
if (p->flags & PKT_REBUILT_FRAGMENT) {
Packet *tp = p->root ? p->root : p;
SCMutexLock(&tp->tunnel_mutex);
tp->nfq_v.mark = (nfq_config.bypass_mark & nfq_config.bypass_mask)
| (tp->nfq_v.mark & ~nfq_config.bypass_mask);
tp->flags |= PKT_MARK_MODIFIED;
SCMutexUnlock(&tp->tunnel_mutex);
return 1;
}
source-nfq: fix tunnel mark callback algorithm In case of a tunnel packet, adding a mark to the root packet will have for consequence to bypass all the flows that are hosted in this tunnel. This is not the attended behavior and as initial fix let's simply warn suricata that bypass for NFQ is not possible for this kind of packets. This patch also fixes a segfault. The root packet was accessed even if it is NULL causing a NULL dereference: ASAN:SIGSEGV ================================================================= ==24408==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000060 (pc 0x00000076f948 bp 0x7f435c000240 sp 0x7f435c000220 T5) ASAN:SIGSEGV ==24408==AddressSanitizer: while reporting a bug found another one. Ignoring. #0 0x76f947 in NFQBypassCallback /home/victor/dev/suricata/src/source-nfq.c:510 #1 0x4d0f02 in PacketBypassCallback /home/victor/dev/suricata/src/decode.c:395 #2 0x7b8a95 in StreamTcpPacket /home/victor/dev/suricata/src/stream-tcp.c:4661 #3 0x7b9ddd in StreamTcp /home/victor/dev/suricata/src/stream-tcp.c:4913 #4 0x68fa50 in FlowWorker /home/victor/dev/suricata/src/flow-worker.c:194 #5 0x7f0abd in TmThreadsSlotVarRun /home/victor/dev/suricata/src/tm-threads.c:128 #6 0x7f2958 in TmThreadsSlotVar /home/victor/dev/suricata/src/tm-threads.c:585 #7 0x7f436368e6f9 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76f9) #8 0x7f4362802b5c in clone (/lib/x86_64-linux-gnu/libc.so.6+0x106b5c) AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /home/victor/dev/suricata/src/source-nfq.c:510 NFQBypassCallback Thread T5 (W#04) created by T0 (Suricata-Main) here: #0 0x7f4364ff2253 in pthread_create (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x36253) #1 0x7f9c48 in TmThreadSpawn /home/victor/dev/suricata/src/tm-threads.c:1843 #2 0x8da7c0 in RunModeSetIPSAutoFp /home/victor/dev/suricata/src/util-runmodes.c:519 #3 0x73e3ff in RunModeIpsNFQAutoFp /home/victor/dev/suricata/src/runmode-nfq.c:74 #4 0x7503fa in RunModeDispatch /home/victor/dev/suricata/src/runmodes.c:382 #5 0x7e5cb3 in main /home/victor/dev/suricata/src/suricata.c:2547 #6 0x7f436271c82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
10 years ago
return 0;
} else {
/* coverity[missing_lock] */
p->nfq_v.mark = (nfq_config.bypass_mark & nfq_config.bypass_mask)
| (p->nfq_v.mark & ~nfq_config.bypass_mask);
p->flags |= PKT_MARK_MODIFIED;
}
return 1;
}
17 years ago
static int NFQCallBack(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data)
{
NFQThreadVars *ntv = (NFQThreadVars *)data;
ThreadVars *tv = ntv->tv;
int ret;
/* grab a packet */
Packet *p = PacketGetFromQueueOrAlloc();
if (p == NULL) {
return -1;
}
PKT_SET_SRC(p, PKT_SRC_WIRE);
p->nfq_v.nfq_index = ntv->nfq_index;
/* if bypass mask is set then we may want to bypass so set pointer */
if (nfq_config.bypass_mask) {
p->BypassPacketsFlow = NFQBypassCallback;
}
ret = NFQSetupPkt(p, qh, (void *)nfa);
if (ret == -1) {
#ifdef COUNTERS
NFQQueueVars *nfq_q = NFQGetQueue(ntv->nfq_index);
nfq_q->errs++;
nfq_q->pkts++;
nfq_q->bytes += GET_PKT_LEN(p);
#endif /* COUNTERS */
/* NFQSetupPkt is issuing a verdict
so we only recycle Packet and leave */
TmqhOutputPacketpool(tv, p);
return 0;
}
p->ReleasePacket = NFQReleasePacket;
#ifdef COUNTERS
NFQQueueVars *nfq_q = NFQGetQueue(ntv->nfq_index);
nfq_q->pkts++;
nfq_q->bytes += GET_PKT_LEN(p);
#endif /* COUNTERS */
if (ntv->slot) {
if (TmThreadsSlotProcessPkt(tv, ntv->slot, p) != TM_ECODE_OK) {
TmqhOutputPacketpool(ntv->tv, p);
return -1;
}
} else {
/* pass on... */
tv->tmqh_out(tv, p);
}
return 0;
}
TmEcode NFQInitThread(NFQThreadVars *nfq_t, uint32_t queue_maxlen)
{
#ifndef OS_WIN32
struct timeval tv;
int opt;
#endif
NFQQueueVars *nfq_q = NFQGetQueue(nfq_t->nfq_index);
if (nfq_q == NULL) {
SCLogError(SC_ERR_NFQ_OPEN, "no queue for given index");
return TM_ECODE_FAILED;
}
SCLogDebug("opening library handle");
nfq_q->h = nfq_open();
if (!nfq_q->h) {
SCLogError(SC_ERR_NFQ_OPEN, "nfq_open() failed");
return TM_ECODE_FAILED;
}
if (nfq_g.unbind == 0)
{
/* VJ: on my Ubuntu Hardy system this fails the first time it's
* run. Ignoring the error seems to have no bad effects. */
SCLogDebug("unbinding existing nf_queue handler for AF_INET (if any)");
if (nfq_unbind_pf(nfq_q->h, AF_INET) < 0) {
17 years ago
SCLogError(SC_ERR_NFQ_UNBIND, "nfq_unbind_pf() for AF_INET failed");
exit(EXIT_FAILURE);
}
if (nfq_unbind_pf(nfq_q->h, AF_INET6) < 0) {
17 years ago
SCLogError(SC_ERR_NFQ_UNBIND, "nfq_unbind_pf() for AF_INET6 failed");
exit(EXIT_FAILURE);
}
nfq_g.unbind = 1;
SCLogDebug("binding nfnetlink_queue as nf_queue handler for AF_INET and AF_INET6");
if (nfq_bind_pf(nfq_q->h, AF_INET) < 0) {
SCLogError(SC_ERR_NFQ_BIND, "nfq_bind_pf() for AF_INET failed");
17 years ago
exit(EXIT_FAILURE);
}
if (nfq_bind_pf(nfq_q->h, AF_INET6) < 0) {
SCLogError(SC_ERR_NFQ_BIND, "nfq_bind_pf() for AF_INET6 failed");
17 years ago
exit(EXIT_FAILURE);
}
}
SCLogInfo("binding this thread %d to queue '%" PRIu32 "'", nfq_t->nfq_index, nfq_q->queue_num);
/* pass the thread memory as a void ptr so the
* callback function has access to it. */
nfq_q->qh = nfq_create_queue(nfq_q->h, nfq_q->queue_num, &NFQCallBack, (void *)nfq_t);
if (nfq_q->qh == NULL)
{
SCLogError(SC_ERR_NFQ_CREATE_QUEUE, "nfq_create_queue failed");
return TM_ECODE_FAILED;
}
SCLogDebug("setting copy_packet mode");
/* 05DC = 1500 */
//if (nfq_set_mode(nfq_t->qh, NFQNL_COPY_PACKET, 0x05DC) < 0) {
if (nfq_set_mode(nfq_q->qh, NFQNL_COPY_PACKET, 0xFFFF) < 0) {
SCLogError(SC_ERR_NFQ_SET_MODE, "can't set packet_copy mode");
return TM_ECODE_FAILED;
}
#ifdef HAVE_NFQ_MAXLEN
if (queue_maxlen > 0) {
SCLogInfo("setting queue length to %" PRId32 "", queue_maxlen);
/* non-fatal if it fails */
if (nfq_set_queue_maxlen(nfq_q->qh, queue_maxlen) < 0) {
SCLogWarning(SC_ERR_NFQ_MAXLEN, "can't set queue maxlen: your kernel probably "
"doesn't support setting the queue length");
}
}
#endif /* HAVE_NFQ_MAXLEN */
#ifndef OS_WIN32
/* set netlink buffer size to a decent value */
nfnl_rcvbufsiz(nfq_nfnlh(nfq_q->h), queue_maxlen * 1500);
SCLogInfo("setting nfnl bufsize to %" PRId32 "", queue_maxlen * 1500);
nfq_q->nh = nfq_nfnlh(nfq_q->h);
nfq_q->fd = nfnl_fd(nfq_q->nh);
NFQMutexInit(nfq_q);
/* Set some netlink specific option on the socket to increase
performance */
opt = 1;
#ifdef NETLINK_BROADCAST_SEND_ERROR
if (setsockopt(nfq_q->fd, SOL_NETLINK,
NETLINK_BROADCAST_SEND_ERROR, &opt, sizeof(int)) == -1) {
SCLogWarning(SC_ERR_NFQ_SETSOCKOPT,
"can't set netlink broadcast error: %s",
strerror(errno));
}
#endif
/* Don't send error about no buffer space available but drop the
packets instead */
#ifdef NETLINK_NO_ENOBUFS
if (setsockopt(nfq_q->fd, SOL_NETLINK,
NETLINK_NO_ENOBUFS, &opt, sizeof(int)) == -1) {
SCLogWarning(SC_ERR_NFQ_SETSOCKOPT,
"can't set netlink enobufs: %s",
strerror(errno));
}
#endif
#ifdef HAVE_NFQ_SET_QUEUE_FLAGS
if (nfq_config.flags & NFQ_FLAG_FAIL_OPEN) {
uint32_t flags = NFQA_CFG_F_FAIL_OPEN;
uint32_t mask = NFQA_CFG_F_FAIL_OPEN;
int r = nfq_set_queue_flags(nfq_q->qh, mask, flags);
if (r == -1) {
SCLogWarning(SC_ERR_NFQ_SET_MODE, "can't set fail-open mode: %s",
strerror(errno));
} else {
SCLogInfo("fail-open mode should be set on queue");
}
}
#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;
tv.tv_usec = 0;
if(setsockopt(nfq_q->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
SCLogWarning(SC_ERR_NFQ_SETSOCKOPT, "can't set socket timeout: %s", strerror(errno));
}
SCLogDebug("nfq_q->h %p, nfq_q->nh %p, nfq_q->qh %p, nfq_q->fd %" PRId32 "",
nfq_q->h, nfq_q->nh, nfq_q->qh, nfq_q->fd);
#else /* OS_WIN32 */
NFQMutexInit(nfq_q);
nfq_q->ovr.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
nfq_q->fd = nfq_fd(nfq_q->h);
SCLogDebug("nfq_q->h %p, nfq_q->qh %p, nfq_q->fd %p", nfq_q->h, nfq_q->qh, nfq_q->fd);
#endif /* OS_WIN32 */
return TM_ECODE_OK;
}
TmEcode ReceiveNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
{
SCMutexLock(&nfq_init_lock);
#ifndef OS_WIN32
sigset_t sigs;
sigfillset(&sigs);
pthread_sigmask(SIG_BLOCK, &sigs, NULL);
#endif /* OS_WIN32 */
NFQThreadVars *ntv = (NFQThreadVars *) initdata;
/* store the ThreadVars pointer in our NFQ thread context
17 years ago
* as we will need it in our callback function */
ntv->tv = tv;
int r = NFQInitThread(ntv, (max_pending_packets * NFQ_BURST_FACTOR));
if (r != TM_ECODE_OK) {
SCLogError(SC_ERR_NFQ_THREAD_INIT, "nfq thread failed to initialize");
SCMutexUnlock(&nfq_init_lock);
exit(EXIT_FAILURE);
}
#define T_DATA_SIZE 70000
ntv->data = SCMalloc(T_DATA_SIZE);
if (ntv->data == NULL) {
SCMutexUnlock(&nfq_init_lock);
return TM_ECODE_FAILED;
}
ntv->datalen = T_DATA_SIZE;
#undef T_DATA_SIZE
*data = (void *)ntv;
SCMutexUnlock(&nfq_init_lock);
return TM_ECODE_OK;
}
TmEcode ReceiveNFQThreadDeinit(ThreadVars *t, void *data)
{
NFQThreadVars *ntv = (NFQThreadVars *)data;
NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
if (ntv->data != NULL) {
SCFree(ntv->data);
ntv->data = NULL;
}
ntv->datalen = 0;
NFQMutexLock(nq);
SCLogDebug("starting... will close queuenum %" PRIu32 "", nq->queue_num);
if (nq->qh) {
nfq_destroy_queue(nq->qh);
nq->qh = NULL;
}
NFQMutexUnlock(nq);
return TM_ECODE_OK;
}
TmEcode VerdictNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
{
NFQThreadVars *ntv = (NFQThreadVars *) initdata;
CaptureStatsSetup(tv, &ntv->stats);
18 years ago
*data = (void *)ntv;
return TM_ECODE_OK;
}
TmEcode VerdictNFQThreadDeinit(ThreadVars *tv, void *data)
{
NFQThreadVars *ntv = (NFQThreadVars *)data;
NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
SCLogDebug("starting... will close queuenum %" PRIu32 "", nq->queue_num);
NFQMutexLock(nq);
if (nq->qh) {
nfq_destroy_queue(nq->qh);
nq->qh = NULL;
}
NFQMutexUnlock(nq);
return TM_ECODE_OK;
}
/**
* \brief Add a Netfilter queue
*
* \param string with the queue name
*
* \retval 0 on success.
* \retval -1 on failure.
*/
int NFQRegisterQueue(char *queue)
{
NFQThreadVars *ntv = NULL;
NFQQueueVars *nq = NULL;
/* Extract the queue number from the specified command line argument */
uint16_t queue_num = 0;
if ((ByteExtractStringUint16(&queue_num, 10, strlen(queue), queue)) < 0)
{
SCLogError(SC_ERR_INVALID_ARGUMENT, "specified queue number %s is not "
"valid", queue);
return -1;
}
SCMutexLock(&nfq_init_lock);
if (receive_queue_num >= NFQ_MAX_QUEUE) {
SCLogError(SC_ERR_INVALID_ARGUMENT,
"too much Netfilter queue registered (%d)",
receive_queue_num);
SCMutexUnlock(&nfq_init_lock);
return -1;
}
if (receive_queue_num == 0) {
memset(&nfq_t, 0, sizeof(nfq_t));
memset(&nfq_q, 0, sizeof(nfq_q));
}
ntv = &nfq_t[receive_queue_num];
ntv->nfq_index = receive_queue_num;
nq = &nfq_q[receive_queue_num];
nq->queue_num = queue_num;
receive_queue_num++;
SCMutexUnlock(&nfq_init_lock);
LiveRegisterDevice(queue);
SCLogDebug("Queue \"%s\" registered.", queue);
return 0;
}
/**
* \brief Get a pointer to the NFQ queue at index
*
* \param number idx of the queue in our array
*
* \retval ptr pointer to the NFQThreadVars at index
* \retval NULL on error
*/
void *NFQGetQueue(int number)
{
if (number >= receive_queue_num)
return NULL;
return (void *)&nfq_q[number];
}
/**
* \brief Get a pointer to the NFQ thread at index
*
* This function is temporary used as configuration parser.
*
* \param number idx of the queue in our array
*
* \retval ptr pointer to the NFQThreadVars at index
* \retval NULL on error
*/
void *NFQGetThread(int number)
{
if (number >= receive_queue_num)
return NULL;
return (void *)&nfq_t[number];
}
/**
* \brief NFQ function to get a packet from the kernel
*
* \note separate functions for Linux and Win32 for readability.
*/
#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, flag);
if (rv < 0) {
if (errno == EINTR || errno == EWOULDBLOCK) {
/* no error on timeout */
if (flag)
NFQVerdictCacheFlush(t);
} else {
#ifdef COUNTERS
NFQMutexLock(t);
t->errs++;
NFQMutexUnlock(t);
#endif /* COUNTERS */
}
} else if(rv == 0) {
SCLogWarning(SC_ERR_NFQ_RECV, "recv got returncode 0");
} else {
#ifdef DBG_PERF
if (rv > t->dbg_maxreadsize)
t->dbg_maxreadsize = rv;
#endif /* DBG_PERF */
//printf("NFQRecvPkt: t %p, rv = %" PRId32 "\n", t, rv);
NFQMutexLock(t);
if (t->qh != NULL) {
ret = nfq_handle_packet(t->h, tv->data, rv);
} else {
SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "NFQ handle has been destroyed");
ret = -1;
}
NFQMutexUnlock(t);
if (ret != 0) {
SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "nfq_handle_packet error %" PRId32 "", ret);
}
}
}
#else /* WIN32 version of NFQRecvPkt */
void NFQRecvPkt(NFQQueueVars *t, NFQThreadVars *tv)
{
int rv, ret;
static int timeouted = 0;
if (timeouted) {
if (WaitForSingleObject(t->ovr.hEvent, 1000) == WAIT_TIMEOUT) {
rv = -1;
errno = EINTR;
goto process_rv;
}
timeouted = 0;
}
read_packet_again:
if (!ReadFile(t->fd, tv->buf, sizeof(tv->buf), (DWORD*)&rv, &t->ovr)) {
if (GetLastError() != ERROR_IO_PENDING) {
rv = -1;
errno = EIO;
} else {
if (WaitForSingleObject(t->ovr.hEvent, 1000) == WAIT_TIMEOUT) {
rv = -1;
errno = EINTR;
timeouted = 1;
} else {
/* We needn't to call GetOverlappedResult() because it always
* fail with our error code ERROR_MORE_DATA. */
goto read_packet_again;
}
}
}
process_rv:
if (rv < 0) {
if (errno == EINTR) {
/* no error on timeout */
} else {
#ifdef COUNTERS
t->errs++;
#endif /* COUNTERS */
}
} else if(rv == 0) {
SCLogWarning(SC_ERR_NFQ_RECV, "recv got returncode 0");
} else {
#ifdef DBG_PERF
if (rv > t->dbg_maxreadsize)
t->dbg_maxreadsize = rv;
#endif /* DBG_PERF */
//printf("NFQRecvPkt: t %p, rv = %" PRId32 "\n", t, rv);
NFQMutexLock(t);
if (t->qh) {
ret = nfq_handle_packet(t->h, buf, rv);
} else {
SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "NFQ handle has been destroyed");
ret = -1;
}
NFQMutexUnlock(t);
if (ret != 0) {
SCLogWarning(SC_ERR_NFQ_HANDLE_PKT, "nfq_handle_packet error %" PRId32 "", ret);
}
}
}
#endif /* OS_WIN32 */
/**
* \brief Main NFQ reading Loop function
*/
TmEcode ReceiveNFQLoop(ThreadVars *tv, void *data, void *slot)
{
SCEnter();
NFQThreadVars *ntv = (NFQThreadVars *)data;
NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
ntv->slot = ((TmSlot *) slot)->slot_next;
while(1) {
if (suricata_ctl_flags != 0) {
NFQMutexLock(nq);
if (nq->qh) {
nfq_destroy_queue(nq->qh);
nq->qh = NULL;
}
NFQMutexUnlock(nq);
break;
}
NFQRecvPkt(nq, ntv);
StatsSyncCountersIfSignalled(tv);
}
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief NFQ receive module stats printing function
*/
void ReceiveNFQThreadExitStats(ThreadVars *tv, void *data)
{
NFQThreadVars *ntv = (NFQThreadVars *)data;
NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index);
#ifdef COUNTERS
SCLogNotice("(%s) Treated: Pkts %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
tv->name, nq->pkts, nq->bytes, nq->errs);
SCLogNotice("(%s) Verdict: Accepted %"PRIu32", Dropped %"PRIu32", Replaced %"PRIu32,
tv->name, nq->accepted, nq->dropped, nq->replaced);
#endif
}
/**
* \brief NFQ verdict function
*/
TmEcode NFQSetVerdict(Packet *p)
{
int iter = 0;
int ret = 0;
uint32_t verdict = NF_ACCEPT;
/* we could also have a direct pointer but we need to have a ref counf in this case */
NFQQueueVars *t = nfq_q + p->nfq_v.nfq_index;
/** \todo add a test on validity of the entry NFQQueueVars could have been
* wipeout
*/
p->nfq_v.verdicted = 1;
/* can't verdict a "fake" packet */
if (p->flags & PKT_PSEUDO_STREAM_END) {
return TM_ECODE_OK;
}
//printf("%p verdicting on queue %" PRIu32 "\n", t, t->queue_num);
NFQMutexLock(t);
if (t->qh == NULL) {
/* Somebody has started a clean-up, we leave */
NFQMutexUnlock(t);
return TM_ECODE_OK;
}
if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
verdict = NF_DROP;
#ifdef COUNTERS
t->dropped++;
#endif /* COUNTERS */
} else {
switch (nfq_config.mode) {
default:
case NFQ_ACCEPT_MODE:
verdict = NF_ACCEPT;
break;
case NFQ_REPEAT_MODE:
verdict = NF_REPEAT;
break;
case NFQ_ROUTE_MODE:
verdict = ((uint32_t) NF_QUEUE) | nfq_config.next_queue;
break;
}
if (p->flags & PKT_STREAM_MODIFIED) {
#ifdef COUNTERS
t->replaced++;
#endif /* COUNTERS */
}
#ifdef COUNTERS
t->accepted++;
#endif /* COUNTERS */
}
ret = NFQVerdictCacheAdd(t, p, verdict);
if (ret == 0) {
NFQMutexUnlock(t);
return TM_ECODE_OK;
}
do {
switch (nfq_config.mode) {
default:
case NFQ_ACCEPT_MODE:
case NFQ_ROUTE_MODE:
if (p->flags & PKT_MARK_MODIFIED) {
#ifdef HAVE_NFQ_SET_VERDICT2
if (p->flags & PKT_STREAM_MODIFIED) {
ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
p->nfq_v.mark,
GET_PKT_LEN(p), GET_PKT_DATA(p));
} else {
ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict,
p->nfq_v.mark,
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(p->nfq_v.mark),
GET_PKT_LEN(p), GET_PKT_DATA(p));
} else {
ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict,
htonl(p->nfq_v.mark),
0, NULL);
}
#endif /* HAVE_NFQ_SET_VERDICT2 */
} else {
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));
} else {
ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, 0, NULL);
}
}
break;
case NFQ_REPEAT_MODE:
#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 /* HAVE_NFQ_SET_VERDICT2 */
break;
}
} while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME));
NFQMutexUnlock(t);
if (ret < 0) {
SCLogWarning(SC_ERR_NFQ_SET_VERDICT,
"nfq_set_verdict of %p failed %" PRId32 ": %s",
p, ret, strerror(errno));
return TM_ECODE_FAILED;
}
return TM_ECODE_OK;
}
/**
* \brief NFQ verdict module packet entry function
*/
TmEcode VerdictNFQ(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
NFQThreadVars *ntv = (NFQThreadVars *)data;
/* update counters */
CaptureStatsUpdate(tv, &ntv->stats, p);
int ret;
/* if this is a tunnel packet we check if we are ready to verdict
* already. */
if (IS_TUNNEL_PKT(p)) {
char verdict = 1;
18 years ago
//printf("VerdictNFQ: tunnel pkt: %p %s\n", p, p->root ? "upper layer" : "root");
SCMutex *m = p->root ? &p->root->tunnel_mutex : &p->tunnel_mutex;
SCMutexLock(m);
/* if there are more tunnel packets than ready to verdict packets,
* we won't verdict this one */
18 years ago
if (TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p)) {
SCLogDebug("not ready to verdict yet: TUNNEL_PKT_TPR(p) > "
"TUNNEL_PKT_RTV(p) = %" PRId32 " > %" PRId32,
TUNNEL_PKT_TPR(p), TUNNEL_PKT_RTV(p));
verdict = 0;
}
SCMutexUnlock(m);
/* don't verdict if we are not ready */
if (verdict == 1) {
18 years ago
//printf("VerdictNFQ: setting verdict\n");
ret = NFQSetVerdict(p->root ? p->root : p);
if (ret != TM_ECODE_OK)
return ret;
18 years ago
} else {
TUNNEL_INCR_PKT_RTV(p);
}
} else {
/* no tunnel, verdict normally */
ret = NFQSetVerdict(p);
if (ret != TM_ECODE_OK)
return ret;
}
return TM_ECODE_OK;
}
/**
* \brief Decode a packet coming from NFQ
*/
TmEcode DecodeNFQ(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
DecodeThreadVars *dtv = (DecodeThreadVars *)data;
/* XXX HACK: flow timeout can call us for injected pseudo packets
* see bug: https://redmine.openinfosecfoundation.org/issues/1107 */
if (p->flags & PKT_PSEUDO_STREAM_END)
return TM_ECODE_OK;
DecodeUpdatePacketCounters(tv, dtv, p);
if (IPV4_GET_RAW_VER(ip4h) == 4) {
SCLogDebug("IPv4 packet");
DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
} else if(IPV6_GET_RAW_VER(ip6h) == 6) {
SCLogDebug("IPv6 packet");
DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
} else {
SCLogDebug("packet unsupported by NFQ, first byte: %02x", *GET_PKT_DATA(p));
}
PacketDecodeFinalize(tv, dtv, p);
return TM_ECODE_OK;
}
/**
* \brief Initialize the NFQ Decode threadvars
*/
TmEcode DecodeNFQThreadInit(ThreadVars *tv, void *initdata, void **data)
{
DecodeThreadVars *dtv = NULL;
dtv = DecodeThreadVarsAlloc(tv);
if (dtv == NULL)
SCReturnInt(TM_ECODE_FAILED);
DecodeRegisterPerfCounters(dtv, tv);
*data = (void *)dtv;
return TM_ECODE_OK;
}
TmEcode DecodeNFQThreadDeinit(ThreadVars *tv, void *data)
{
if (data != NULL)
DecodeThreadVarsFree(tv, data);
SCReturnInt(TM_ECODE_OK);
}
#endif /* NFQ */