netmap support

pull/1318/head
gureedo 11 years ago
parent cbe934267e
commit 10104066e1

@ -1055,6 +1055,22 @@
[[#include <linux/if_packet.h>]]) [[#include <linux/if_packet.h>]])
]) ])
# Netmap support
AC_ARG_ENABLE(netmap,
AS_HELP_STRING([--enable-netmap], [Enable Netmap support]),,[enable_netmap=no])
AC_ARG_WITH(netmap_includes,
[ --with-netmap-includes=DIR netmap include directory],
[with_netmap_includes="$withval"],[with_netmap_includes=no])
AS_IF([test "x$enable_netmap" = "xyes"], [
CFLAGS="$CFLAGS -DHAVE_NETMAP"
if test "$with_netmap_includes" != "no"; then
CPPFLAGS="${CPPFLAGS} -I${with_netmap_includes}"
fi
AC_CHECK_HEADER(net/netmap_user.h,,[AC_ERROR(net/netmap_user.h not found ...)],)
])
# libhtp # libhtp
AC_ARG_ENABLE(non-bundled-htp, AC_ARG_ENABLE(non-bundled-htp,
@ -1717,6 +1733,7 @@ SURICATA_BUILD_CONF="Suricata Configuration:
NFQueue support: ${enable_nfqueue} NFQueue support: ${enable_nfqueue}
NFLOG support: ${enable_nflog} NFLOG support: ${enable_nflog}
IPFW support: ${enable_ipfw} IPFW support: ${enable_ipfw}
Netmap support: ${enable_netmap}
DAG enabled: ${enable_dag} DAG enabled: ${enable_dag}
Napatech enabled: ${enable_napatech} Napatech enabled: ${enable_napatech}
Unix socket enabled: ${enable_unixsocket} Unix socket enabled: ${enable_unixsocket}

@ -249,6 +249,7 @@ runmode-erf-dag.c runmode-erf-dag.h \
runmode-erf-file.c runmode-erf-file.h \ runmode-erf-file.c runmode-erf-file.h \
runmode-ipfw.c runmode-ipfw.h \ runmode-ipfw.c runmode-ipfw.h \
runmode-napatech.c runmode-napatech.h \ runmode-napatech.c runmode-napatech.h \
runmode-netmap.c runmode-netmap.h \
runmode-nfq.c runmode-nfq.h \ runmode-nfq.c runmode-nfq.h \
runmode-nflog.c runmode-nflog.h \ runmode-nflog.c runmode-nflog.h \
runmode-pcap.c runmode-pcap.h \ runmode-pcap.c runmode-pcap.h \
@ -264,6 +265,7 @@ source-erf-file.c source-erf-file.h \
source-ipfw.c source-ipfw.h \ source-ipfw.c source-ipfw.h \
source-mpipe.c source-mpipe.h \ source-mpipe.c source-mpipe.h \
source-napatech.c source-napatech.h \ source-napatech.c source-napatech.h \
source-netmap.c source-netmap.h \
source-nfq.c source-nfq.h \ source-nfq.c source-nfq.h \
source-nflog.c source-nflog.h \ source-nflog.c source-nflog.h \
source-pcap.c source-pcap.h \ source-pcap.c source-pcap.h \

@ -61,6 +61,7 @@ enum PktSrcEnum {
#include "source-pcap.h" #include "source-pcap.h"
#include "source-af-packet.h" #include "source-af-packet.h"
#include "source-mpipe.h" #include "source-mpipe.h"
#include "source-netmap.h"
#include "action-globals.h" #include "action-globals.h"
@ -414,6 +415,9 @@ typedef struct Packet_
/* tilegx mpipe stuff */ /* tilegx mpipe stuff */
MpipePacketVars mpipe_v; MpipePacketVars mpipe_v;
#endif #endif
#ifdef HAVE_NETMAP
NetmapPacketVars netmap_v;
#endif
/** libpcap vars: shared by Pcap Live mode and Pcap File mode */ /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
PcapPacketVars pcap_v; PcapPacketVars pcap_v;

@ -0,0 +1,439 @@
/* Copyright (C) 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.
*/
/**
* \ingroup netmap
*
* @{
*/
/**
* \file
*
* \author Aleksey Katargin <gureedo@gmail.com>
*
* Netmap runmode
*
*/
#include "suricata-common.h"
#include "config.h"
#include "tm-threads.h"
#include "conf.h"
#include "runmodes.h"
#include "runmode-netmap.h"
#include "log-httplog.h"
#include "output.h"
#include "detect-engine-mpm.h"
#include "alert-fastlog.h"
#include "alert-prelude.h"
#include "alert-unified2-alert.h"
#include "alert-debuglog.h"
#include "util-debug.h"
#include "util-time.h"
#include "util-cpu.h"
#include "util-affinity.h"
#include "util-device.h"
#include "util-runmodes.h"
#include "util-ioctl.h"
#include "source-netmap.h"
extern int max_pending_packets;
static const char *default_mode_workers = NULL;
const char *RunModeNetmapGetDefaultMode(void)
{
return default_mode_workers;
}
void RunModeIdsNetmapRegister(void)
{
RunModeRegisterNewRunMode(RUNMODE_NETMAP, "single",
"Single threaded netmap mode",
RunModeIdsNetmapSingle);
RunModeRegisterNewRunMode(RUNMODE_NETMAP, "workers",
"Workers netmap mode, each thread does all"
" tasks from acquisition to logging",
RunModeIdsNetmapWorkers);
default_mode_workers = "workers";
RunModeRegisterNewRunMode(RUNMODE_NETMAP, "autofp",
"Multi threaded netmap mode. Packets from "
"each flow are assigned to a single detect "
"thread.",
RunModeIdsNetmapAutoFp);
return;
}
#ifdef HAVE_NETMAP
static void NetmapDerefConfig(void *conf)
{
NetmapIfaceConfig *pfp = (NetmapIfaceConfig *)conf;
/* config is used only once but cost of this low. */
if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
SCFree(pfp);
}
}
/**
* \brief extract information from config file
*
* The returned structure will be freed by the thread init function.
* This is thus necessary to or copy the structure before giving it
* to thread or to reparse the file for each thread (and thus have
* new structure.
*
* \return a NetmapIfaceConfig corresponding to the interface name
*/
static void *ParseNetmapConfig(const char *iface)
{
char *threadsstr = NULL;
ConfNode *if_root;
ConfNode *if_default = NULL;
ConfNode *netmap_node;
NetmapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
char *tmpctype;
char *copymodestr;
int boolval;
char *bpf_filter = NULL;
char *out_iface = NULL;
if (unlikely(aconf == NULL)) {
return NULL;
}
if (iface == NULL) {
SCFree(aconf);
return NULL;
}
memset(aconf, 0, sizeof(*aconf));
strlcpy(aconf->iface, iface, sizeof(aconf->iface));
SC_ATOMIC_INIT(aconf->ref);
(void) SC_ATOMIC_ADD(aconf->ref, 1);
aconf->DerefFunc = NetmapDerefConfig;
aconf->threads = 1;
aconf->promisc = 1;
aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
aconf->copy_mode = NETMAP_COPY_MODE_NONE;
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 */
netmap_node = ConfGetNode("netmap");
if (netmap_node == NULL) {
SCLogInfo("Unable to find netmap config using default value");
return aconf;
}
if_root = ConfNodeLookupKeyValue(netmap_node, "interface", iface);
if_default = ConfNodeLookupKeyValue(netmap_node, "interface", "default");
if (if_root == NULL && if_default == NULL) {
SCLogInfo("Unable to find netmap config for "
"interface \"%s\" or \"default\", using default value",
iface);
return aconf;
}
/* If there is no setting for current interface use default one as main iface */
if (if_root == NULL) {
if_root = if_default;
if_default = NULL;
}
if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
aconf->threads = 1;
} else {
if (strcmp(threadsstr, "auto") == 0) {
aconf->threads = GetIfaceRSSQueuesNum(iface);
} else {
aconf->threads = (uint8_t)atoi(threadsstr);
}
}
if (aconf->threads <= 0) {
aconf->threads = 1;
}
if (aconf->threads) {
SCLogInfo("Using %d threads for interface %s", aconf->threads, iface);
}
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
if (strlen(out_iface) > 0) {
aconf->out_iface = out_iface;
}
}
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
if (aconf->out_iface == NULL) {
SCLogInfo("Copy mode activated but no destination"
" iface. Disabling feature");
} else if (strlen(copymodestr) <= 0) {
aconf->out_iface = NULL;
} else if (strcmp(copymodestr, "ips") == 0) {
SCLogInfo("Netmap IPS mode activated %s->%s",
iface,
aconf->out_iface);
aconf->copy_mode = NETMAP_COPY_MODE_IPS;
} else if (strcmp(copymodestr, "tap") == 0) {
SCLogInfo("Netmap TAP mode activated %s->%s",
iface,
aconf->out_iface);
aconf->copy_mode = NETMAP_COPY_MODE_TAP;
} else {
SCLogInfo("Invalid mode (not in tap, ips)");
}
}
SC_ATOMIC_RESET(aconf->ref);
(void) SC_ATOMIC_ADD(aconf->ref, aconf->threads);
/* load netmap bpf filter */
/* command line value has precedence */
if (ConfGet("bpf-filter", &bpf_filter) != 1) {
if (ConfGetChildValueWithDefault(if_root, if_default, "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);
}
}
}
(void)ConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", (int *)&boolval);
if (boolval) {
SCLogInfo("Disabling promiscuous mode on iface %s", aconf->iface);
aconf->promisc = 0;
}
if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
if (strcmp(tmpctype, "auto") == 0) {
aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
} else if (strcmp(tmpctype, "yes") == 0) {
aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
} else if (strcmp(tmpctype, "no") == 0) {
aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
} else {
SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", aconf->iface);
}
}
return aconf;
}
static int NetmapConfigGeThreadsCount(void *conf)
{
NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)conf;
return aconf->threads;
}
int NetmapRunModeIsIPS()
{
int nlive = LiveGetDeviceCount();
int ldev;
ConfNode *if_root;
ConfNode *if_default = NULL;
ConfNode *netmap_node;
int has_ips = 0;
int has_ids = 0;
/* Find initial node */
netmap_node = ConfGetNode("netmap");
if (netmap_node == NULL) {
return 0;
}
if_default = ConfNodeLookupKeyValue(netmap_node, "interface", "default");
for (ldev = 0; ldev < nlive; ldev++) {
char *live_dev = LiveGetDeviceName(ldev);
if (live_dev == NULL) {
SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
return 0;
}
char *copymodestr = NULL;
if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev);
if (if_root == NULL) {
if (if_default == NULL) {
SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
return 0;
}
if_root = if_default;
}
if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
if (strcmp(copymodestr, "ips") == 0) {
has_ips = 1;
} else {
has_ids = 1;
}
} else {
has_ids = 1;
}
}
if (has_ids && has_ips) {
SCLogInfo("Netmap mode using IPS and IDS mode");
for (ldev = 0; ldev < nlive; ldev++) {
char *live_dev = LiveGetDeviceName(ldev);
if (live_dev == NULL) {
SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
return 0;
}
if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev);
char *copymodestr = NULL;
if (if_root == NULL) {
if (if_default == NULL) {
SCLogError(SC_ERR_INVALID_VALUE, "Problem with config file");
return 0;
}
if_root = if_default;
}
if (! ((ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) &&
(strcmp(copymodestr, "ips") == 0))) {
SCLogError(SC_ERR_INVALID_ARGUMENT,
"Netmap IPS mode used and interface '%s' is in IDS or TAP mode. "
"Sniffing '%s' but expect bad result as stream-inline is activated.",
live_dev, live_dev);
}
}
}
return has_ips;
}
#endif // #ifdef HAVE_NETMAP
int RunModeIdsNetmapAutoFp(DetectEngineCtx *de_ctx)
{
SCEnter();
#ifdef HAVE_NETMAP
int ret;
char *live_dev = NULL;
RunModeInitialize();
TimeModeSetLive();
(void)ConfGet("netmap.live-interface", &live_dev);
SCLogDebug("live_dev %s", live_dev);
ret = RunModeSetLiveCaptureAutoFp(de_ctx,
ParseNetmapConfig,
NetmapConfigGeThreadsCount,
"ReceiveNetmap",
"DecodeNetmap", "RxNetmap",
live_dev);
if (ret != 0) {
SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
exit(EXIT_FAILURE);
}
SCLogInfo("RunModeIdsNetmapAutoFp initialised");
#endif /* HAVE_NETMAP */
SCReturnInt(0);
}
/**
* \brief Single thread version of the netmap processing.
*/
int RunModeIdsNetmapSingle(DetectEngineCtx *de_ctx)
{
SCEnter();
#ifdef HAVE_NETMAP
int ret;
char *live_dev = NULL;
RunModeInitialize();
TimeModeSetLive();
(void)ConfGet("netmap.live-interface", &live_dev);
ret = RunModeSetLiveCaptureSingle(de_ctx,
ParseNetmapConfig,
NetmapConfigGeThreadsCount,
"ReceiveNetmap",
"DecodeNetmap", "NetmapPkt",
live_dev);
if (ret != 0) {
SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
exit(EXIT_FAILURE);
}
SCLogInfo("RunModeIdsNetmapSingle initialised");
#endif /* HAVE_NETMAP */
SCReturnInt(0);
}
/**
* \brief Workers version of the netmap processing.
*
* Start N threads with each thread doing all the work.
*
*/
int RunModeIdsNetmapWorkers(DetectEngineCtx *de_ctx)
{
SCEnter();
#ifdef HAVE_NETMAP
int ret;
char *live_dev = NULL;
RunModeInitialize();
TimeModeSetLive();
(void)ConfGet("netmap.live-interface", &live_dev);
ret = RunModeSetLiveCaptureWorkers(de_ctx,
ParseNetmapConfig,
NetmapConfigGeThreadsCount,
"ReceiveNetmap",
"DecodeNetmap", "NetmapPkt",
live_dev);
if (ret != 0) {
SCLogError(SC_ERR_RUNMODE, "Unable to start runmode");
exit(EXIT_FAILURE);
}
SCLogInfo("RunModeIdsNetmapWorkers initialised");
#endif /* HAVE_NETMAP */
SCReturnInt(0);
}
/**
* @}
*/

