suricata
source-netmap.c
Go to the documentation of this file.
1/* Copyright (C) 2011-2022 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19* \defgroup netmap Netmap running mode
20*
21* @{
22*/
23
24/**
25 * \file
26 *
27 * \author Aleksey Katargin <gureedo@gmail.com>
28 * \author Victor Julien <victor@inliniac.net>
29 * \author Bill Meeks <billmeeks8@gmail.com>
30 *
31 * Netmap socket acquisition support
32 *
33 * Many thanks to Luigi Rizzo for guidance and support.
34 *
35 */
36
37#include "suricata.h"
38#include "suricata-common.h"
39#include "tm-threads.h"
40#include "packet.h"
41#include "util-bpf.h"
42#include "util-privs.h"
43#include "util-validate.h"
44#include "util-datalink.h"
45#include "util-device-private.h"
46
47#include "source-netmap.h"
48
49#ifdef HAVE_NETMAP
50
51#define NETMAP_WITH_LIBS
52#ifdef DEBUG
53#define DEBUG_NETMAP_USER
54#endif
55
56#include <net/netmap_user.h>
57#include <libnetmap.h>
58
59#endif /* HAVE_NETMAP */
60
61#include "util-ioctl.h"
62
63#ifndef HAVE_NETMAP
64
65/**
66* \brief this function prints an error message and exits.
67*/
68static TmEcode NoNetmapSupportExit(ThreadVars *tv, const void *initdata, void **data)
69{
70 FatalError("Error creating thread %s: Netmap is not enabled. "
71 "Make sure to pass --enable-netmap to configure when building.",
72 tv->name);
73}
74
81
82/**
83* \brief Registration Function for DecodeNetmap.
84*/
91
92#else /* We have NETMAP support */
93
94#include "action-globals.h"
95
96#define POLL_TIMEOUT 100
97
98#if defined(__linux__)
99#define POLL_EVENTS (POLLHUP|POLLRDHUP|POLLERR|POLLNVAL)
100
101#ifndef IFF_PPROMISC
102#define IFF_PPROMISC IFF_PROMISC
103#endif
104
105#else
106#define POLL_EVENTS (POLLHUP|POLLERR|POLLNVAL)
107#endif
108
109enum { NETMAP_FLAG_ZERO_COPY = 1, NETMAP_FLAG_EXCL_RING_ACCESS = 2 };
110
111/**
112 * \brief Netmap device instance. Each ring for each device gets its own
113 * device.
114 */
115typedef struct NetmapDevice_
116{
117 struct nmport_d *nmd;
118 unsigned int ref;
119 SC_ATOMIC_DECLARE(unsigned int, threads_run);
120 TAILQ_ENTRY(NetmapDevice_) next;
121 // actual ifname can only be 16, but we store a bit more,
122 // like the options string and a 'netmap:' prefix.
123 char ifname[32];
124 int ring;
125 int direction; // 0 rx, 1 tx
126
127 // autofp: Used to lock a destination ring while we are sending data.
128 SCMutex netmap_dev_lock;
129} NetmapDevice;
130
131/**
132 * \brief Module thread local variables.
133 */
134typedef struct NetmapThreadVars_
135{
136 /* receive interface */
137 NetmapDevice *ifsrc;
138 /* dst interface for IPS mode */
139 NetmapDevice *ifdst;
140
141 int flags;
142 struct bpf_program bpf_prog;
143
144 /* suricata internals */
145 TmSlot *slot;
146 ThreadVars *tv;
147 LiveDevice *livedev;
148
149 /* copy from config */
150 int copy_mode;
151 ChecksumValidationMode checksum_mode;
152
153 /* counters */
154 uint64_t pkts;
155 uint64_t bytes;
156 uint64_t drops;
157 uint16_t capture_kernel_packets;
158 uint16_t capture_kernel_drops;
159} NetmapThreadVars;
160
161typedef TAILQ_HEAD(NetmapDeviceList_, NetmapDevice_) NetmapDeviceList;
162
163static NetmapDeviceList netmap_devlist = TAILQ_HEAD_INITIALIZER(netmap_devlist);
164static SCMutex netmap_devlist_lock = SCMUTEX_INITIALIZER;
165
166/** \brief get RSS RX-queue count
167 * \retval rx_rings RSS RX queue count or 0 on error
168 */
169int NetmapGetRSSCount(const char *ifname)
170{
171 struct nmreq_port_info_get req;
172 struct nmreq_header hdr;
173 int rx_rings = 0;
174
175 /* we need the base interface name to query queues */
176 char base_name[IFNAMSIZ];
177 strlcpy(base_name, ifname, sizeof(base_name));
178 if (strlen(base_name) > 0 &&
179 (base_name[strlen(base_name) - 1] == '^' || base_name[strlen(base_name) - 1] == '*')) {
180 base_name[strlen(base_name) - 1] = '\0';
181 }
182
183 SCMutexLock(&netmap_devlist_lock);
184
185 /* open netmap device */
186 int fd = open("/dev/netmap", O_RDWR);
187 if (fd == -1) {
188 SCLogError("%s: open netmap device failed: %s", ifname, strerror(errno));
189 goto error_open;
190 }
191
192 /* query netmap interface info for ring count */
193 memset(&req, 0, sizeof(req));
194 memset(&hdr, 0, sizeof(hdr));
195 hdr.nr_version = NETMAP_API;
196 hdr.nr_reqtype = NETMAP_REQ_PORT_INFO_GET;
197 hdr.nr_body = (uintptr_t)&req;
198 strlcpy(hdr.nr_name, base_name, sizeof(hdr.nr_name));
199
200 if (ioctl(fd, NIOCCTRL, &hdr) != 0) {
202 "Query of netmap HW rings count on %s failed; error: %s", ifname, strerror(errno));
203 goto error_fd;
204 };
205
206 /* return RX rings count if it equals TX rings count */
207 if (req.nr_rx_rings == req.nr_tx_rings) {
208 rx_rings = req.nr_rx_rings;
209 }
210
211error_fd:
212 close(fd);
213error_open:
214 SCMutexUnlock(&netmap_devlist_lock);
215 return rx_rings;
216}
217
218static void NetmapDestroyDevice(NetmapDevice *pdev)
219{
220 nmport_close(pdev->nmd);
221 SCMutexDestroy(&pdev->netmap_dev_lock);
222 SCFree(pdev);
223}
224
225/**
226 * \brief Close or dereference netmap device instance.
227 * \param dev Netmap device instance.
228 * \return Zero on success.
229 */
230static int NetmapClose(NetmapDevice *dev)
231{
232 NetmapDevice *pdev, *tmp;
233
234 SCMutexLock(&netmap_devlist_lock);
235
236 TAILQ_FOREACH_SAFE (pdev, &netmap_devlist, next, tmp) {
237 if (pdev == dev) {
238 pdev->ref--;
239 if (!pdev->ref) {
240 NetmapDestroyDevice(pdev);
241 }
242 SCMutexUnlock(&netmap_devlist_lock);
243 return 0;
244 }
245 }
246
247 SCMutexUnlock(&netmap_devlist_lock);
248 return -1;
249}
250
251/**
252 * \brief Close all open netmap device instances.
253 */
254static void NetmapCloseAll(void)
255{
256 NetmapDevice *pdev, *tmp;
257
258 SCMutexLock(&netmap_devlist_lock);
259
260 TAILQ_FOREACH_SAFE (pdev, &netmap_devlist, next, tmp) {
261 NetmapDestroyDevice(pdev);
262 }
263
264 SCMutexUnlock(&netmap_devlist_lock);
265}
266
267/**
268 * \brief Open interface in netmap mode.
269 * \param ifname Interface name.
270 * \param promisc Enable promiscuous mode.
271 * \param dev Pointer to requested netmap device instance.
272 * \param verbose Verbose error logging.
273 * \param read Indicates direction: RX or TX
274 * \param zerocopy 1 if zerocopy access requested
275 * \param soft Use Host stack (software) interface
276 * \return Zero on success.
277 */
278static int NetmapOpen(NetmapIfaceSettings *ns, NetmapDevice **pdevice, int verbose, int read,
279 bool zerocopy, bool soft)
280{
281 SCEnter();
282 SCLogDebug("ifname %s", ns->iface);
283
284 char base_name[IFNAMSIZ];
285 strlcpy(base_name, ns->iface, sizeof(base_name));
286 if (strlen(base_name) > 0 &&
287 (base_name[strlen(base_name)-1] == '^' ||
288 base_name[strlen(base_name)-1] == '*'))
289 {
290 base_name[strlen(base_name)-1] = '\0';
291 }
292
293 if (ns->real) {
294 /* check interface is up */
295 int if_flags = GetIfaceFlags(base_name);
296 if (if_flags == -1) {
297 if (verbose) {
298 SCLogError("%s: cannot access network interface: %s", base_name, ns->iface);
299 }
300 goto error;
301 }
302
303 /* bring iface up if it is down */
304 if ((if_flags & IFF_UP) == 0) {
305 SCLogError("%s: interface is down", base_name);
306 goto error;
307 }
308 /* if needed, try to set iface in promisc mode */
309 if (ns->promisc && (if_flags & (IFF_PROMISC|IFF_PPROMISC)) == 0) {
310 if_flags |= IFF_PPROMISC;
311 SetIfaceFlags(base_name, if_flags); // TODO reset at exit
312 // TODO move to parse config?
313 }
314 }
315 NetmapDevice *pdev = NULL, *spdev = NULL;
316 pdev = SCCalloc(1, sizeof(*pdev));
317 if (unlikely(pdev == NULL)) {
318 SCLogError("%s: memory allocation failed", base_name);
319 goto error;
320 }
321 SC_ATOMIC_INIT(pdev->threads_run);
322
323 SCMutexLock(&netmap_devlist_lock);
324
325 const int direction = (read != 1);
326 int ring = 0;
327 /* Search for interface in our already opened list. */
328 /* We will find it when opening multiple rings on */
329 /* the device when it exposes multiple RSS queues. */
330 TAILQ_FOREACH(spdev, &netmap_devlist, next) {
331 SCLogDebug("spdev %s", spdev->ifname);
332 if (direction == spdev->direction && strcmp(ns->iface, spdev->ifname) == 0) {
333 ring = spdev->ring + 1;
334 }
335 }
336 SCLogDebug("netmap/%s: using ring %d", ns->iface, ring);
337
338 const char *opt_R = "R";
339 const char *opt_T = "T";
340 const char *opt_x = "x"; // not for IPS
341 const char *opt_z = "z"; // zero copy, not for IPS
342
343 /* assemble options string */
344 char optstr[16];
345 if (ns->ips)
346 opt_x = "";
347 // z seems to not play well with multiple opens of a real dev on linux
348 opt_z = "";
349
350 /*
351 * How netmap endpoint names are selected:
352 *
353 * The following logic within the "retry" loop builds endpoint names.
354 *
355 * IPS Mode:
356 * There are two endpoints: one hardware NIC and either a hardware NIC or host stack "NIC".
357 *
358 * IDS Mode:
359 * One endpoint -- usually a hardware NIC.
360 *
361 * IPS mode -- with one endpoint a host stack "NIC":
362 * When using multiple rings/threads, then the open of the initial Ring 0 MUST
363 * instruct netmap to open multiple Host Stack rings (as the default is to open only a single
364 * pair). This is also critical for the HW NIC endpoint. This is done by adding
365 * “@conf:host-rings=x” suffix option (where “x” is the number of host rings desired)
366 * to BOTH endpoint nmport_open_desc() calls for ring 0 (hardware and host stack).
367 * For subsequent additional ring open calls, omit the suffix option specifying host ring count.
368 *
369 * IPS mode -- both endpoints are hardware NICs:
370 * Do NOT pass any suffix option (even for Ring 0). You do not need to tell netmap how many
371 * rings, because it already knows the correct value from the NIC driver itself. Specifying a
372 * desired ring count when both ends are Hardware NICs confuses netmap, and it seems to default
373 * to using only a single hardware ring. In this scenario, specify only the specific ring number
374 * being opened.
375 */
376
377 // loop to retry opening if unsupported options are used
378retry:
379 snprintf(optstr, sizeof(optstr), "%s%s%s", opt_z, opt_x, direction == 0 ? opt_R : opt_T);
380
381 char devname[128];
382 if (strncmp(ns->iface, "netmap:", 7) == 0) {
383 snprintf(devname, sizeof(devname), "%s}%d%s%s",
384 ns->iface, ring, strlen(optstr) ? "/" : "", optstr);
385 } else if (strlen(ns->iface) > 5 && strncmp(ns->iface, "vale", 4) == 0 && isdigit(ns->iface[4])) {
386 snprintf(devname, sizeof(devname), "%s", ns->iface);
387 } else if (ring == 0 && ns->threads == 1) {
388 /* just a single thread and ring, so don't use ring param */
389 snprintf(devname, sizeof(devname), "netmap:%s%s%s",
390 ns->iface, strlen(optstr) ? "/" : "", optstr);
391 SCLogDebug("device with %s-ring enabled (devname): %s", soft ? "SW" : "HW", devname);
392 } else {
393 /* Going to be using multiple threads and rings */
394 if (ns->sw_ring) {
395 /* Opening a host stack interface */
396 if (ring == 0) {
397 /* Ring 0, so tell netmap how many host rings we want created */
398 snprintf(devname, sizeof(devname), "netmap:%s%d%s%s@conf:host-rings=%d", ns->iface,
399 ring, strlen(optstr) ? "/" : "", optstr, ns->threads);
400 } else {
401 /* Software (host) ring, but not initial open of ring 0 */
402 snprintf(devname, sizeof(devname), "netmap:%s%d%s%s", ns->iface, ring,
403 strlen(optstr) ? "/" : "", optstr);
404 }
405 SCLogDebug("device with SW-ring enabled (devname): %s", devname);
406 } else if (ring == 0 && soft) {
407 /* Ring 0 of HW endpoint, and other endpoint is SW stack,
408 * so request SW host stack rings to match HW rings count.
409 */
410 snprintf(devname, sizeof(devname), "netmap:%s-%d%s%s@conf:host-rings=%d", ns->iface,
411 ring, strlen(optstr) ? "/" : "", optstr, ns->threads);
412 SCLogDebug("device with HW-ring enabled (devname): %s", devname);
413 } else {
414 /* Hardware ring other than ring 0, or both endpoints are HW
415 * and there is no host stack (SW) endpoint */
416 snprintf(devname, sizeof(devname), "netmap:%s-%d%s%s", ns->iface, ring,
417 strlen(optstr) ? "/" : "", optstr);
418 SCLogDebug("device with HW-ring enabled (devname): %s", devname);
419 }
420 }
421
422 strlcpy(pdev->ifname, ns->iface, sizeof(pdev->ifname));
423
424 /* have the netmap API parse device name and prepare the port descriptor for us */
425 pdev->nmd = nmport_prepare(devname);
426
427 if (pdev->nmd != NULL) {
428 /* For RX devices, set the nr_mode flag we need on the netmap port TX rings prior to opening
429 */
430 if (read) {
431 pdev->nmd->reg.nr_flags |= NR_NO_TX_POLL;
432 }
433
434 /* Now attempt to actually open the netmap port descriptor */
435 if (nmport_open_desc(pdev->nmd) < 0) {
436 /* the open failed, so clean-up the descriptor and fall through to error handler */
437 nmport_close(pdev->nmd);
438 pdev->nmd = NULL;
439 }
440 }
441
442 if (pdev->nmd == NULL) {
443 if (errno == EINVAL) {
444 if (opt_z[0] == 'z') {
446 "%s: dev '%s' got EINVAL: going to retry without 'z'", base_name, devname);
447 opt_z = "";
448 goto retry;
449 } else if (opt_x[0] == 'x') {
451 "%s: dev '%s' got EINVAL: going to retry without 'x'", base_name, devname);
452 opt_x = "";
453 goto retry;
454 }
455 }
456
457 SCMutexUnlock(&netmap_devlist_lock);
458 NetmapCloseAll();
459 FatalError("opening devname %s failed: %s", devname, strerror(errno));
460 }
461
462 /* Work around bug in libnetmap library where "cur_{r,t}x_ring" values not initialized */
463 SCLogDebug("%s -- cur rings: [%d, %d] first rings: [%d, %d]", devname, pdev->nmd->cur_rx_ring,
464 pdev->nmd->cur_tx_ring, pdev->nmd->first_rx_ring, pdev->nmd->first_tx_ring);
465 pdev->nmd->cur_rx_ring = pdev->nmd->first_rx_ring;
466 pdev->nmd->cur_tx_ring = pdev->nmd->first_tx_ring;
467
468 SCLogInfo("%s: %s opened [fd: %d]", devname, ns->iface, pdev->nmd->fd);
469
470 pdev->direction = direction;
471 pdev->ring = ring;
472 SCMutexInit(&pdev->netmap_dev_lock, NULL);
473 TAILQ_INSERT_TAIL(&netmap_devlist, pdev, next);
474
475 SCMutexUnlock(&netmap_devlist_lock);
476 *pdevice = pdev;
477
478 return 0;
479error:
480 return -1;
481}
482
483/**
484 * \brief PcapDumpCounters
485 * \param ntv
486 */
487static inline void NetmapDumpCounters(NetmapThreadVars *ntv)
488{
489 StatsAddUI64(ntv->tv, ntv->capture_kernel_packets, ntv->pkts);
490 StatsAddUI64(ntv->tv, ntv->capture_kernel_drops, ntv->drops);
491 (void) SC_ATOMIC_ADD(ntv->livedev->drop, ntv->drops);
492 (void) SC_ATOMIC_ADD(ntv->livedev->pkts, ntv->pkts);
493 ntv->drops = 0;
494 ntv->pkts = 0;
495}
496
497/**
498 * \brief Init function for ReceiveNetmap.
499 * \param tv pointer to ThreadVars
500 * \param initdata pointer to the interface passed from the user
501 * \param data pointer gets populated with NetmapThreadVars
502 */
503static TmEcode ReceiveNetmapThreadInit(ThreadVars *tv, const void *initdata, void **data)
504{
505 SCEnter();
506
507 NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)initdata;
508 if (initdata == NULL) {
509 SCLogError("initdata == NULL");
511 }
512
513 NetmapThreadVars *ntv = SCCalloc(1, sizeof(*ntv));
514 if (unlikely(ntv == NULL)) {
515 SCLogError("Memory allocation failed");
516 goto error;
517 }
518
519 ntv->livedev = LiveGetDevice(aconf->iface_name);
520 if (ntv->livedev == NULL) {
521 SCLogError("Unable to find Live device");
522 goto error_ntv;
523 }
524
525 ntv->tv = tv;
526 ntv->checksum_mode = aconf->in.checksum_mode;
527 ntv->copy_mode = aconf->in.copy_mode;
528
529 /* enable zero-copy mode for workers runmode */
530 char const *active_runmode = RunmodeGetActive();
531 if (strcmp("workers", active_runmode) == 0) {
532 ntv->flags |= NETMAP_FLAG_ZERO_COPY;
533 SCLogDebug("Enabling zero copy mode for %s", aconf->in.iface);
534 } else if (strcmp("autofp", active_runmode) == 0) {
535 ntv->flags |= NETMAP_FLAG_EXCL_RING_ACCESS;
536 }
537
538 /* Need to insure open of ring 0 conveys requested ring count for open */
539 bool soft = aconf->in.sw_ring || aconf->out.sw_ring;
540 if (NetmapOpen(&aconf->in, &ntv->ifsrc, 1, 1, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
541 soft) != 0) {
542 goto error_ntv;
543 }
544
545 if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
546 if (NetmapOpen(&aconf->out, &ntv->ifdst, 1, 0, (ntv->flags & NETMAP_FLAG_ZERO_COPY) != 0,
547 soft) != 0) {
548 goto error_src;
549 }
550 }
551
552 /* basic counters */
553 ntv->capture_kernel_packets = StatsRegisterCounter("capture.kernel_packets",
554 ntv->tv);
555 ntv->capture_kernel_drops = StatsRegisterCounter("capture.kernel_drops",
556 ntv->tv);
557
558 if (aconf->in.bpf_filter) {
559 SCLogConfig("%s: using BPF '%s'", ntv->ifsrc->ifname, aconf->in.bpf_filter);
560 char errbuf[PCAP_ERRBUF_SIZE];
561 if (SCBPFCompile(default_packet_size, /* snaplen_arg */
562 LINKTYPE_ETHERNET, /* linktype_arg */
563 &ntv->bpf_prog, /* program */
564 aconf->in.bpf_filter, /* const char *buf */
565 1, /* optimize */
566 PCAP_NETMASK_UNKNOWN, /* mask */
567 errbuf,
568 sizeof(errbuf)) == -1)
569 {
570 SCLogError("%s: failed to compile BPF \"%s\": %s", ntv->ifsrc->ifname,
571 aconf->in.bpf_filter, errbuf);
572 goto error_dst;
573 }
574 }
575
576 SCLogDebug("thread: %s polling on fd: %d", tv->name, ntv->ifsrc->nmd->fd);
577
579
580 *data = (void *)ntv;
581 aconf->DerefFunc(aconf);
583
584error_dst:
585 if (aconf->in.copy_mode != NETMAP_COPY_MODE_NONE) {
586 NetmapClose(ntv->ifdst);
587 }
588
589error_src:
590 NetmapClose(ntv->ifsrc);
591
592error_ntv:
593 SCFree(ntv);
594
595error:
596 aconf->DerefFunc(aconf);
598}
599
600/**
601 * \brief Output packet to destination interface or drop.
602 * \param ntv Thread local variables.
603 * \param p Source packet.
604 */
605static TmEcode NetmapWritePacket(NetmapThreadVars *ntv, Packet *p)
606{
607 if (ntv->copy_mode == NETMAP_COPY_MODE_IPS) {
609 return TM_ECODE_OK;
610 }
611 }
612 DEBUG_VALIDATE_BUG_ON(ntv->ifdst == NULL);
613
614 /* Lock the destination netmap ring while writing to it */
615 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
616 SCMutexLock(&ntv->ifdst->netmap_dev_lock);
617 }
618
619 int write_tries = 0;
620try_write:
621 /* attempt to write the packet into the netmap ring buffer(s) */
622 if (nmport_inject(ntv->ifdst->nmd, GET_PKT_DATA(p), GET_PKT_LEN(p)) == 0) {
623
624 /* writing the packet failed, but ask kernel to sync TX rings
625 * for us as the ring buffers may simply be full */
626 (void)ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
627
628 /* Try write up to 2 more times before giving up */
629 if (write_tries < 3) {
630 write_tries++;
631 goto try_write;
632 }
633
634 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
635 SCMutexUnlock(&ntv->ifdst->netmap_dev_lock);
636 }
637 SCLogDebug("failed to send %s -> %s", ntv->ifsrc->ifname, ntv->ifdst->ifname);
638 ntv->drops++;
639 return TM_ECODE_FAILED;
640 }
641
642 SCLogDebug("sent successfully: %s(%d)->%s(%d) (%u)", ntv->ifsrc->ifname, ntv->ifsrc->ring,
643 ntv->ifdst->ifname, ntv->ifdst->ring, GET_PKT_LEN(p));
644
645 /* Instruct netmap to push the data on the TX ring on the destination port */
646 (void)ioctl(ntv->ifdst->nmd->fd, NIOCTXSYNC, 0);
647 if (ntv->flags & NETMAP_FLAG_EXCL_RING_ACCESS) {
648 SCMutexUnlock(&ntv->ifdst->netmap_dev_lock);
649 }
650 return TM_ECODE_OK;
651}
652
653/**
654 * \brief Packet release routine.
655 * \param p Packet.
656 */
657static void NetmapReleasePacket(Packet *p)
658{
659 NetmapThreadVars *ntv = (NetmapThreadVars *)p->netmap_v.ntv;
660
661 if ((ntv->copy_mode != NETMAP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) {
662 NetmapWritePacket(ntv, p);
663 }
664
666}
667
668static void NetmapProcessPacket(NetmapThreadVars *ntv, const struct nm_pkthdr *ph)
669{
670 if (ntv->bpf_prog.bf_len) {
671 struct pcap_pkthdr pkthdr = { {0, 0}, ph->len, ph->len };
672 if (pcap_offline_filter(&ntv->bpf_prog, &pkthdr, ph->buf) == 0) {
673 return;
674 }
675 }
676
678 if (unlikely(p == NULL)) {
679 return;
680 }
681
683 p->livedev = ntv->livedev;
685 p->ts = SCTIME_FROM_TIMEVAL(&ph->ts);
686 ntv->pkts++;
687 ntv->bytes += ph->len;
688
689 if (ntv->flags & NETMAP_FLAG_ZERO_COPY) {
690 if (PacketSetData(p, (uint8_t *)ph->buf, ph->len) == -1) {
691 TmqhOutputPacketpool(ntv->tv, p);
692 return;
693 }
694 } else {
695 if (PacketCopyData(p, (uint8_t *)ph->buf, ph->len) == -1) {
696 TmqhOutputPacketpool(ntv->tv, p);
697 return;
698 }
699 }
700
701 p->ReleasePacket = NetmapReleasePacket;
702 p->netmap_v.ntv = ntv;
703
704 SCLogDebug("pktlen: %" PRIu32 " (pkt %p, pkt data %p)",
705 GET_PKT_LEN(p), p, GET_PKT_DATA(p));
706
707 (void)TmThreadsSlotProcessPkt(ntv->tv, ntv->slot, p);
708}
709
710/**
711 * \brief Copy netmap rings data into Packet structures.
712 * \param *d nmport_d (or nm_desc) netmap if structure.
713 * \param cnt int count of packets to read (-1 = all).
714 * \param *ntv NetmapThreadVars.
715 */
716static TmEcode NetmapReadPackets(struct nmport_d *d, int cnt, NetmapThreadVars *ntv)
717{
718 struct nm_pkthdr hdr;
719 int last_ring = d->last_rx_ring - d->first_rx_ring + 1;
720 int cur_ring, got = 0, cur_rx_ring = d->cur_rx_ring;
721
722 memset(&hdr, 0, sizeof(hdr));
723 hdr.flags = NM_MORE_PKTS;
724
725 if (cnt == 0)
726 cnt = -1;
727
728 for (cur_ring = 0; cur_ring < last_ring && cnt != got; cur_ring++, cur_rx_ring++) {
729 struct netmap_ring *ring;
730
731 if (cur_rx_ring > d->last_rx_ring)
732 cur_rx_ring = d->first_rx_ring;
733
734 ring = NETMAP_RXRING(d->nifp, cur_rx_ring);
735
736 /* cycle through the non-empty ring slots to fetch their data */
737 for (; !nm_ring_empty(ring) && cnt != got; got++) {
738 u_int idx, i;
739 u_char *oldbuf;
740 struct netmap_slot *slot;
741
742 if (hdr.buf) { /* from previous round */
743 NetmapProcessPacket(ntv, &hdr);
744 }
745
746 i = ring->cur;
747 slot = &ring->slot[i];
748 idx = slot->buf_idx;
749 d->cur_rx_ring = cur_rx_ring;
750 hdr.slot = slot;
751 oldbuf = hdr.buf = (u_char *)NETMAP_BUF(ring, idx);
752 hdr.len = hdr.caplen = slot->len;
753
754 /* loop through the ring slots to get packet data */
755 while (slot->flags & NS_MOREFRAG) {
756 /* packet can be fragmented across multiple slots, */
757 /* so loop until we find the slot with the flag */
758 /* cleared, signalling the end of the packet data. */
759 u_char *nbuf;
760 u_int oldlen = slot->len;
761 i = nm_ring_next(ring, i);
762 slot = &ring->slot[i];
763 hdr.len += slot->len;
764 nbuf = (u_char *)NETMAP_BUF(ring, slot->buf_idx);
765
766 if (oldbuf != NULL && nbuf - oldbuf == ring->nr_buf_size &&
767 oldlen == ring->nr_buf_size) {
768 hdr.caplen += slot->len;
769 oldbuf = nbuf;
770 } else {
771 oldbuf = NULL;
772 }
773 }
774
775 hdr.ts = ring->ts;
776 ring->head = ring->cur = nm_ring_next(ring, i);
777 }
778 }
779
780 if (hdr.buf) { /* from previous round */
781 hdr.flags = 0;
782 NetmapProcessPacket(ntv, &hdr);
783 }
784 return got;
785}
786
787/**
788 * \brief Main netmap reading loop function
789 */
790static TmEcode ReceiveNetmapLoop(ThreadVars *tv, void *data, void *slot)
791{
792 SCEnter();
793
794 TmSlot *s = (TmSlot *)slot;
795 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
796 struct pollfd fds;
797
798 ntv->slot = s->slot_next;
799 fds.fd = ntv->ifsrc->nmd->fd;
800 fds.events = POLLIN;
801
802 SCLogDebug("thread %s polling on %d", tv->name, fds.fd);
803
804 // Indicate that the thread is actually running its application level code (i.e., it can poll
805 // packets)
807
808 for(;;) {
809 if (unlikely(suricata_ctl_flags != 0)) {
810 break;
811 }
812
813 /* make sure we have at least one packet in the packet pool,
814 * to prevent us from alloc'ing packets at line rate */
816
817 int r = poll(&fds, 1, POLL_TIMEOUT);
818 if (r < 0) {
819 /* error */
820 if (errno != EINTR)
821 SCLogError("%s: error polling netmap: %s", ntv->ifsrc->ifname, strerror(errno));
822 continue;
823
824 } else if (r == 0) {
825 /* no events, timeout */
826 /* sync counters */
827 NetmapDumpCounters(ntv);
829
830 /* poll timed out, lets handle the timeout */
831 TmThreadsCaptureHandleTimeout(tv, NULL);
832 continue;
833 }
834
835 if (unlikely(fds.revents & POLL_EVENTS)) {
836 if (fds.revents & POLLERR) {
837 SCLogError("%s: error reading netmap data via polling: %s", ntv->ifsrc->ifname,
838 strerror(errno));
839 } else if (fds.revents & POLLNVAL) {
840 SCLogError("%s: invalid polling request", ntv->ifsrc->ifname);
841 }
842 continue;
843 }
844
845 if (likely(fds.revents & POLLIN)) {
846 /* have data on RX ring, so copy to Packet for processing */
847 NetmapReadPackets(ntv->ifsrc->nmd, -1, ntv);
848 }
849
850 NetmapDumpCounters(ntv);
852 }
853
854 NetmapDumpCounters(ntv);
857}
858
859/**
860 * \brief This function prints stats to the screen at exit.
861 * \param tv pointer to ThreadVars
862 * \param data pointer that gets cast into NetmapThreadVars for ntv
863 */
864static void ReceiveNetmapThreadExitStats(ThreadVars *tv, void *data)
865{
866 SCEnter();
867 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
868
869 NetmapDumpCounters(ntv);
870 SCLogPerf("%s: (%s) packets %" PRIu64 ", dropped %" PRIu64 ", bytes %" PRIu64 "",
871 ntv->ifsrc->ifname, tv->name,
872 StatsGetLocalCounterValue(tv, ntv->capture_kernel_packets),
873 StatsGetLocalCounterValue(tv, ntv->capture_kernel_drops), ntv->bytes);
874}
875
876/**
877 * \brief
878 * \param tv
879 * \param data Pointer to NetmapThreadVars.
880 */
881static TmEcode ReceiveNetmapThreadDeinit(ThreadVars *tv, void *data)
882{
883 SCEnter();
884
885 NetmapThreadVars *ntv = (NetmapThreadVars *)data;
886
887 if (ntv->ifsrc) {
888 NetmapClose(ntv->ifsrc);
889 ntv->ifsrc = NULL;
890 }
891 if (ntv->ifdst) {
892 NetmapClose(ntv->ifdst);
893 ntv->ifdst = NULL;
894 }
895 if (ntv->bpf_prog.bf_insns) {
896 SCBPFFree(&ntv->bpf_prog);
897 }
898
899 SCFree(ntv);
900
902}
903
904/**
905 * \brief Prepare netmap decode thread.
906 * \param tv Thread local variables.
907 * \param initdata Thread config.
908 * \param data Pointer to DecodeThreadVars placed here.
909 */
910static TmEcode DecodeNetmapThreadInit(ThreadVars *tv, const void *initdata, void **data)
911{
912 SCEnter();
913
915 if (dtv == NULL)
917
919
920 *data = (void *)dtv;
921
923}
924
925/**
926 * \brief This function passes off to link type decoders.
927 *
928 * \param t pointer to ThreadVars
929 * \param p pointer to the current packet
930 * \param data pointer that gets cast into NetmapThreadVars for ntv
931 */
932static TmEcode DecodeNetmap(ThreadVars *tv, Packet *p, void *data)
933{
934 SCEnter();
935
937
939
940 /* update counters */
942
944
946
948}
949
950/**
951 * \brief
952 * \param tv
953 * \param data Pointer to DecodeThreadVars.
954 */
955static TmEcode DecodeNetmapThreadDeinit(ThreadVars *tv, void *data)
956{
957 SCEnter();
958
959 if (data != NULL)
961
963}
964
965/**
966 * \brief Registration Function for ReceiveNetmap.
967 */
969{
970 tmm_modules[TMM_RECEIVENETMAP].name = "ReceiveNetmap";
971 tmm_modules[TMM_RECEIVENETMAP].ThreadInit = ReceiveNetmapThreadInit;
972 tmm_modules[TMM_RECEIVENETMAP].PktAcqLoop = ReceiveNetmapLoop;
973 tmm_modules[TMM_RECEIVENETMAP].ThreadExitPrintStats = ReceiveNetmapThreadExitStats;
974 tmm_modules[TMM_RECEIVENETMAP].ThreadDeinit = ReceiveNetmapThreadDeinit;
977}
978
979/**
980 * \brief Registration Function for DecodeNetmap.
981 */
983{
984 tmm_modules[TMM_DECODENETMAP].name = "DecodeNetmap";
985 tmm_modules[TMM_DECODENETMAP].ThreadInit = DecodeNetmapThreadInit;
986 tmm_modules[TMM_DECODENETMAP].Func = DecodeNetmap;
987 tmm_modules[TMM_DECODENETMAP].ThreadDeinit = DecodeNetmapThreadDeinit;
990}
991
992#endif /* HAVE_NETMAP */
993
994/**
995* @}
996*/
#define ACTION_DROP
struct HtpBodyChunk_ * next
uint16_t StatsRegisterCounter(const char *name, struct ThreadVars_ *tv)
Registers a normal, unqualified counter.
Definition counters.c:952
void StatsSyncCountersIfSignalled(ThreadVars *tv)
Definition counters.c:450
uint64_t StatsGetLocalCounterValue(ThreadVars *tv, uint16_t id)
Get the value of the local copy of the counter that hold this id.
Definition counters.c:1255
void StatsAddUI64(ThreadVars *tv, uint16_t id, uint64_t x)
Adds a value of type uint64_t to the local counter.
Definition counters.c:146
int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
uint8_t flags
Definition decode-gre.h:0
ChecksumValidationMode
Definition decode.h:42
#define PKT_SET_SRC(p, src_val)
Definition decode.h:1325
#define GET_PKT_DATA(p)
Definition decode.h:209
#define GET_PKT_LEN(p)
Definition decode.h:208
#define PKT_IS_PSEUDOPKT(p)
return 1 if the packet is a pseudo packet
Definition decode.h:1321
@ PKT_SRC_WIRE
Definition decode.h:52
DecodeThreadVars * dtv
ThreadVars * tv
#define POLL_TIMEOUT
void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
Definition decode.c:628
void PacketDecodeFinalize(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p)
Finalize decoding of a packet.
Definition decode.c:232
DecodeThreadVars * DecodeThreadVarsAlloc(ThreadVars *tv)
Alloc and setup DecodeThreadVars.
Definition decode.c:804
void DecodeThreadVarsFree(ThreadVars *tv, DecodeThreadVars *dtv)
Definition decode.c:822
int PacketCopyData(Packet *p, const uint8_t *pktdata, uint32_t pktlen)
Copy data to Packet payload and set packet length.
Definition decode.c:377
void DecodeUpdatePacketCounters(ThreadVars *tv, const DecodeThreadVars *dtv, const Packet *p)
Definition decode.c:770
uint32_t default_packet_size
Definition decode.c:77
int PacketSetData(Packet *p, const uint8_t *pktdata, uint32_t pktlen)
Set data for Packet and set length when zero copy is used.
Definition decode.c:842
void PacketFreeOrRelease(Packet *p)
Return a packet to where it was allocated.
Definition decode.c:276
void TmModuleDecodeNetmapRegister(void)
Registration Function for DecodeNetmap.
void TmModuleReceiveNetmapRegister(void)
#define PCAP_NETMASK_UNKNOWN
Definition log-pcap.c:91
bool PacketCheckAction(const Packet *p, const uint8_t a)
Definition packet.c:49
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:252
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:329
#define TAILQ_HEAD(name, type)
Definition queue.h:230
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:294
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:236
#define TAILQ_ENTRY(type)
Definition queue.h:239
char * RunmodeGetActive(void)
Definition runmodes.c:199
@ NETMAP_COPY_MODE_NONE
@ NETMAP_COPY_MODE_IPS
int NetmapGetRSSCount(const char *ifname)
Structure to hold thread specific data for all decode modules.
Definition decode.h:963
char iface_name[NETMAP_IFACE_NAME_LENGTH]
NetmapIfaceSettings out
void(* DerefFunc)(void *)
NetmapIfaceSettings in
ChecksumValidationMode checksum_mode
char iface[NETMAP_IFACE_NAME_LENGTH]
const char * bpf_filter
SCTime_t ts
Definition decode.h:555
int datalink
Definition decode.h:639
struct LiveDevice_ * livedev
Definition decode.h:618
void(* ReleasePacket)(struct Packet_ *)
Definition decode.h:591
Per thread variable structure.
Definition threadvars.h:58
char name[16]
Definition threadvars.h:65
const char * name
Definition tm-modules.h:48
TmEcode(* ThreadDeinit)(ThreadVars *, void *)
Definition tm-modules.h:53
void(* ThreadExitPrintStats)(ThreadVars *, void *)
Definition tm-modules.h:52
uint8_t cap_flags
Definition tm-modules.h:77
TmEcode(* Func)(ThreadVars *, Packet *, void *)
Definition tm-modules.h:56
TmEcode(* PktAcqLoop)(ThreadVars *, void *, void *)
Definition tm-modules.h:58
uint8_t flags
Definition tm-modules.h:80
TmEcode(* ThreadInit)(ThreadVars *, const void *, void **)
Definition tm-modules.h:51
struct TmSlot_ * slot_next
Definition tm-threads.h:62
#define BUG_ON(x)
size_t strlcpy(char *dst, const char *src, size_t siz)
volatile uint8_t suricata_ctl_flags
Definition suricata.c:172
#define SCMutexDestroy
#define SCMUTEX_INITIALIZER
#define SCMutex
#define SCMutexUnlock(mut)
#define SCMutexInit(mut, mutattrs)
#define SCMutexLock(mut)
#define THV_RUNNING
Definition threadvars.h:55
TmModule tmm_modules[TMM_SIZE]
Definition tm-modules.c:29
#define TM_FLAG_RECEIVE_TM
Definition tm-modules.h:32
#define TM_FLAG_DECODE_TM
Definition tm-modules.h:33
@ TMM_DECODENETMAP
@ TMM_RECEIVENETMAP
@ TM_ECODE_FAILED
@ TM_ECODE_OK
void TmThreadsSetFlag(ThreadVars *tv, uint32_t flag)
Set a thread flag.
Definition tm-threads.c:101
void PacketPoolWait(void)
Packet * PacketPoolGetPacket(void)
Get a new packet from the packet pool.
void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
uint32_t cnt
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
#define SC_ATOMIC_DECLARE(type, name)
wrapper for declaring atomic variables.
void SCBPFFree(struct bpf_program *program)
Definition util-bpf.c:56
int SCBPFCompile(int snaplen_arg, int linktype_arg, struct bpf_program *program, const char *buf, int optimize, uint32_t mask, char *errbuf, size_t errbuf_len)
Definition util-bpf.c:62
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define SCLogPerf(...)
Definition util-debug.h:234
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition util-debug.h:243
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCLogConfig(...)
Definition util-debug.h:229
LiveDevice * LiveGetDevice(const char *name)
Get a pointer to the device at idx.
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define likely(expr)
#define unlikely(expr)
#define SC_CAP_NET_RAW
Definition util-privs.h:32
#define SCTIME_FROM_TIMEVAL(tv)
Definition util-time.h:79
#define DEBUG_VALIDATE_BUG_ON(exp)