-/* Copyright (C) 2014-2018 Open Information Security Foundation
+/* Copyright (C) 2014-2021 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
*/
/**
-* \file
-*
-* \author Aleksey Katargin <gureedo@gmail.com>
-*
-* Netmap runmode
-*
-*/
+ * \file
+ *
+ * \author Aleksey Katargin <gureedo@gmail.com>
+ * \author Bill Meeks <billmeeks8@gmail.com>
+ *
+ * Netmap runmode
+ *
+ */
#include "suricata-common.h"
-#include "tm-threads.h"
-#include "conf.h"
#include "runmodes.h"
#include "runmode-netmap.h"
-#include "output.h"
-#include "log-httplog.h"
-#include "detect-engine-mpm.h"
-
-#include "alert-fastlog.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 "util-byte.h"
+#ifdef HAVE_NETMAP
+#define NETMAP_WITH_LIBS
+#include <net/netmap_user.h>
+#endif /* HAVE_NETMAP */
+
#include "source-netmap.h"
extern int max_pending_packets;
" tasks from acquisition to logging",
RunModeIdsNetmapWorkers);
RunModeRegisterNewRunMode(RUNMODE_NETMAP, "autofp",
- "Multi threaded netmap mode. Packets from "
+ "Multi-threaded netmap mode. Packets from "
"each flow are assigned to a single detect "
"thread.",
RunModeIdsNetmapAutoFp);
}
}
+ /* we will need the base interface name for later */
+ char base_name[IFNAMSIZ];
+ strlcpy(base_name, ns->iface, sizeof(base_name));
+ if (strlen(base_name) > 0 &&
+ (base_name[strlen(base_name) - 1] == '^' || base_name[strlen(base_name) - 1] == '*')) {
+ base_name[strlen(base_name) - 1] = '\0';
+ }
+
/* prefixed with netmap or vale means it's not a real interface
* and we don't check offloading. */
if (strncmp(ns->iface, "netmap:", 7) != 0 &&
ns->ips = (ns->copy_mode != NETMAP_COPY_MODE_NONE);
- if (ns->sw_ring) {
- /* just one thread per interface supported */
- ns->threads = 1;
- } else if (ns->threads_auto) {
+ if (ns->threads_auto) {
/* As NetmapGetRSSCount used to be broken on Linux,
* fall back to GetIfaceRSSQueuesNum if needed. */
ns->threads = NetmapGetRSSCount(ns->iface);
if (ns->threads == 0) {
- ns->threads = GetIfaceRSSQueuesNum(ns->iface);
+ /* need to use base_name of interface here */
+ ns->threads = GetIfaceRSSQueuesNum(base_name);
}
}
if (ns->threads <= 0) {
}
/**
-* \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
-*/
+ * \brief extract information from config file
+ *
+ * The returned structure will be freed by the thread init function.
+ * This is thus necessary to 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_name)
{
ConfNode *if_root = NULL;
return NULL;
}
- NetmapIfaceConfig *aconf = SCMalloc(sizeof(*aconf));
+ NetmapIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
if (unlikely(aconf == NULL)) {
return NULL;
}
- memset(aconf, 0, sizeof(*aconf));
aconf->DerefFunc = NetmapDerefConfig;
strlcpy(aconf->iface_name, iface_name, sizeof(aconf->iface_name));
if (strlen(out_iface) > 0) {
if_root = ConfFindDeviceConfig(netmap_node, out_iface);
ParseNetmapSettings(&aconf->out, out_iface, if_root, if_default);
-
- /* if one side of the IPS peering uses a sw_ring, we will default
- * to using a single ring/thread on the other side as well. Only
- * if thread variable is set to 'auto'. So the user can override
- * this. */
- if (aconf->out.sw_ring && aconf->in.threads_auto) {
- aconf->out.threads = aconf->in.threads = 1;
- } else if (aconf->in.sw_ring && aconf->out.threads_auto) {
- aconf->out.threads = aconf->in.threads = 1;
- }
}
}
+ int ring_count = NetmapGetRSSCount(aconf->iface_name);
+ if (strlen(aconf->iface_name) > 0 &&
+ (aconf->iface_name[strlen(aconf->iface_name) - 1] == '^' ||
+ aconf->iface_name[strlen(aconf->iface_name) - 1] == '*')) {
+ SCLogDebug("%s -- using %d netmap host ring pair%s", aconf->iface_name, ring_count,
+ ring_count == 1 ? "" : "s");
+ } else {
+ SCLogDebug("%s -- using %d netmap ring pair%s", aconf->iface_name, ring_count,
+ ring_count == 1 ? "" : "s");
+ }
+
+ for (int i = 0; i < ring_count; i++) {
+ char live_buf[32] = { 0 };
+ snprintf(live_buf, sizeof(live_buf), "netmap%d", i);
+ LiveRegisterDevice(live_buf);
+ }
+
/* netmap needs all offloading to be disabled */
if (aconf->in.real) {
char base_name[sizeof(aconf->in.iface)];
SCLogPerf("Using %d threads for interface %s", aconf->in.threads,
aconf->iface_name);
+ LiveDeviceHasNoStats();
return aconf;
}
return has_ips;
}
-#endif // #ifdef HAVE_NETMAP
+typedef enum { NETMAP_AUTOFP, NETMAP_WORKERS, NETMAP_SINGLE } NetmapRunMode_t;
-int RunModeIdsNetmapAutoFp(void)
+static int NetmapRunModeInit(NetmapRunMode_t runmode)
{
SCEnter();
-#ifdef HAVE_NETMAP
- int ret;
- const char *live_dev = NULL;
-
RunModeInitialize();
-
TimeModeSetLive();
+ const char *live_dev = NULL;
(void)ConfGet("netmap.live-interface", &live_dev);
- SCLogDebug("live_dev %s", live_dev);
-
- ret = RunModeSetLiveCaptureAutoFp(
- ParseNetmapConfig,
- NetmapConfigGeThreadsCount,
- "ReceiveNetmap",
- "DecodeNetmap", thread_name_autofp,
- live_dev);
+ int ret;
+ switch (runmode) {
+ case NETMAP_AUTOFP:
+ ret = RunModeSetLiveCaptureAutoFp(ParseNetmapConfig, NetmapConfigGeThreadsCount,
+ "ReceiveNetmap", "DecodeNetmap", thread_name_autofp, live_dev);
+ break;
+ case NETMAP_WORKERS:
+ ret = RunModeSetLiveCaptureWorkers(ParseNetmapConfig, NetmapConfigGeThreadsCount,
+ "ReceiveNetmap", "DecodeNetmap", thread_name_workers, live_dev);
+ break;
+ case NETMAP_SINGLE:
+ ret = RunModeSetLiveCaptureSingle(ParseNetmapConfig, NetmapConfigGeThreadsCount,
+ "ReceiveNetmap", "DecodeNetmap", thread_name_single, live_dev);
+ break;
+ }
if (ret != 0) {
- FatalError(SC_ERR_FATAL, "Unable to start runmode");
+ FatalError(SC_ERR_FATAL, "Unable to start runmode %s",
+ runmode == NETMAP_AUTOFP ? "autofp"
+ : runmode == NETMAP_WORKERS ? "workers" : "single");
}
- SCLogDebug("RunModeIdsNetmapAutoFp initialised");
-#endif /* HAVE_NETMAP */
+ SCLogDebug("%s initialized",
+ runmode == NETMAP_AUTOFP ? "autofp" : runmode == NETMAP_WORKERS ? "workers" : "single");
SCReturnInt(0);
}
+int RunModeIdsNetmapAutoFp(void)
+{
+ return NetmapRunModeInit(NETMAP_AUTOFP);
+}
+
/**
* \brief Single thread version of the netmap processing.
*/
int RunModeIdsNetmapSingle(void)
{
- SCEnter();
-
-#ifdef HAVE_NETMAP
- int ret;
- const char *live_dev = NULL;
-
- RunModeInitialize();
- TimeModeSetLive();
-
- (void)ConfGet("netmap.live-interface", &live_dev);
-
- ret = RunModeSetLiveCaptureSingle(
- ParseNetmapConfig,
- NetmapConfigGeThreadsCount,
- "ReceiveNetmap",
- "DecodeNetmap", thread_name_single,
- live_dev);
- if (ret != 0) {
- FatalError(SC_ERR_FATAL, "Unable to start runmode");
- }
+ return NetmapRunModeInit(NETMAP_SINGLE);
+}
- SCLogDebug("RunModeIdsNetmapSingle initialised");
+/**
+ * \brief Workers version of the netmap processing.
+ *
+ * Start N threads with each thread doing all the work.
+ *
+ */
+int RunModeIdsNetmapWorkers(void)
+{
+ return NetmapRunModeInit(NETMAP_WORKERS);
+}
+#else
+int RunModeIdsNetmapAutoFp(void)
+{
+ SCEnter();
+ FatalError(SC_ERR_FATAL, "Netmap not configured");
+ SCReturnInt(0);
+}
-#endif /* HAVE_NETMAP */
+/**
+ * \brief Single thread version of the netmap processing.
+ */
+int RunModeIdsNetmapSingle(void)
+{
+ SCEnter();
+ FatalError(SC_ERR_FATAL, "Netmap not configured");
SCReturnInt(0);
}
int RunModeIdsNetmapWorkers(void)
{
SCEnter();
-
-#ifdef HAVE_NETMAP
- int ret;
- const char *live_dev = NULL;
-
- RunModeInitialize();
- TimeModeSetLive();
-
- (void)ConfGet("netmap.live-interface", &live_dev);
-
- ret = RunModeSetLiveCaptureWorkers(
- ParseNetmapConfig,
- NetmapConfigGeThreadsCount,
- "ReceiveNetmap",
- "DecodeNetmap", thread_name_workers,
- live_dev);
- if (ret != 0) {
- FatalError(SC_ERR_FATAL, "Unable to start runmode");
- }
-
- SCLogDebug("RunModeIdsNetmapWorkers initialised");
-
-#endif /* HAVE_NETMAP */
+ FatalError(SC_ERR_FATAL, "Netmap not configured");
SCReturnInt(0);
}
+#endif // #ifdef HAVE_NETMAP
/**
* @}
-/* Copyright (C) 2011-2018 Open Information Security Foundation
+/* Copyright (C) 2011-2021 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
*/
/**
-* \file
-*
-* \author Aleksey Katargin <gureedo@gmail.com>
-* \author Victor Julien <victor@inliniac.net>
-*
-* Netmap socket acquisition support
-*
-* Many thanks to Luigi Rizzo for guidance and support.
-*
-*/
-
+ * \file
+ *
+ * \author Aleksey Katargin <gureedo@gmail.com>
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Bill Meeks <billmeeks8@gmail.com>
+ *
+ * Netmap socket acquisition support
+ *
+ * Many thanks to Luigi Rizzo for guidance and support.
+ *
+ */
-#include "suricata-common.h"
#include "suricata.h"
-#include "decode.h"
-#include "threads.h"
-#include "threadvars.h"
+#include "suricata-common.h"
#include "tm-threads.h"
-#include "conf.h"
#include "util-bpf.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-validate.h"
-#include "tmqh-packetpool.h"
#include "source-netmap.h"
-#include "runmodes.h"
#ifdef HAVE_NETMAP
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#if HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
#define NETMAP_WITH_LIBS
#ifdef DEBUG
#define DEBUG_NETMAP_USER
#endif
+
#include <net/netmap_user.h>
+#include <libnetmap.h>
#endif /* HAVE_NETMAP */
*/
static TmEcode NoNetmapSupportExit(ThreadVars *tv, const 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);
+ FatalError(SC_ERR_NO_NETMAP,
+ "Error creating thread %s: Netmap is not enabled. "
+ "Make sure to pass --enable-netmap to configure when building.",
+ tv->name);
}
void TmModuleReceiveNetmapRegister (void)
#define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
#endif
-enum {
- NETMAP_OK,
- NETMAP_FAILURE,
-};
-
-enum {
- NETMAP_FLAG_ZERO_COPY = 1,
-};
+enum { NETMAP_FLAG_ZERO_COPY = 1, NETMAP_FLAG_EXCL_RING_ACCESS = 2 };
/**
* \brief Netmap device instance. Each ring for each device gets its own
*/
typedef struct NetmapDevice_
{
- struct nm_desc *nmd;
+ struct nmport_d *nmd;
unsigned int ref;
SC_ATOMIC_DECLARE(unsigned int, threads_run);
TAILQ_ENTRY(NetmapDevice_) next;
char ifname[32];
int ring;
int direction; // 0 rx, 1 tx
+
+ // autofp: Used to lock a destination ring while we are sending data.
+ SCMutex netmap_dev_lock;
} NetmapDevice;
/**
*/
typedef struct NetmapThreadVars_
{
- /* receive inteface */
+ /* receive interface */
NetmapDevice *ifsrc;
/* dst interface for IPS mode */
NetmapDevice *ifdst;
*/
int NetmapGetRSSCount(const char *ifname)
{
- struct nmreq nm_req;
+ struct nmreq_port_info_get req;
+ struct nmreq_header hdr;
int rx_rings = 0;
+ /* we need the base interface name to query queues */
+ char base_name[IFNAMSIZ];
+ strlcpy(base_name, ifname, sizeof(base_name));
+ if (strlen(base_name) > 0 &&
+ (base_name[strlen(base_name) - 1] == '^' || base_name[strlen(base_name) - 1] == '*')) {
+ base_name[strlen(base_name) - 1] = '\0';
+ }
+
SCMutexLock(&netmap_devlist_lock);
- /* open netmap */
+ /* open netmap device */
int fd = open("/dev/netmap", O_RDWR);
if (fd == -1) {
SCLogError(SC_ERR_NETMAP_CREATE,
goto error_open;
}
- /* 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;
+ /* query netmap interface info */
+ memset(&req, 0, sizeof(req));
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.nr_version = NETMAP_API;
+ hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
+ hdr.nr_body = (uintptr_t)&req;
+ strlcpy(hdr.nr_name, base_name, sizeof(hdr.nr_name));
- if (ioctl(fd, NIOCGINFO, &nm_req) != 0) {
- SCLogError(SC_ERR_NETMAP_CREATE,
- "Couldn't query netmap for %s, error %s",
+ if (ioctl(fd, NIOCCTRL, &hdr) != 0) {
+ SCLogError(SC_ERR_NETMAP_CREATE, "Couldn't query netmap for info about %s, error %s",
ifname, strerror(errno));
goto error_fd;
};
- rx_rings = nm_req.nr_rx_rings;
+ /* return RX rings count if it equals TX rings count */
+ if (req.nr_rx_rings == req.nr_tx_rings) {
+ rx_rings = req.nr_rx_rings;
+ }
error_fd:
close(fd);
return rx_rings;
}
+static void NetmapDestroyDevice(NetmapDevice *pdev)
+{
+ nmport_close(pdev->nmd);
+ SCMutexDestroy(&pdev->netmap_dev_lock);
+ SCFree(pdev);
+}
+
+/**
+ * \brief Close or dereference netmap device instance.
+ * \param dev 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) {
+ NetmapDestroyDevice(pdev);
+ }
+ SCMutexUnlock(&netmap_devlist_lock);
+ return 0;
+ }
+ }
+
+ SCMutexUnlock(&netmap_devlist_lock);
+ return -1;
+}
+
+/**
+ * \brief Close all open netmap device instances.
+ */
+static void NetmapCloseAll(void)
+{
+ NetmapDevice *pdev, *tmp;
+
+ SCMutexLock(&netmap_devlist_lock);
+
+ TAILQ_FOREACH_SAFE (pdev, &netmap_devlist, next, tmp) {
+ NetmapDestroyDevice(pdev);
+ }
+
+ SCMutexUnlock(&netmap_devlist_lock);
+}
+
/**
* \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.
+ * \param read Indicates direction: RX or TX
+ * \param zerocopy 1 if zerocopy access requested
+ * \param soft Use Host stack (software) interface
* \return Zero on success.
*/
-static int NetmapOpen(NetmapIfaceSettings *ns,
- NetmapDevice **pdevice, int verbose, int read, bool zerocopy)
+static int NetmapOpen(NetmapIfaceSettings *ns, NetmapDevice **pdevice, int verbose, int read,
+ bool zerocopy, bool soft)
{
SCEnter();
SCLogDebug("ifname %s", ns->iface);
int if_flags = GetIfaceFlags(base_name);
if (if_flags == -1) {
if (verbose) {
- SCLogError(SC_ERR_NETMAP_CREATE,
- "Can not access to interface '%s' (%s)",
+ SCLogError(SC_ERR_NETMAP_CREATE, "Cannot access network interface '%s' (%s)",
base_name, ns->iface);
}
goto error;
}
}
NetmapDevice *pdev = NULL, *spdev = NULL;
- pdev = SCMalloc(sizeof(*pdev));
+ pdev = SCCalloc(1, 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);
SCMutexLock(&netmap_devlist_lock);
const int direction = (read != 1);
int ring = 0;
- /* search interface in our already opened list */
+ /* Search for interface in our already opened list. */
+ /* We will find it when opening multiple rings on */
+ /* the device when it exposes multiple RSS queues. */
TAILQ_FOREACH(spdev, &netmap_devlist, next) {
SCLogDebug("spdev %s", spdev->ifname);
if (direction == spdev->direction && strcmp(ns->iface, spdev->ifname) == 0) {
const char *opt_x = "x"; // not for IPS
const char *opt_z = "z"; // zero copy, not for IPS
- // FreeBSD 11 doesn't have R and T.
-#if NETMAP_API<=11
- opt_R = "";
- opt_T = "";
-#endif
/* assemble options string */
char optstr[16];
if (ns->ips)
opt_x = "";
-// z seems to not play well with multiple opens of a real dev on linux
-// if (!zerocopy || ips)
+ // z seems to not play well with multiple opens of a real dev on linux
opt_z = "";
+ /*
+ * How netmap endpoint names are selected:
+ *
+ * The following logic within the "retry" loop builds endpoint names.
+ *
+ * IPS Mode:
+ * There are two endpoints: one hardware NIC and either a hardware NIC or host stack "NIC".
+ *
+ * IDS Mode:
+ * One endpoint -- usually a hardware NIC.
+ *
+ * IPS mode -- with one endpoint a host stack "NIC":
+ * When using multiple rings/threads, then the open of the initial Ring 0 MUST
+ * instruct netmap to open multiple Host Stack rings (as the default is to open only a single
+ * pair). This is also critical for the HW NIC endpoint. This is done by adding
+ * “@conf:host-rings=x” suffix option (where “x” is the number of host rings desired)
+ * to BOTH endpoint nmport_open_desc() calls for ring 0 (hardware and host stack).
+ * For subsequent additional ring open calls, omit the suffix option specifying host ring count.
+ *
+ * IPS mode -- both endpoints are hardware NICs:
+ * Do NOT pass any suffix option (even for Ring 0). You do not need to tell netmap how many
+ * rings, because it already knows the correct value from the NIC driver itself. Specifying a
+ * desired ring count when both ends are Hardware NICs confuses netmap, and it seems to default
+ * to using only a single hardware ring. In this scenario, specify only the specific ring number
+ * being opened.
+ */
+
// loop to retry opening if unsupported options are used
retry:
snprintf(optstr, sizeof(optstr), "%s%s%s", opt_z, opt_x, direction == 0 ? opt_R : opt_T);
ns->iface, ring, strlen(optstr) ? "/" : "", optstr);
} else if (strlen(ns->iface) > 5 && strncmp(ns->iface, "vale", 4) == 0 && isdigit(ns->iface[4])) {
snprintf(devname, sizeof(devname), "%s", ns->iface);
- } else if (ns->iface[strlen(ns->iface)-1] == '*' ||
- ns->iface[strlen(ns->iface)-1] == '^') {
- SCLogDebug("device with SW-ring enabled (ns->iface): %s",ns->iface);
- snprintf(devname, sizeof(devname), "netmap:%s", ns->iface);
- SCLogDebug("device with SW-ring enabled (devname): %s",devname);
- /* just a single ring, so don't use ring param */
} else if (ring == 0 && ns->threads == 1) {
+ /* just a single thread and ring, so don't use ring param */
snprintf(devname, sizeof(devname), "netmap:%s%s%s",
ns->iface, strlen(optstr) ? "/" : "", optstr);
+ SCLogDebug("device with %s-ring enabled (devname): %s", soft ? "SW" : "HW", devname);
} else {
- snprintf(devname, sizeof(devname), "netmap:%s-%d%s%s",
- ns->iface, ring, strlen(optstr) ? "/" : "", optstr);
+ /* Going to be using multiple threads and rings */
+ if (ns->sw_ring) {
+ /* Opening a host stack interface */
+ if (ring == 0) {
+ /* Ring 0, so tell netmap how many host rings we want created */
+ snprintf(devname, sizeof(devname), "netmap:%s%d%s%s@conf:host-rings=%d", ns->iface,
+ ring, strlen(optstr) ? "/" : "", optstr, ns->threads);
+ } else {
+ /* Software (host) ring, but not initial open of ring 0 */
+ snprintf(devname, sizeof(devname), "netmap:%s%d%s%s", ns->iface, ring,
+ strlen(optstr) ? "/" : "", optstr);
+ }
+ SCLogDebug("device with SW-ring enabled (devname): %s", devname);
+ } else if (ring == 0 && soft) {
+ /* Ring 0 of HW endpoint, and other endpoint is SW stack,
+ * so request SW host stack rings to match HW rings count.
+ */
+ snprintf(devname, sizeof(devname), "netmap:%s-%d%s%s@conf:host-rings=%d", ns->iface,
+ ring, strlen(optstr) ? "/" : "", optstr, ns->threads);
+ SCLogDebug("device with HW-ring enabled (devname): %s", devname);
+ } else {
+ /* Hardware ring other than ring 0, or both endpoints are HW
+ * and there is no host stack (SW) endpoint */
+ snprintf(devname, sizeof(devname), "netmap:%s-%d%s%s", ns->iface, ring,
+ strlen(optstr) ? "/" : "", optstr);
+ SCLogDebug("device with HW-ring enabled (devname): %s", devname);
+ }
}
+
strlcpy(pdev->ifname, ns->iface, sizeof(pdev->ifname));
- pdev->nmd = nm_open(devname, NULL, 0, NULL);
+ /* have the netmap API parse device name and prepare the port descriptor for us */
+ pdev->nmd = nmport_prepare(devname);
+
+ if (pdev->nmd != NULL) {
+ /* For RX devices, set the nr_mode flag we need on the netmap port TX rings prior to opening
+ */
+ if (read) {
+ pdev->nmd->reg.nr_flags |= NR_NO_TX_POLL;
+ }
+
+ /* Now attempt to actually open the netmap port descriptor */
+ if (nmport_open_desc(pdev->nmd) < 0) {
+ /* the open failed, so clean-up the descriptor and fall through to error handler */
+ nmport_close(pdev->nmd);
+ pdev->nmd = NULL;
+ }
+ }
+
if (pdev->nmd == NULL) {
- if (errno == EINVAL && opt_z[0] == 'z') {
- SCLogNotice("got '%s' EINVAL: going to retry without 'z'", devname);
- opt_z = "";
- goto retry;
- } else if (errno == EINVAL && opt_x[0] == 'x') {
- SCLogNotice("dev '%s' got EINVAL: going to retry without 'x'", devname);
- opt_x = "";
- goto retry;
+ if (errno == EINVAL) {
+ if (opt_z[0] == 'z') {
+ SCLogNotice("got '%s' EINVAL: going to retry without 'z'", devname);
+ opt_z = "";
+ goto retry;
+ } else if (opt_x[0] == 'x') {
+ SCLogNotice("dev '%s' got EINVAL: going to retry without 'x'", devname);
+ opt_x = "";
+ goto retry;
+ }
}
- SCLogError(SC_ERR_NETMAP_CREATE, "opening devname %s failed: %s",
- devname, strerror(errno));
- exit(EXIT_FAILURE);
+ NetmapCloseAll();
+ FatalError(SC_ERR_FATAL, "opening devname %s failed: %s", devname, strerror(errno));
}
- SCLogDebug("devname %s %s opened", devname, ns->iface);
+
+ /* Work around bug in libnetmap library where "cur_{r,t}x_ring" values not initialized */
+ SCLogDebug("%s -- cur rings: [%d, %d] first rings: [%d, %d]", devname, pdev->nmd->cur_rx_ring,
+ pdev->nmd->cur_tx_ring, pdev->nmd->first_rx_ring, pdev->nmd->first_tx_ring);
+ pdev->nmd->cur_rx_ring = pdev->nmd->first_rx_ring;
+ pdev->nmd->cur_tx_ring = pdev->nmd->first_tx_ring;
+
+ SCLogInfo("devname [fd: %d] %s %s opened", pdev->nmd->fd, devname, ns->iface);
pdev->direction = direction;
pdev->ring = ring;
+ SCMutexInit(&pdev->netmap_dev_lock, NULL);
TAILQ_INSERT_TAIL(&netmap_devlist, pdev, next);
- SCLogNotice("opened %s from %s: %p", devname, ns->iface, pdev->nmd);
SCMutexUnlock(&netmap_devlist_lock);
*pdevice = pdev;
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) {
- nm_close(pdev->nmd);
- SCFree(pdev);
- }
- SCMutexUnlock(&netmap_devlist_lock);
- return 0;
- }
- }
-
- SCMutexUnlock(&netmap_devlist_lock);
- return -1;
-}
-
/**
* \brief PcapDumpCounters
* \param ntv
static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, const void *initdata, void **data)
{
SCEnter();
- NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)initdata;
+ NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)initdata;
if (initdata == NULL) {
SCLogError(SC_ERR_INVALID_ARGUMENT, "initdata == NULL");
SCReturnInt(TM_ECODE_FAILED);
}
- NetmapThreadVars *ntv = SCMalloc(sizeof(*ntv));
+ NetmapThreadVars *ntv = SCCalloc(1, 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->in.checksum_mode;
- ntv->copy_mode = aconf->in.copy_mode;
ntv->livedev = LiveGetDevice(aconf->iface_name);
if (ntv->livedev == NULL) {
goto error_ntv;
}
+ ntv->tv = tv;
+ ntv->checksum_mode = aconf->in.checksum_mode;
+ ntv->copy_mode = aconf->in.copy_mode;
+
/* enable zero-copy mode for workers runmode */
char const *active_runmode = RunmodeGetActive();
if (strcmp("workers", active_runmode) == 0) {
ntv->flags |= NETMAP_FLAG_ZERO_COPY;
SCLogDebug("Enabling zero copy mode for %s", aconf->in.iface);
+ } else if (strcmp("autofp", active_runmode) == 0) {
+ ntv->flags |= NETMAP_FLAG_EXCL_RING_ACCESS;
}
- if (NetmapOpen(&aconf->in, &ntv->ifsrc, 1, 1,
- (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0) != 0) {
+ /* Need to insure open of ring 0 conveys requested ring count for open */
+ bool soft = aconf->in.sw_ring || aconf->out.sw_ring;
+ if (NetmapOpen(&aconf->in, &ntv->ifsrc, 1, 1, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
+ soft) != 0) {
goto error_ntv;
}
- if (unlikely(aconf->in.sw_ring && aconf->in.threads > 1)) {
- SCLogError(SC_ERR_INVALID_VALUE,
- "Interface '%s+'. "
- "Thread count can't be greater than 1 for SW ring.",
- aconf->iface_name);
- goto error_src;
- }
-
if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
- SCLogDebug("IPS: opening out iface %s", aconf->out.iface);
- if (NetmapOpen(&aconf->out, &ntv->ifdst,
- 1, 0, false) != 0) {
+ if (NetmapOpen(&aconf->out, &ntv->ifdst, 1, 0, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
+ soft) != 0) {
goto error_src;
}
}
}
}
+ SCLogDebug("thread: %s polling on fd: %d", tv->name, ntv->ifsrc->nmd->fd);
+
*data = (void *)ntv;
aconf->DerefFunc(aconf);
SCReturnInt(TM_ECODE_OK);
+
error_dst:
if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
NetmapClose(ntv->ifdst);
}
+
error_src:
NetmapClose(ntv->ifsrc);
+
error_ntv:
SCFree(ntv);
+
error:
aconf->DerefFunc(aconf);
SCReturnInt(TM_ECODE_FAILED);
}
DEBUG_VALIDATE_BUG_ON(ntv->ifdst == NULL);
- if (nm_inject(ntv->ifdst->nmd, GET_PKT_DATA(p), GET_PKT_LEN(p)) == 0) {
- SCLogDebug("failed to send %s -> %s",
- ntv->ifsrc->ifname, ntv->ifdst->ifname);
+ /* Lock the destination netmap ring while writing to it */
+ if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
+ SCMutexLock(&ntv->ifdst->netmap_dev_lock);
+ }
+
+ /* attempt to write the packet into the netmap ring buffer(s) */
+ if (nmport_inject(ntv->ifdst->nmd, GET_PKT_DATA(p), GET_PKT_LEN(p)) == 0) {
+ if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
+ SCMutexUnlock(&ntv->ifdst->netmap_dev_lock);
+ }
+ SCLogDebug("failed to send %s -> %s", ntv->ifsrc->ifname, ntv->ifdst->ifname);
ntv->drops++;
+ return TM_ECODE_FAILED;
}
- SCLogDebug("sent succesfully: %s(%d)->%s(%d) (%u)",
- ntv->ifsrc->ifname, ntv->ifsrc->ring,
+
+ SCLogDebug("sent successfully: %s(%d)->%s(%d) (%u)", ntv->ifsrc->ifname, ntv->ifsrc->ring,
ntv->ifdst->ifname, ntv->ifdst->ring, GET_PKT_LEN(p));
+ /* Instruct netmap to push the data on the TX ring on the destination port */
ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
+ if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
+ SCMutexUnlock(&ntv->ifdst->netmap_dev_lock);
+ }
return TM_ECODE_OK;
}
PacketFreeOrRelease(p);
}
-static void NetmapCallback(u_char *user, const struct nm_pkthdr *ph, const u_char *d)
+static void NetmapProcessPacket(NetmapThreadVars *ntv, const struct nm_pkthdr *ph)
{
- NetmapThreadVars *ntv = (NetmapThreadVars *)user;
-
if (ntv->bpf_prog.bf_len) {
struct pcap_pkthdr pkthdr = { {0, 0}, ph->len, ph->len };
- if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, d) == 0) {
+ if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, ph->buf) == 0) {
return;
}
}
ntv->bytes += ph->len;
if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
- if (PacketSetData(p, (uint8_t *)d, ph->len) == -1) {
+ if (PacketSetData(p, (uint8_t *)ph->buf, ph->len) == -1) {
TmqhOutputPacketpool(ntv->tv, p);
return;
}
} else {
- if (PacketCopyData(p, (uint8_t *)d, ph->len) == -1) {
+ if (PacketCopyData(p, (uint8_t *)ph->buf, ph->len) == -1) {
TmqhOutputPacketpool(ntv->tv, p);
return;
}
(void)TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p);
}
+/**
+ * \brief Copy netmap rings data into Packet structures.
+ * \param *d nmport_d (or nm_desc) netmap if structure.
+ * \param cnt int count of packets to read (-1 = all).
+ * \param *ntv NetmapThreadVars.
+ */
+static TmEcode NetmapReadPackets(struct nmport_d *d, int cnt, NetmapThreadVars *ntv)
+{
+ struct nm_pkthdr hdr;
+ int last_ring = d->last_rx_ring - d->first_rx_ring + 1;
+ int cur_ring, got = 0, cur_rx_ring = d->cur_rx_ring;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.flags = NM_MORE_PKTS;
+
+ if (cnt == 0)
+ cnt = -1;
+
+ for (cur_ring = 0; cur_ring < last_ring && cnt != got; cur_ring++, cur_rx_ring++) {
+ struct netmap_ring *ring;
+
+ if (cur_rx_ring > d->last_rx_ring)
+ cur_rx_ring = d->first_rx_ring;
+
+ ring = NETMAP_RXRING(d->nifp, cur_rx_ring);
+
+ /* cycle through the non-empty ring slots to fetch their data */
+ for (; !nm_ring_empty(ring) && cnt != got; got++) {
+ u_int idx, i;
+ u_char *oldbuf;
+ struct netmap_slot *slot;
+
+ if (hdr.buf) { /* from previous round */
+ NetmapProcessPacket(ntv, &hdr);
+ }
+
+ i = ring->cur;
+ slot = &ring->slot[i];
+ idx = slot->buf_idx;
+ d->cur_rx_ring = cur_rx_ring;
+ hdr.slot = slot;
+ oldbuf = hdr.buf = (u_char *)NETMAP_BUF(ring, idx);
+ hdr.len = hdr.caplen = slot->len;
+
+ /* loop through the ring slots to get packet data */
+ while (slot->flags & NS_MOREFRAG) {
+ /* packet can be fragmented across multiple slots, */
+ /* so loop until we find the slot with the flag */
+ /* cleared, signalling the end of the packet data. */
+ u_char *nbuf;
+ u_int oldlen = slot->len;
+ i = nm_ring_next(ring, i);
+ slot = &ring->slot[i];
+ hdr.len += slot->len;
+ nbuf = (u_char *)NETMAP_BUF(ring, slot->buf_idx);
+
+ if (oldbuf != NULL && nbuf - oldbuf == ring->nr_buf_size &&
+ oldlen == ring->nr_buf_size) {
+ hdr.caplen += slot->len;
+ oldbuf = nbuf;
+ } else {
+ oldbuf = NULL;
+ }
+ }
+
+ hdr.ts = ring->ts;
+ ring->head = ring->cur = nm_ring_next(ring, i);
+ }
+ }
+
+ if (hdr.buf) { /* from previous round */
+ hdr.flags = 0;
+ NetmapProcessPacket(ntv, &hdr);
+ }
+ return got;
+}
+
/**
* \brief Main netmap reading loop function
*/
fds.fd = ntv->ifsrc->nmd->fd;
fds.events = POLLIN;
+ SCLogDebug("thread %s polling on %d", tv->name, fds.fd);
for(;;) {
if (unlikely(suricata_ctl_flags != 0)) {
break;
PacketPoolWait();
int r = poll(&fds, 1, 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));
+ "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->src_ring_from, ntv->src_ring_to);
-
/* sync counters */
NetmapDumpCounters(ntv);
StatsSyncCountersIfSignalled(tv);
if (unlikely(fds.revents & POLL_EVENTS)) {
if (fds.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.revents & POLLNVAL) {
SCLogError(SC_ERR_NETMAP_READ,
- "Invalid polling request");
+ "Error reading netmap data via polling from iface '%s': (%d" PRIu32 ") %s",
+ ntv->ifsrc->ifname, errno, strerror(errno));
+ } else if (fds.revents & POLLNVAL) {
+ SCLogError(SC_ERR_NETMAP_READ, "Invalid polling request");
}
continue;
}
if (likely(fds.revents & POLLIN)) {
- nm_dispatch(ntv->ifsrc->nmd, -1, NetmapCallback, (void *)ntv);
+ /* have data on RX ring, so copy to Packet for processing */
+ NetmapReadPackets(ntv->ifsrc->nmd, -1, ntv);
}
NetmapDumpCounters(ntv);
/**
* \brief Prepare netmap decode thread.
- * \param tv Thread local avariables.
+ * \param tv Thread local variables.
* \param initdata Thread config.
* \param data Pointer to DecodeThreadVars placed here.
*/