@ -0,0 +1,33 @@
/* Copyright (C) 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 Aleksey Katargin <gureedo@gmail.com>
*/
#ifndef __RUNMODE_NETMAP_H__
#define __RUNMODE_NETMAP_H__
int RunModeIdsNetmapSingle(DetectEngineCtx *);
int RunModeIdsNetmapAutoFp(DetectEngineCtx *);
int RunModeIdsNetmapWorkers(DetectEngineCtx *);
void RunModeIdsNetmapRegister(void);
const char *RunModeNetmapGetDefaultMode(void);
int NetmapRunModeIsIPS();
#endif /* __RUNMODE_NETMAP_H__ */

@ -133,6 +133,12 @@ static const char *RunModeTranslateModeToName(int runmode)
return "MPIPE"; return "MPIPE";
case RUNMODE_AFP_DEV: case RUNMODE_AFP_DEV:
return "AF_PACKET_DEV"; return "AF_PACKET_DEV";
case RUNMODE_NETMAP:
#ifdef HAVE_NETMAP
return "NETMAP";
#else
return "NETMAP(DISABLED)";
#endif
case RUNMODE_UNIX_SOCKET: case RUNMODE_UNIX_SOCKET:
return "UNIX_SOCKET"; return "UNIX_SOCKET";
default: default:
@ -205,6 +211,7 @@ void RunModeRegisterRunModes(void)
RunModeErfDagRegister(); RunModeErfDagRegister();
RunModeNapatechRegister(); RunModeNapatechRegister();
RunModeIdsAFPRegister(); RunModeIdsAFPRegister();
RunModeIdsNetmapRegister();
RunModeIdsNflogRegister(); RunModeIdsNflogRegister();
RunModeTileMpipeRegister(); RunModeTileMpipeRegister();
RunModeUnixSocketRegister(); RunModeUnixSocketRegister();
@ -306,6 +313,9 @@ void RunModeDispatch(int runmode, const char *custom_mode, DetectEngineCtx *de_c
case RUNMODE_AFP_DEV: case RUNMODE_AFP_DEV:
custom_mode = RunModeAFPGetDefaultMode(); custom_mode = RunModeAFPGetDefaultMode();
break; break;
case RUNMODE_NETMAP:
custom_mode = RunModeNetmapGetDefaultMode();
break;
case RUNMODE_UNIX_SOCKET: case RUNMODE_UNIX_SOCKET:
custom_mode = RunModeUnixSocketGetDefaultMode(); custom_mode = RunModeUnixSocketGetDefaultMode();
break; break;

@ -35,6 +35,7 @@ enum {
RUNMODE_ERF_FILE, RUNMODE_ERF_FILE,
RUNMODE_DAG, RUNMODE_DAG,
RUNMODE_AFP_DEV, RUNMODE_AFP_DEV,
RUNMODE_NETMAP,
RUNMODE_TILERA_MPIPE, RUNMODE_TILERA_MPIPE,
RUNMODE_UNITTEST, RUNMODE_UNITTEST,
RUNMODE_NAPATECH, RUNMODE_NAPATECH,
@ -89,6 +90,7 @@ int RunModeOutputFiledataEnabled(void);
#include "runmode-af-packet.h" #include "runmode-af-packet.h"
#include "runmode-nflog.h" #include "runmode-nflog.h"
#include "runmode-unix-socket.h" #include "runmode-unix-socket.h"
#include "runmode-netmap.h"
int threading_set_cpu_affinity; int threading_set_cpu_affinity;
extern float threading_detect_ratio; extern float threading_detect_ratio;

@ -0,0 +1,999 @@
/* Copyright (C) 2011-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.
*/
/**
* \defgroup netmap Netmap running mode
*
* @{
*/
/**
* \file
*
* \author Aleksey Katargin <gureedo@gmail.com>
*
* Netmap socket acquisition support
*
*/
#include "suricata-common.h"
#include "config.h"
#include "suricata.h"
#include "decode.h"
#include "packet-queue.h"
#include "threads.h"
#include "threadvars.h"
#include "tm-queuehandlers.h"
#include "tm-modules.h"
#include "tm-threads.h"
#include "tm-threads-common.h"
#include "conf.h"
#include "util-debug.h"
#include "util-device.h"
#include "util-error.h"
#include "util-privs.h"
#include "util-optimize.h"
#include "util-checksum.h"
#include "util-ioctl.h"
#include "util-host-info.h"
#include "tmqh-packetpool.h"
#include "source-netmap.h"
#include "runmodes.h"
#ifdef __SC_CUDA_SUPPORT__
#include "util-cuda.h"
#include "util-cuda-buffer.h"
#include "util-mpm-ac.h"
#include "util-cuda-handlers.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "util-cuda-vars.h"
#endif /* __SC_CUDA_SUPPORT__ */
#ifdef HAVE_NETMAP
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#if HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <net/netmap_user.h>
#endif /* HAVE_NETMAP */
extern int max_pending_packets;
#ifndef HAVE_NETMAP
TmEcode NoNetmapSupportExit(ThreadVars *, void *, void **);
void TmModuleReceiveNetmapRegister (void)
{
tmm_modules[TMM_RECEIVENETMAP].name = "ReceiveNetmap";
tmm_modules[TMM_RECEIVENETMAP].ThreadInit = NoNetmapSupportExit;
tmm_modules[TMM_RECEIVENETMAP].Func = NULL;
tmm_modules[TMM_RECEIVENETMAP].ThreadExitPrintStats = NULL;
tmm_modules[TMM_RECEIVENETMAP].ThreadDeinit = NULL;
tmm_modules[TMM_RECEIVENETMAP].RegisterTests = NULL;
tmm_modules[TMM_RECEIVENETMAP].cap_flags = 0;
tmm_modules[TMM_RECEIVENETMAP].flags = TM_FLAG_RECEIVE_TM;
}
/**
* \brief Registration Function for DecodeNetmap.
* \todo Unit tests are needed for this module.
*/
void TmModuleDecodeNetmapRegister (void)
{
tmm_modules[TMM_DECODENETMAP].name = "DecodeNetmap";
tmm_modules[TMM_DECODENETMAP].ThreadInit = NoNetmapSupportExit;
tmm_modules[TMM_DECODENETMAP].Func = NULL;
tmm_modules[TMM_DECODENETMAP].ThreadExitPrintStats = NULL;
tmm_modules[TMM_DECODENETMAP].ThreadDeinit = NULL;
tmm_modules[TMM_DECODENETMAP].RegisterTests = NULL;
tmm_modules[TMM_DECODENETMAP].cap_flags = 0;
tmm_modules[TMM_DECODENETMAP].flags = TM_FLAG_DECODE_TM;
}
/**
* \brief this function prints an error message and exits.
*/
TmEcode NoNetmapSupportExit(ThreadVars *tv, void *initdata, void **data)
{
SCLogError(SC_ERR_NO_NETMAP,"Error creating thread %s: you do not have "
"support for netmap enabled, please recompile "
"with --enable-netmap", tv->name);
exit(EXIT_FAILURE);
}
#else /* We have NETMAP support */
#define POLL_TIMEOUT 100
#if defined(__linux__)
#define POLL_EVENTS (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL)
#else
#define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
#endif
enum {
NETMAP_OK,
NETMAP_FAILURE,
};
enum {
NETMAP_FLAG_ZERO_COPY = 1,
};
/**
* \brief Netmap ring isntance.
*/
typedef struct NetmapRing
{
int fd;
struct netmap_ring *rx;
struct netmap_ring *tx;
SCSpinlock tx_lock;
} NetmapRing;
/**
* \brief Netmap device instance.
*/
typedef struct NetmapDevice_
{
char ifname[IFNAMSIZ];
void *mem;
size_t memsize;
struct netmap_if *nif;
int rings_cnt;
NetmapRing *rings;
unsigned int ref;
SC_ATOMIC_DECLARE(unsigned int, threads_run);
TAILQ_ENTRY(NetmapDevice_) next;
} NetmapDevice;
/**
* \brief Module thread local variables.
*/
typedef struct NetmapThreadVars_
{
/* receive inteface */
NetmapDevice *ifsrc;
/* dst interface for IPS mode */
NetmapDevice *ifdst;
int ring_from;
int ring_to;
int thread_idx;
int flags;
struct bpf_program bpf_prog;
/* internal shit */
TmSlot *slot;
ThreadVars *tv;
LiveDevice *livedev;
/* copy from config */
int copy_mode;
ChecksumValidationMode checksum_mode;
/* counters */
uint64_t pkts;
uint64_t bytes;
uint64_t drops;
uint16_t capture_kernel_packets;
uint16_t capture_kernel_drops;
} NetmapThreadVars;
typedef TAILQ_HEAD(NetmapDeviceList_, NetmapDevice_) NetmapDeviceList;
static NetmapDeviceList netmap_devlist = TAILQ_HEAD_INITIALIZER(netmap_devlist);
static SCMutex netmap_devlist_lock = SCMUTEX_INITIALIZER;
/**
* \brief Get interface flags.
* \param fd Network susbystem file descritor.
* \param ifname Inteface name.
* \return Interface flags or -1 on error
*/
static int NetmapGetIfaceFlags(int fd, const char *ifname)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Unable to get flags for iface \"%s\": %s",
ifname, strerror(errno));
return -1;
}
#ifdef OS_FREEBSD
int flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
return flags;
#else
return ifr.ifr_flags;
#endif
}
/**
* \brief Set interface flags.
* \param fd Network susbystem file descritor.
* \param ifname Inteface name.
* \param flags Flags to set.
* \return Zero on success.
*/
static int NetmapSetIfaceFlags(int fd, const char *ifname, int flags)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
#ifdef OS_FREEBSD
ifr.ifr_flags = flags & 0xffff;
ifr.ifr_flagshigh = flags >> 16;
#else
ifr.ifr_flags = flags;
#endif
if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Unable to set flags for iface \"%s\": %s",
ifname, strerror(errno));
return -1;
}
return 0;
}
/**
* \brief Open interface in netmap mode.
* \param ifname Interface name.
* \param promisc Enable promiscuous mode.
* \param dev Pointer to requested netmap device instance.
* \param verbose Verbose error logging.
* \return Zero on success.
*/
static int NetmapOpen(char *ifname, int promisc, NetmapDevice **pdevice, int verbose)
{
NetmapDevice *pdev = NULL;
struct nmreq nm_req;
*pdevice = NULL;
SCMutexLock(&netmap_devlist_lock);
/* search interface in our already opened list */
TAILQ_FOREACH(pdev, &netmap_devlist, next) {
if (strcmp(ifname, pdev->ifname) == 0) {
*pdevice = pdev;
pdev->ref++;
SCMutexUnlock(&netmap_devlist_lock);
return 0;
}
}
/* not found, create new record */
pdev = SCMalloc(sizeof(*pdev));
if (unlikely(pdev == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
goto error;
}
memset(pdev, 0, sizeof(*pdev));
SC_ATOMIC_INIT(pdev->threads_run);
strlcpy(pdev->ifname, ifname, sizeof(pdev->ifname));
/* open netmap */
int fd = open("/dev/netmap", O_RDWR);
if (fd == -1) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't open netmap device, error %s",
strerror(errno));
goto error_pdev;
}
/* check interface is up */
int if_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (if_fd < 0) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't create control socket for '%s' interface",
ifname);
goto error_fd;
}
int if_flags = NetmapGetIfaceFlags(if_fd, ifname);
if (if_flags == -1) {
if (verbose) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Can not access to interface '%s'",
ifname);
}
close(if_fd);
goto error_fd;
}
if ((if_flags & IFF_UP) == 0) {
if (verbose) {
SCLogError(SC_ERR_NETMAP_CREATE, "Interface '%s' is down", ifname);
}
close(if_fd);
goto error_fd;
}
if (promisc) {
if_flags |= IFF_PROMISC;
NetmapSetIfaceFlags(if_fd, ifname, if_flags);
}
close(if_fd);
/* query netmap info */
memset(&nm_req, 0, sizeof(nm_req));
strlcpy(nm_req.nr_name, ifname, sizeof(nm_req.nr_name));
nm_req.nr_version = NETMAP_API;
if (ioctl(fd, NIOCGINFO, &nm_req) != 0) {
if (verbose) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't query netmap for %s, error %s",
ifname, strerror(errno));
}
goto error_fd;
};
if (nm_req.nr_rx_rings != nm_req.nr_tx_rings) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Interface '%s' have non-equeal Tx/Rx rings (%"PRIu16"/%"PRIu16")",
ifname, nm_req.nr_rx_rings, nm_req.nr_tx_rings);
goto error_fd;
}
pdev->rings_cnt = nm_req.nr_rx_rings;
pdev->memsize = nm_req.nr_memsize;
pdev->rings = SCMalloc(sizeof(*pdev->rings) * pdev->rings_cnt);
if (unlikely(pdev->rings == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
goto error_fd;
}
memset(pdev->rings, 0, sizeof(*pdev->rings) * pdev->rings_cnt);
/* open individual instance for each ring */
int success_cnt = 0;
for (int i = 0; i < pdev->rings_cnt; i++) {
NetmapRing *pring = &pdev->rings[i];
pring->fd = open("/dev/netmap", O_RDWR);
if (pring->fd == -1) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't open netmap device: %s",
strerror(errno));
break;
}
nm_req.nr_flags = NR_REG_ONE_NIC;
nm_req.nr_ringid = i | NETMAP_NO_TX_POLL;
if (ioctl(pring->fd, NIOCREGIF, &nm_req) != 0) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't register %s with netmap: %s",
ifname, strerror(errno));
break;
}
if (pdev->mem == NULL) {
pdev->mem = mmap(0, pdev->memsize, PROT_WRITE | PROT_READ,
MAP_SHARED, pring->fd, 0);
if (pdev->mem == MAP_FAILED) {
SCLogError(SC_ERR_NETMAP_CREATE,
"Couldn't mmap netmap device: %s",
strerror(errno));
goto error_fd;
}
pdev->nif = NETMAP_IF(pdev->mem, nm_req.nr_offset);
}
pring->rx = NETMAP_RXRING(pdev->nif, i);
pring->tx = NETMAP_TXRING(pdev->nif, i);
SCSpinInit(&pring->tx_lock, 0);
success_cnt++;
}
if (success_cnt != pdev->rings_cnt) {
for(int i = 0; i < success_cnt; i++) {
close(pdev->rings[i].fd);
}
SCFree(pdev->rings);
goto error_mem;
}
close(fd);
*pdevice = pdev;
TAILQ_INSERT_TAIL(&netmap_devlist, pdev, next);
SCMutexUnlock(&netmap_devlist_lock);
return 0;
error_mem:
munmap(pdev->mem, pdev->memsize);
error_fd:
close(fd);
error_pdev:
SCFree(pdev);
error:
SCMutexUnlock(&netmap_devlist_lock);
return -1;
}
/**
* \brief Close or dereference netmap device instance.
* \param pdev Netmap device instance.
* \return Zero on success.
*/
static int NetmapClose(NetmapDevice *dev)
{
NetmapDevice *pdev, *tmp;
SCMutexLock(&netmap_devlist_lock);
TAILQ_FOREACH_SAFE(pdev, &netmap_devlist, next, tmp) {
if (pdev == dev) {
pdev->ref--;
if (!pdev->ref) {
munmap(pdev->mem, pdev->memsize);
for (int i = 0; i < pdev->rings_cnt; i++) {
NetmapRing *pring = &pdev->rings[i];
close(pring->fd);
SCSpinDestroy(&pring->tx_lock);
}
SCFree(pdev->rings);
TAILQ_REMOVE(&netmap_devlist, pdev, next);
SCFree(pdev);
}
SCMutexUnlock(&netmap_devlist_lock);
return 0;
}
}
SCMutexUnlock(&netmap_devlist_lock);
return -1;
}
/**
* \brief PcapDumpCounters
* \param ntv
*/
static inline void NetmapDumpCounters(NetmapThreadVars *ntv)
{
SCPerfCounterAddUI64(ntv->capture_kernel_packets, ntv->tv->sc_perf_pca, ntv->pkts);
SCPerfCounterAddUI64(ntv->capture_kernel_drops, ntv->tv->sc_perf_pca, ntv->drops);
(void) SC_ATOMIC_ADD(ntv->livedev->drop, ntv->drops);
(void) SC_ATOMIC_ADD(ntv->livedev->pkts, ntv->pkts);
ntv->drops = 0;
ntv->pkts = 0;
}
/**
* \brief Init function for ReceiveNetmap.
* \param tv pointer to ThreadVars
* \param initdata pointer to the interface passed from the user
* \param data pointer gets populated with NetmapThreadVars
*/
static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, void *initdata, void **data)
{
SCEnter();
NetmapIfaceConfig *aconf = initdata;
if (initdata == NULL) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
SCReturnInt(TM_ECODE_FAILED);
}
NetmapThreadVars *ntv = SCMalloc(sizeof(*ntv));
if (unlikely(ntv == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
goto error;
}
memset(ntv, 0, sizeof(*ntv));
ntv->tv = tv;
ntv->checksum_mode = aconf->checksum_mode;
ntv->copy_mode = aconf->copy_mode;
ntv->livedev = LiveGetDevice(aconf->iface);
if (ntv->livedev == NULL) {
SCLogError(SC_ERR_INVALID_VALUE, "Unable to find Live device");
goto error_ntv;
}
if (NetmapOpen(aconf->iface, aconf->promisc, &ntv->ifsrc, 1) != 0) {
goto error_ntv;
}
if (aconf->threads > ntv->ifsrc->rings_cnt) {
SCLogError(SC_ERR_INVALID_VALUE,
"Thread count can't be greater than ring count. "
"Configured %d threads for interfaces '%s' with %u rings.",
aconf->threads, aconf->iface, ntv->ifsrc->rings_cnt);
goto error_src;
}
do {
ntv->thread_idx = SC_ATOMIC_GET(ntv->ifsrc->threads_run);
} while (SC_ATOMIC_CAS(&ntv->ifsrc->threads_run, ntv->thread_idx, ntv->thread_idx + 1) == 0);
/* calculate rings borders */
int tmp = ntv->ifsrc->rings_cnt / aconf->threads;
ntv->ring_from = ntv->thread_idx * tmp;
ntv->ring_to = ntv->ring_from + tmp - 1;
if (ntv->ring_to >= ntv->ifsrc->rings_cnt)
ntv->ring_to = ntv->ifsrc->rings_cnt - 1;
if (aconf->copy_mode != NETMAP_COPY_MODE_NONE) {
if (NetmapOpen(aconf->out_iface, 0, &ntv->ifdst, 1) != 0) {
goto error_src;
}
}
/* basic counters */
ntv->capture_kernel_packets = SCPerfTVRegisterCounter("capture.kernel_packets",
ntv->tv,
SC_PERF_TYPE_UINT64,
"NULL");
ntv->capture_kernel_drops = SCPerfTVRegisterCounter("capture.kernel_drops",
ntv->tv,
SC_PERF_TYPE_UINT64,
"NULL");
char const *active_runmode = RunmodeGetActive();
if (active_runmode && !strcmp("workers", active_runmode)) {
ntv->flags |= NETMAP_FLAG_ZERO_COPY;
SCLogInfo("Enabling zero copy mode");
}
if (aconf->bpf_filter) {
SCLogInfo("Using BPF '%s' on iface '%s'",
aconf->bpf_filter, ntv->ifsrc->ifname);
if (pcap_compile_nopcap(default_packet_size, /* snaplen_arg */
LINKTYPE_ETHERNET, /* linktype_arg */
&ntv->bpf_prog, /* program */
aconf->bpf_filter, /* const char *buf */
1, /* optimize */
PCAP_NETMASK_UNKNOWN /* mask */
) == -1) {
SCLogError(SC_ERR_NETMAP_CREATE, "Filter compilation failed.");
goto error_src;
}
}
if (GetIfaceOffloading(aconf->iface) == 1) {
SCLogWarning(SC_ERR_NETMAP_CREATE,
"Using mmap mode with GRO or LRO activated can lead to capture problems");
}
*data = (void *)ntv;
aconf->DerefFunc(aconf);
SCReturnInt(TM_ECODE_OK);
error_src:
NetmapClose(ntv->ifsrc);
error_ntv:
SCFree(ntv);
error:
aconf->DerefFunc(aconf);
SCReturnInt(TM_ECODE_FAILED);
}
/**
* \brief Output packet to destination interface or drop.
* \param ntv Thread local variables.
* \param p Source packet.
*/
static TmEcode NetmapWritePacket(NetmapThreadVars *ntv, Packet *p)
{
if (ntv->copy_mode == NETMAP_COPY_MODE_IPS) {
if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
return TM_ECODE_OK;
}
}
/* map src ring_id to dst ring_id */
int dst_ring_id = p->netmap_v.ring_id % ntv->ifdst->rings_cnt;
NetmapRing *txring = &ntv->ifdst->rings[dst_ring_id];
NetmapRing *rxring = &ntv->ifsrc->rings[p->netmap_v.ring_id];
SCSpinLock(&txring->tx_lock);
if (!nm_ring_space(txring->tx)) {
ntv->drops++;
SCSpinUnlock(&txring->tx_lock);
return TM_ECODE_FAILED;
}
struct netmap_slot *rs = &rxring->rx->slot[p->netmap_v.slot_id];
struct netmap_slot *ts = &txring->tx->slot[txring->tx->cur];
/* swap slot buffers */
uint32_t tmp_idx;
tmp_idx = ts->buf_idx;
ts->buf_idx = rs->buf_idx;
rs->buf_idx = tmp_idx;
ts->len = rs->len;
ts->flags |= NS_BUF_CHANGED;
rs->flags |= NS_BUF_CHANGED;
txring->tx->head = txring->tx->cur = nm_ring_next(txring->tx, txring->tx->cur);
SCSpinUnlock(&txring->tx_lock);
return TM_ECODE_OK;
}
/**
* \brief Packet release routine.
* \param p Packet.
*/
static void NetmapReleasePacket(Packet *p)
{
NetmapThreadVars *ntv = (NetmapThreadVars *)p->netmap_v.ntv;
/* Need to be in copy mode and need to detect early release
where Ethernet header could not be set (and pseudo packet) */
if ((ntv->copy_mode != NETMAP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
NetmapWritePacket(ntv, p);
}
PacketFreeOrRelease(p);
}
/**
* \brief Read packets from ring and pass them further.
* \param ntv Thread local variables.
* \param ring_id Ring id to read.
*/
static int NetmapRingRead(NetmapThreadVars *ntv, int ring_id)
{
SCEnter();
struct netmap_ring *ring = ntv->ifsrc->rings[ring_id].rx;
uint32_t avail = nm_ring_space(ring);
uint32_t cur = ring->cur;
while (likely(avail-- > 0)) {
struct netmap_slot *slot = &ring->slot[cur];
unsigned char *slot_data = (unsigned char *)NETMAP_BUF(ring, slot->buf_idx);
if (ntv->bpf_prog.bf_len) {
struct pcap_pkthdr pkthdr = { {0, 0}, slot->len, slot->len };
if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, slot_data) == 0) {
/* rejected by bpf */
cur = nm_ring_next(ring, cur);
continue;
}
}
Packet *p = PacketGetFromQueueOrAlloc();
if (unlikely(p == NULL)) {
SCReturnInt(NETMAP_FAILURE);
}
PKT_SET_SRC(p, PKT_SRC_WIRE);
p->livedev = ntv->livedev;
p->datalink = LINKTYPE_ETHERNET;
p->ts = ring->ts;
ntv->pkts++;
ntv->bytes += slot->len;
/* checksum validation */
if (ntv->checksum_mode == CHECKSUM_VALIDATION_DISABLE) {
p->flags |= PKT_IGNORE_CHECKSUM;
} else if (ntv->checksum_mode == CHECKSUM_VALIDATION_AUTO) {
if (ntv->livedev->ignore_checksum) {
p->flags |= PKT_IGNORE_CHECKSUM;
} else if (ChecksumAutoModeCheck(ntv->pkts,
SC_ATOMIC_GET(ntv->livedev->pkts),
SC_ATOMIC_GET(ntv->livedev->invalid_checksums))) {
ntv->livedev->ignore_checksum = 1;
p->flags |= PKT_IGNORE_CHECKSUM;
}
}
if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
if (PacketSetData(p, slot_data, slot->len) == -1) {
TmqhOutputPacketpool(ntv->tv, p);
SCReturnInt(NETMAP_FAILURE);
} else {
p->ReleasePacket = NetmapReleasePacket;
p->netmap_v.ring_id = ring_id;
p->netmap_v.slot_id = cur;
p->netmap_v.ntv = ntv;
}
} else {
if (PacketCopyData(p, slot_data, slot->len) == -1) {
TmqhOutputPacketpool(ntv->tv, p);
SCReturnInt(NETMAP_FAILURE);
}
}
SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)",
GET_PKT_LEN(p), p, GET_PKT_DATA(p));
if (TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p) != TM_ECODE_OK) {
TmqhOutputPacketpool(ntv->tv, p);
SCReturnInt(NETMAP_FAILURE);
}
cur = nm_ring_next(ring, cur);
}
ring->head = ring->cur = cur;
SCReturnInt(NETMAP_OK);
}
/**
* \brief Main netmap reading loop function
*/
static TmEcode ReceiveNetmapLoop(ThreadVars *tv, void *data, void *slot)
{
SCEnter();
TmSlot *s = (TmSlot *)slot;
NetmapThreadVars *ntv = (NetmapThreadVars *)data;
struct pollfd *fds;
int rings_count = ntv->ring_to - ntv->ring_from + 1;
ntv->slot = s->slot_next;
fds = SCMalloc(sizeof(*fds) * rings_count);
if (unlikely(fds == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
SCReturnInt(TM_ECODE_FAILED);
}
for (int i = 0; i < rings_count; i++) {
fds[i].fd = ntv->ifsrc->rings[ntv->ring_from + i].fd;
fds[i].events = POLLIN;
}
for(;;) {
if (suricata_ctl_flags != 0) {
break;
}
/* make sure we have at least one packet in the packet pool,
* to prevent us from alloc'ing packets at line rate */
PacketPoolWait();
int r = poll(fds, rings_count, POLL_TIMEOUT);
if (r < 0) {
/* error */
if(errno != EINTR)
SCLogError(SC_ERR_NETMAP_READ,
"Error polling netmap from iface '%s': (%d" PRIu32 ") %s",
ntv->ifsrc->ifname, errno, strerror(errno));
continue;
} else if (r == 0) {
/* no events, timeout */
SCLogDebug("(%s:%d-%d) Poll timeout", ntv->ifsrc->ifname,
ntv->ring_from, ntv->ring_to);
continue;
}
for (int i = 0; i < rings_count; i++) {
if (fds[i].revents & POLL_EVENTS) {
if (fds[i].revents & POLLERR) {
SCLogError(SC_ERR_NETMAP_READ,
"Error reading data from iface '%s': (%d" PRIu32 ") %s",
ntv->ifsrc->ifname, errno, strerror(errno));
} else if (fds[i].revents & POLLNVAL) {
SCLogError(SC_ERR_NETMAP_READ,
"Invalid polling request");
}
continue;
}
if (likely(fds[i].revents & POLLIN)) {
int src_ring_id = ntv->ring_from + i;
NetmapRingRead(ntv, src_ring_id);
if (ntv->copy_mode != NETMAP_COPY_MODE_NONE) {
/* sync dst tx rings */
int dst_ring_id = src_ring_id % ntv->ifdst->rings_cnt;
NetmapRing *dst_ring = &ntv->ifdst->rings[dst_ring_id];
if (SCSpinTrylock(&dst_ring->tx_lock) == 0) {
ioctl(dst_ring->fd, NIOCTXSYNC, 0);
SCSpinUnlock(&dst_ring->tx_lock);
}
}
}
}
NetmapDumpCounters(ntv);
SCPerfSyncCountersIfSignalled(tv);
}
SCFree(fds);
SCPerfSyncCountersIfSignalled(tv);
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief This function prints stats to the screen at exit.
* \param tv pointer to ThreadVars
* \param data pointer that gets cast into NetmapThreadVars for ntv
*/
static void ReceiveNetmapThreadExitStats(ThreadVars *tv, void *data)
{
SCEnter();
NetmapThreadVars *ntv = (NetmapThreadVars *)data;
NetmapDumpCounters(ntv);
SCLogInfo("(%s) Kernel: Packets %" PRIu64 ", dropped %" PRIu64 ", bytes %" PRIu64 "",
tv->name,
(uint64_t) SCPerfGetLocalCounterValue(ntv->capture_kernel_packets, tv->sc_perf_pca),
(uint64_t) SCPerfGetLocalCounterValue(ntv->capture_kernel_drops, tv->sc_perf_pca),
ntv->bytes);
}
/**
* \brief
* \param tv
* \param data Pointer to NetmapThreadVars.
*/
static TmEcode ReceiveNetmapThreadDeinit(ThreadVars *tv, void *data)
{
SCEnter();
NetmapThreadVars *ntv = (NetmapThreadVars *)data;
if (ntv->ifsrc) {
NetmapClose(ntv->ifsrc);
ntv->ifsrc = NULL;
}
if (ntv->ifdst) {
NetmapClose(ntv->ifdst);
ntv->ifdst = NULL;
}
if (ntv->bpf_prog.bf_insns) {
pcap_freecode(&ntv->bpf_prog);
}
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief Prepare netmap decode thread.
* \param tv Thread local avariables.
* \param initdata Thread config.
* \param data Pointer to DecodeThreadVars placed here.
*/
static TmEcode DecodeNetmapThreadInit(ThreadVars *tv, void *initdata, void **data)
{
SCEnter();
DecodeThreadVars *dtv = NULL;
dtv = DecodeThreadVarsAlloc(tv);
if (dtv == NULL)
SCReturnInt(TM_ECODE_FAILED);
DecodeRegisterPerfCounters(dtv, tv);
*data = (void *)dtv;
#ifdef __SC_CUDA_SUPPORT__
if (CudaThreadVarsInit(&dtv->cuda_vars) < 0)
SCReturnInt(TM_ECODE_FAILED);
#endif
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief This function passes off to link type decoders.
*
* DecodeNetmap reads packets from the PacketQueue and passes
* them off to the proper link type decoder.
*
* \param t pointer to ThreadVars
* \param p pointer to the current packet
* \param data pointer that gets cast into NetmapThreadVars for ntv
* \param pq pointer to the current PacketQueue
* \param postpq
*/
static TmEcode DecodeNetmap(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq)
{
SCEnter();
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)
SCReturnInt(TM_ECODE_OK);
/* update counters */
SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca);
SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, GET_PKT_LEN(p));
SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p));
SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, GET_PKT_LEN(p));
DecodeEthernet(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p), pq);
PacketDecodeFinalize(tv, dtv, p);
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief
* \param tv
* \param data Pointer to DecodeThreadVars.
*/
static TmEcode DecodeNetmapThreadDeinit(ThreadVars *tv, void *data)
{
SCEnter();
if (data != NULL)
DecodeThreadVarsFree(tv, data);
SCReturnInt(TM_ECODE_OK);
}
/**
* \brief Registration Function for RecieveNetmap.
*/
void TmModuleReceiveNetmapRegister(void)
{
tmm_modules[TMM_RECEIVENETMAP].name = "ReceiveNetmap";
tmm_modules[TMM_RECEIVENETMAP].ThreadInit = ReceiveNetmapThreadInit;
tmm_modules[TMM_RECEIVENETMAP].Func = NULL;
tmm_modules[TMM_RECEIVENETMAP].PktAcqLoop = ReceiveNetmapLoop;
tmm_modules[TMM_RECEIVENETMAP].ThreadExitPrintStats = ReceiveNetmapThreadExitStats;
tmm_modules[TMM_RECEIVENETMAP].ThreadDeinit = ReceiveNetmapThreadDeinit;
tmm_modules[TMM_RECEIVENETMAP].RegisterTests = NULL;
tmm_modules[TMM_RECEIVENETMAP].cap_flags = SC_CAP_NET_RAW;
tmm_modules[TMM_RECEIVENETMAP].flags = TM_FLAG_RECEIVE_TM;
}
/**
* \brief Registration Function for DecodeNetmap.
*/
void TmModuleDecodeNetmapRegister(void)
{
tmm_modules[TMM_DECODENETMAP].name = "DecodeNetmap";
tmm_modules[TMM_DECODENETMAP].ThreadInit = DecodeNetmapThreadInit;
tmm_modules[TMM_DECODENETMAP].Func = DecodeNetmap;
tmm_modules[TMM_DECODENETMAP].ThreadExitPrintStats = NULL;
tmm_modules[TMM_DECODENETMAP].ThreadDeinit = DecodeNetmapThreadDeinit;
tmm_modules[TMM_DECODENETMAP].RegisterTests = NULL;
tmm_modules[TMM_DECODENETMAP].cap_flags = 0;
tmm_modules[TMM_DECODENETMAP].flags = TM_FLAG_DECODE_TM;
}
#endif /* HAVE_NETMAP */
/* eof */
/**
* @}
*/

