]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
netmap support 1318/head
authorgureedo <gureedo@intersvyaz.net>
Sat, 13 Dec 2014 11:12:40 +0000 (16:12 +0500)
committergureedo <gureedo@intersvyaz.net>
Thu, 5 Feb 2015 11:51:20 +0000 (16:51 +0500)
15 files changed:
configure.ac
src/Makefile.am
src/decode.h
src/runmode-netmap.c [new file with mode: 0644]
src/runmode-netmap.h [new file with mode: 0644]
src/runmodes.c
src/runmodes.h
src/source-netmap.c [new file with mode: 0644]
src/source-netmap.h [new file with mode: 0644]
src/suricata.c
src/tm-modules.c
src/tm-threads-common.h
src/util-error.c
src/util-error.h
suricata.yaml.in

index fb4dfefe70982c828920759668725a7ab2408048..0cbba4477128e9476e8eb4486a8c480afcb580eb 100644 (file)
             [[#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
     AC_ARG_ENABLE(non-bundled-htp,
@@ -1717,6 +1733,7 @@ SURICATA_BUILD_CONF="Suricata Configuration:
   NFQueue support:                         ${enable_nfqueue}
   NFLOG support:                           ${enable_nflog}
   IPFW support:                            ${enable_ipfw}
+  Netmap support:                          ${enable_netmap}
   DAG enabled:                             ${enable_dag}
   Napatech enabled:                        ${enable_napatech}
   Unix socket enabled:                     ${enable_unixsocket}
index d60f56c4b9b9e81641eb5d7f082688865d4997a7..582247e7623cb5b4cd5705a9de120e3da8301a76 100644 (file)
@@ -249,6 +249,7 @@ runmode-erf-dag.c runmode-erf-dag.h \
 runmode-erf-file.c runmode-erf-file.h \
 runmode-ipfw.c runmode-ipfw.h \
 runmode-napatech.c runmode-napatech.h \
+runmode-netmap.c runmode-netmap.h \
 runmode-nfq.c runmode-nfq.h \
 runmode-nflog.c runmode-nflog.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-mpipe.c source-mpipe.h \
 source-napatech.c source-napatech.h \
+source-netmap.c source-netmap.h \
 source-nfq.c source-nfq.h \
 source-nflog.c source-nflog.h \
 source-pcap.c source-pcap.h \
index bd9aeef61b990cd0c8e5c79889e5af5d4cbbf103..9b16e036ccc03aca94cbf41ad4790bb58dc37f56 100644 (file)
@@ -61,6 +61,7 @@ enum PktSrcEnum {
 #include "source-pcap.h"
 #include "source-af-packet.h"
 #include "source-mpipe.h"
+#include "source-netmap.h"
 
 #include "action-globals.h"
 
@@ -414,6 +415,9 @@ typedef struct Packet_
         /* tilegx mpipe stuff */
         MpipePacketVars mpipe_v;
 #endif
+#ifdef HAVE_NETMAP
+        NetmapPacketVars netmap_v;
+#endif
 
         /** libpcap vars: shared by Pcap Live mode and Pcap File mode */
         PcapPacketVars pcap_v;
diff --git a/src/runmode-netmap.c b/src/runmode-netmap.c
new file mode 100644 (file)
index 0000000..f5172ab
--- /dev/null
@@ -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);
+}
+
+/**
+* @}
+*/
diff --git a/src/runmode-netmap.h b/src/runmode-netmap.h
new file mode 100644 (file)
index 0000000..392e978
--- /dev/null
@@ -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__ */
index 35f4e0739aeba3e8cca0df52af8ec70207d489da..4ce80d7a937d31dd6ab403cad5874208e070520e 100644 (file)
@@ -133,6 +133,12 @@ static const char *RunModeTranslateModeToName(int runmode)
             return "MPIPE";
         case RUNMODE_AFP_DEV:
             return "AF_PACKET_DEV";
+        case RUNMODE_NETMAP:
+#ifdef HAVE_NETMAP
+            return "NETMAP";
+#else
+            return "NETMAP(DISABLED)";
+#endif
         case RUNMODE_UNIX_SOCKET:
             return "UNIX_SOCKET";
         default:
@@ -205,6 +211,7 @@ void RunModeRegisterRunModes(void)
     RunModeErfDagRegister();
     RunModeNapatechRegister();
     RunModeIdsAFPRegister();
+    RunModeIdsNetmapRegister();
     RunModeIdsNflogRegister();
     RunModeTileMpipeRegister();
     RunModeUnixSocketRegister();
@@ -306,6 +313,9 @@ void RunModeDispatch(int runmode, const char *custom_mode, DetectEngineCtx *de_c
             case RUNMODE_AFP_DEV:
                 custom_mode = RunModeAFPGetDefaultMode();
                 break;
+            case RUNMODE_NETMAP:
+                custom_mode = RunModeNetmapGetDefaultMode();
+                break;
             case RUNMODE_UNIX_SOCKET:
                 custom_mode = RunModeUnixSocketGetDefaultMode();
                 break;
index ba155c713477af214b5bebf0e0a15eb0f4b463a1..80ad652b34b44b6c765fc1a5e799e08226e533a7 100644 (file)
@@ -35,6 +35,7 @@ enum {
     RUNMODE_ERF_FILE,
     RUNMODE_DAG,
     RUNMODE_AFP_DEV,
+    RUNMODE_NETMAP,
     RUNMODE_TILERA_MPIPE,
     RUNMODE_UNITTEST,
     RUNMODE_NAPATECH,
@@ -89,6 +90,7 @@ int RunModeOutputFiledataEnabled(void);
 #include "runmode-af-packet.h"
 #include "runmode-nflog.h"
 #include "runmode-unix-socket.h"
+#include "runmode-netmap.h"
 
 int threading_set_cpu_affinity;
 extern float threading_detect_ratio;
diff --git a/src/source-netmap.c b/src/source-netmap.c
new file mode 100644 (file)
index 0000000..8e36d28
--- /dev/null
@@ -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 */
+/**
+* @}
+*/
diff --git a/src/source-netmap.h b/src/source-netmap.h
new file mode 100644 (file)
index 0000000..42ed91f
--- /dev/null
@@ -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__ */
index b71d540f3e3da5d157c638c0c241acff7e8afbd7..894b927aa618ad5786e04418917a88b24af9bccf 100644 (file)
 #include "source-napatech.h"
 
 #include "source-af-packet.h"
+#include "source-netmap.h"
 #include "source-mpipe.h"
 
 #include "respond-reject.h"
@@ -605,6 +606,9 @@ void usage(const char *progname)
 #ifdef HAVE_AF_PACKET
     printf("\t--af-packet[=<dev>]                  : run in af-packet mode, no value select interfaces from suricata.yaml\n");
 #endif
+#ifdef HAVE_NETMAP
+    printf("\t--netmap[=<dev>]                     : run in netmap mode, no value select interfaces from suricata.yaml\n");
+#endif
 #ifdef HAVE_PFRING
     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");
@@ -683,6 +687,9 @@ void SCPrintBuildInfo(void)
 #ifdef HAVE_AF_PACKET
     strlcat(features, "AF_PACKET ", sizeof(features));
 #endif
+#ifdef HAVE_NETMAP
+    strlcat(features, "NETMAP ", sizeof(features));
+#endif
 #ifdef HAVE_PACKET_FANOUT
     strlcat(features, "HAVE_PACKET_FANOUT ", sizeof(features));
 #endif
@@ -844,6 +851,9 @@ void RegisterAllModules()
     /* af-packet */
     TmModuleReceiveAFPRegister();
     TmModuleDecodeAFPRegister();
+    /* netmap */
+    TmModuleReceiveNetmapRegister();
+    TmModuleDecodeNetmapRegister();
     /* pfring */
     TmModuleReceivePfringRegister();
     TmModuleDecodePfringRegister();
@@ -1015,6 +1025,26 @@ static TmEcode ParseInterfacesList(int run_mode, char *pcap_dev)
                 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
     } else if (run_mode == RUNMODE_NFLOG) {
         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-type", required_argument, 0, 0},
         {"af-packet", optional_argument, 0, 0},
+        {"netmap", optional_argument, 0, 0},
         {"pcap", optional_argument, 0, 0},
 #ifdef BUILD_UNIX_SOCKET
         {"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 "
                         "configure when building.");
                 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
             } else if (strcmp((long_opts[option_index]).name, "nflog") == 0) {
 #ifdef HAVE_NFLOG
@@ -1956,6 +2017,7 @@ static int ConfigGetCaptureValue(SCInstance *suri)
         switch (suri->run_mode) {
             case RUNMODE_PCAP_DEV:
             case RUNMODE_AFP_DEV:
+            case RUNMODE_NETMAP:
             case RUNMODE_PFRING:
                 /* FIXME this don't work effficiently in multiinterface */
                 /* find payload for interface and use it */
index 19caeca727244702518b57d340cfa4f5565b2cbd..bd990b4e119c9b15f7ce5ff047c7a99b2854663d 100644 (file)
@@ -269,6 +269,8 @@ const char * TmModuleTmmIdToString(TmmId id)
         CASE_CODE (TMM_FLOWRECYCLER);
         CASE_CODE (TMM_LUALOG);
         CASE_CODE (TMM_LOGSTATSLOG);
+        CASE_CODE (TMM_RECEIVENETMAP);
+        CASE_CODE (TMM_DECODENETMAP);
 
         CASE_CODE (TMM_SIZE);
     }
index 059ba7b12d5f00c6d4e623408b68a101191c20d5..c18e50876521bbc1a6720ccdefc38350ea376bbd 100644 (file)
@@ -74,6 +74,8 @@ typedef enum {
     TMM_DECODEERFDAG,
     TMM_RECEIVEAFP,
     TMM_DECODEAFP,
+    TMM_RECEIVENETMAP,
+    TMM_DECODENETMAP,
     TMM_ALERTPCAPINFO,
     TMM_RECEIVEMPIPE,
     TMM_DECODEMPIPE,
index e8dd99390231869818084e7a2f70184205ce5b45..4ae917191eb0be25cf0950e87e7ca2acba27c5de 100644 (file)
@@ -302,6 +302,9 @@ const char * SCErrorToString(SCError err)
         CASE_CODE (SC_WARN_LUA_SCRIPT);
         CASE_CODE (SC_ERR_LUA_SCRIPT);
         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";
index 8d5577d8efa3f358ce60d69d6ab4798482a07e6b..9e9008a66c497fa98933e8ef1e97ba923afa6d10 100644 (file)
@@ -291,6 +291,9 @@ typedef enum {
     SC_WARN_LUA_SCRIPT,
     SC_ERR_LUA_SCRIPT,
     SC_WARN_NO_STATS_LOGGERS,
+    SC_ERR_NO_NETMAP,
+    SC_ERR_NETMAP_CREATE,
+    SC_ERR_NETMAP_READ,
 } SCError;
 
 const char *SCErrorToString(SCError);
index 2bb208381eee12b8c3d774bda3ca995943b68b16..5caa0031e6471a8c79cb49dde5db5a5281d4c5dd 100644 (file)
@@ -453,6 +453,39 @@ af-packet:
     #threads: auto
     #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:
   uricontent: enabled