@ -0,0 +1,62 @@
/* Copyright (C) 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 Aleksey Katargin <gureedo@gmail.com>
*/
#ifndef __SOURCE_NETMAP_H__
#define __SOURCE_NETMAP_H__
#include "queue.h"
/* copy modes */
enum {
NETMAP_COPY_MODE_NONE,
NETMAP_COPY_MODE_TAP,
NETMAP_COPY_MODE_IPS,
};
#define NETMAP_IFACE_NAME_LENGTH 48
typedef struct NetmapIfaceConfig_
{
char iface[NETMAP_IFACE_NAME_LENGTH];
int threads;
int promisc;
int copy_mode;
ChecksumValidationMode checksum_mode;
char *bpf_filter;
char *out_iface;
SC_ATOMIC_DECLARE(unsigned int, ref);
void (*DerefFunc)(void *);
} NetmapIfaceConfig;
typedef struct NetmapPacketVars_
{
int ring_id;
int slot_id;
/* NetmapThreadVars */
void *ntv;
} NetmapPacketVars;
void TmModuleReceiveNetmapRegister (void);
void TmModuleDecodeNetmapRegister (void);
#endif /* __SOURCE_NETMAP_H__ */

@ -119,6 +119,7 @@
#include "source-napatech.h" #include "source-napatech.h"
#include "source-af-packet.h" #include "source-af-packet.h"
#include "source-netmap.h"
#include "source-mpipe.h" #include "source-mpipe.h"
#include "respond-reject.h" #include "respond-reject.h"
@ -605,6 +606,9 @@ void usage(const char *progname)
#ifdef HAVE_AF_PACKET #ifdef HAVE_AF_PACKET
printf("\t--af-packet[=<dev>] : run in af-packet mode, no value select interfaces from suricata.yaml\n"); printf("\t--af-packet[=<dev>] : run in af-packet mode, no value select interfaces from suricata.yaml\n");
#endif #endif
#ifdef HAVE_NETMAP
printf("\t--netmap[=<dev>] : run in netmap mode, no value select interfaces from suricata.yaml\n");
#endif
#ifdef HAVE_PFRING #ifdef HAVE_PFRING
printf("\t--pfring[=<dev>] : run in pfring mode, use interfaces from suricata.yaml\n"); printf("\t--pfring[=<dev>] : run in pfring mode, use interfaces from suricata.yaml\n");
printf("\t--pfring-int <dev> : run in pfring mode, use interface <dev>\n"); printf("\t--pfring-int <dev> : run in pfring mode, use interface <dev>\n");
@ -683,6 +687,9 @@ void SCPrintBuildInfo(void)
#ifdef HAVE_AF_PACKET #ifdef HAVE_AF_PACKET
strlcat(features, "AF_PACKET ", sizeof(features)); strlcat(features, "AF_PACKET ", sizeof(features));
#endif #endif
#ifdef HAVE_NETMAP
strlcat(features, "NETMAP ", sizeof(features));
#endif
#ifdef HAVE_PACKET_FANOUT #ifdef HAVE_PACKET_FANOUT
strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features)); strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features));
#endif #endif
@ -844,6 +851,9 @@ void RegisterAllModules()
/* af-packet */ /* af-packet */
TmModuleReceiveAFPRegister(); TmModuleReceiveAFPRegister();
TmModuleDecodeAFPRegister(); TmModuleDecodeAFPRegister();
/* netmap */
TmModuleReceiveNetmapRegister();
TmModuleDecodeNetmapRegister();
/* pfring */ /* pfring */
TmModuleReceivePfringRegister(); TmModuleReceivePfringRegister();
TmModuleDecodePfringRegister(); TmModuleDecodePfringRegister();
@ -1015,6 +1025,26 @@ static TmEcode ParseInterfacesList(int run_mode, char *pcap_dev)
EngineModeSetIPS(); EngineModeSetIPS();
} }
} }
#ifdef HAVE_NETMAP
} else if (run_mode == RUNMODE_NETMAP) {
/* iface has been set on command line */
if (strlen(pcap_dev)) {
if (ConfSetFinal("netmap.live-interface", pcap_dev) != 1) {
SCLogError(SC_ERR_INITIALIZATION, "Failed to set netmap.live-interface");
SCReturnInt(TM_ECODE_FAILED);
}
} else {
int ret = LiveBuildDeviceList("netmap");
if (ret == 0) {
SCLogError(SC_ERR_INITIALIZATION, "No interface found in config for netmap");
SCReturnInt(TM_ECODE_FAILED);
}
if (NetmapRunModeIsIPS()) {
SCLogInfo("Netmap: Setting IPS mode");
EngineModeSetIPS();
}
}
#endif
#ifdef HAVE_NFLOG #ifdef HAVE_NFLOG
} else if (run_mode == RUNMODE_NFLOG) { } else if (run_mode == RUNMODE_NFLOG) {
int ret = LiveBuildDeviceListCustom("nflog", "group"); int ret = LiveBuildDeviceListCustom("nflog", "group");
@ -1129,6 +1159,7 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
{"pfring-cluster-id", required_argument, 0, 0}, {"pfring-cluster-id", required_argument, 0, 0},
{"pfring-cluster-type", required_argument, 0, 0}, {"pfring-cluster-type", required_argument, 0, 0},
{"af-packet", optional_argument, 0, 0}, {"af-packet", optional_argument, 0, 0},
{"netmap", optional_argument, 0, 0},
{"pcap", optional_argument, 0, 0}, {"pcap", optional_argument, 0, 0},
#ifdef BUILD_UNIX_SOCKET #ifdef BUILD_UNIX_SOCKET
{"unix-socket", optional_argument, 0, 0}, {"unix-socket", optional_argument, 0, 0},
@ -1248,6 +1279,36 @@ static TmEcode ParseCommandLine(int argc, char** argv, SCInstance *suri)
"host, make sure to pass --enable-af-packet to " "host, make sure to pass --enable-af-packet to "
"configure when building."); "configure when building.");
return TM_ECODE_FAILED; return TM_ECODE_FAILED;
#endif
} else if (strcmp((long_opts[option_index]).name , "netmap") == 0){
#ifdef HAVE_NETMAP
if (suri->run_mode == RUNMODE_UNKNOWN) {
suri->run_mode = RUNMODE_NETMAP;
if (optarg) {
LiveRegisterDevice(optarg);
memset(suri->pcap_dev, 0, sizeof(suri->pcap_dev));
strlcpy(suri->pcap_dev, optarg,
((strlen(optarg) < sizeof(suri->pcap_dev)) ?
(strlen(optarg) + 1) : sizeof(suri->pcap_dev)));
}
} else if (suri->run_mode == RUNMODE_NETMAP) {
SCLogWarning(SC_WARN_PCAP_MULTI_DEV_EXPERIMENTAL, "using "
"multiple devices to get packets is experimental.");
if (optarg) {
LiveRegisterDevice(optarg);
} else {
SCLogInfo("Multiple netmap option without interface on each is useless");
break;
}
} else {
SCLogError(SC_ERR_MULTIPLE_RUN_MODE, "more than one run mode "
"has been specified");
usage(argv[0]);
return TM_ECODE_FAILED;
}
#else
SCLogError(SC_ERR_NO_NETMAP, "NETMAP not enabled.");
return TM_ECODE_FAILED;
#endif #endif
} else if (strcmp((long_opts[option_index]).name, "nflog") == 0) { } else if (strcmp((long_opts[option_index]).name, "nflog") == 0) {
#ifdef HAVE_NFLOG #ifdef HAVE_NFLOG
@ -1956,6 +2017,7 @@ static int ConfigGetCaptureValue(SCInstance *suri)
switch (suri->run_mode) { switch (suri->run_mode) {
case RUNMODE_PCAP_DEV: case RUNMODE_PCAP_DEV:
case RUNMODE_AFP_DEV: case RUNMODE_AFP_DEV:
case RUNMODE_NETMAP:
case RUNMODE_PFRING: case RUNMODE_PFRING:
/* FIXME this don't work effficiently in multiinterface */ /* FIXME this don't work effficiently in multiinterface */
/* find payload for interface and use it */ /* find payload for interface and use it */

@ -269,6 +269,8 @@ const char * TmModuleTmmIdToString(TmmId id)
CASE_CODE (TMM_FLOWRECYCLER); CASE_CODE (TMM_FLOWRECYCLER);
CASE_CODE (TMM_LUALOG); CASE_CODE (TMM_LUALOG);
CASE_CODE (TMM_LOGSTATSLOG); CASE_CODE (TMM_LOGSTATSLOG);
CASE_CODE (TMM_RECEIVENETMAP);
CASE_CODE (TMM_DECODENETMAP);
CASE_CODE (TMM_SIZE); CASE_CODE (TMM_SIZE);
} }

@ -74,6 +74,8 @@ typedef enum {
TMM_DECODEERFDAG, TMM_DECODEERFDAG,
TMM_RECEIVEAFP, TMM_RECEIVEAFP,
TMM_DECODEAFP, TMM_DECODEAFP,
TMM_RECEIVENETMAP,
TMM_DECODENETMAP,
TMM_ALERTPCAPINFO, TMM_ALERTPCAPINFO,
TMM_RECEIVEMPIPE, TMM_RECEIVEMPIPE,
TMM_DECODEMPIPE, TMM_DECODEMPIPE,

@ -302,6 +302,9 @@ const char * SCErrorToString(SCError err)
CASE_CODE (SC_WARN_LUA_SCRIPT); CASE_CODE (SC_WARN_LUA_SCRIPT);
CASE_CODE (SC_ERR_LUA_SCRIPT); CASE_CODE (SC_ERR_LUA_SCRIPT);
CASE_CODE (SC_WARN_NO_STATS_LOGGERS); CASE_CODE (SC_WARN_NO_STATS_LOGGERS);
CASE_CODE (SC_ERR_NO_NETMAP);
CASE_CODE (SC_ERR_NETMAP_CREATE);
CASE_CODE (SC_ERR_NETMAP_READ);
} }
return "UNKNOWN_ERROR"; return "UNKNOWN_ERROR";

@ -291,6 +291,9 @@ typedef enum {
SC_WARN_LUA_SCRIPT, SC_WARN_LUA_SCRIPT,
SC_ERR_LUA_SCRIPT, SC_ERR_LUA_SCRIPT,
SC_WARN_NO_STATS_LOGGERS, SC_WARN_NO_STATS_LOGGERS,
SC_ERR_NO_NETMAP,
SC_ERR_NETMAP_CREATE,
SC_ERR_NETMAP_READ,
} SCError; } SCError;
const char *SCErrorToString(SCError); const char *SCErrorToString(SCError);

@ -453,6 +453,39 @@ af-packet:
#threads: auto #threads: auto
#use-mmap: yes #use-mmap: yes
# netmap support
netmap:
- interface: eth2
# Number of receive threads. "auto" uses number of RSS queues on interface.
threads: auto
# You can use the following variables to activate netmap tap or IPS mode.
# If copy-mode is set to ips or tap, the traffic coming to the current
# interface will be copied to the copy-iface interface. If 'tap' is set, the
# copy is complete. If 'ips' is set, the packet matching a 'drop' action
# will not be copied.
#copy-mode: tap
#copy-iface: eth3
# Set to yes to disable promiscuous mode
# disable-promisc: no
# Choose checksum verification mode for the interface. At the moment
# of the capture, some packets may be with an invalid checksum due to
# offloading to the network card of the checksum computation.
# Possible values are:
# - yes: checksum validation is forced
# - no: checksum validation is disabled
# - auto: suricata uses a statistical approach to detect when
# checksum off-loading is used.
# Warning: 'checksum-validation' must be set to yes to have any validation
#checksum-checks: auto
# BPF filter to apply to this interface. The pcap filter syntax apply here.
#bpf-filter: port 80 or udp
#- interface: eth3
#threads: auto
#copy-mode: tap
#copy-iface: eth2
# Put default values here
- interface: default
legacy: legacy:
uricontent: enabled uricontent: enabled

Loading…
Cancel
Save