suricata
runmode-netmap.c
Go to the documentation of this file.
1/* Copyright (C) 2014-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* \ingroup netmap
20*
21* @{
22*/
23
24/**
25 * \file
26 *
27 * \author Aleksey Katargin <gureedo@gmail.com>
28 * \author Bill Meeks <billmeeks8@gmail.com>
29 *
30 * Netmap runmode
31 *
32 */
33
34#include "suricata-common.h"
35#include "decode.h"
36#include "runmodes.h"
37#include "runmode-netmap.h"
38#include "util-runmodes.h"
39#include "util-ioctl.h"
40#include "util-byte.h"
41#include "util-time.h"
42
43#ifdef HAVE_NETMAP
44#define NETMAP_WITH_LIBS
45#include <net/netmap_user.h>
46#endif /* HAVE_NETMAP */
47
48#include "source-netmap.h"
49#include "util-conf.h"
50#include "suricata.h"
51#include "util-bpf.h"
52
53extern uint32_t max_pending_packets;
54
56{
57 return "workers";
58}
59
60static int NetmapRunModeIsIPS(void)
61{
62 int nlive = LiveGetDeviceCount();
63 int ldev;
64 SCConfNode *if_root;
65 SCConfNode *if_default = NULL;
66 SCConfNode *netmap_node;
67 int has_ips = 0;
68 int has_ids = 0;
69
70 /* Find initial node */
71 netmap_node = SCConfGetNode("netmap");
72 if (netmap_node == NULL) {
73 return 0;
74 }
75
76 if_default = SCConfNodeLookupKeyValue(netmap_node, "interface", "default");
77
78 for (ldev = 0; ldev < nlive; ldev++) {
79 const char *live_dev = LiveGetDeviceName(ldev);
80 if (live_dev == NULL) {
81 SCLogError("Problem with config file");
82 return -1;
83 }
84 if_root = SCConfNodeLookupKeyValue(netmap_node, "interface", live_dev);
85
86 if (if_root == NULL) {
87 if (if_default == NULL) {
88 SCLogError("Problem with config file");
89 return -1;
90 }
91 if_root = if_default;
92 }
93
94 const char *copymodestr = NULL;
95 const char *copyifacestr = NULL;
96 if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1 &&
97 SCConfGetChildValue(if_root, "copy-iface", &copyifacestr) == 1) {
98 if (strcmp(copymodestr, "ips") == 0) {
99 has_ips = 1;
100 } else {
101 has_ids = 1;
102 }
103 } else {
104 has_ids = 1;
105 }
106 }
107
108 if (has_ids && has_ips) {
109 SCLogError("using both IPS and TAP/IDS mode is not allowed due to undefined behavior. See "
110 "ticket #5588.");
111 return -1;
112 }
113
114 return has_ips;
115}
116
117static int NetmapRunModeEnableIPS(void)
118{
119 int r = NetmapRunModeIsIPS();
120 if (r == 1) {
121 SCLogInfo("Netmap: Setting IPS mode");
123 }
124 return r;
125}
126
128{
129 RunModeRegisterNewRunMode(RUNMODE_NETMAP, "single", "Single threaded netmap mode",
130 RunModeIdsNetmapSingle, NetmapRunModeEnableIPS);
132 "Workers netmap mode, each thread does all"
133 " tasks from acquisition to logging",
134 RunModeIdsNetmapWorkers, NetmapRunModeEnableIPS);
136 "Multi-threaded netmap mode. Packets from "
137 "each flow are assigned to a single detect "
138 "thread.",
139 RunModeIdsNetmapAutoFp, NetmapRunModeEnableIPS);
140}
141
142#ifdef HAVE_NETMAP
143
144static void NetmapDerefConfig(void *conf)
145{
147 /* config is used only once but cost of this low. */
148 if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) {
149 SCFree(pfp);
150 }
151}
152
153static int ParseNetmapSettings(
154 NetmapIfaceSettings *ns, const char *iface, SCConfNode *if_root, SCConfNode *if_default)
155{
156 ns->threads = 0;
157 ns->promisc = true;
160 strlcpy(ns->iface, iface, sizeof(ns->iface));
161
162 if (ns->iface[0]) {
163 size_t len = strlen(ns->iface);
164 if (ns->iface[len-1] == '+') {
165 SCLogWarning("%s: interface uses obsolete '+' notation. Using '^' instead", ns->iface);
166 ns->iface[len-1] = '^';
167 ns->sw_ring = true;
168 } else if (ns->iface[len-1] == '^') {
169 ns->sw_ring = true;
170 }
171 }
172
173 /* we will need the base interface name for later */
174 char base_name[IFNAMSIZ];
175 strlcpy(base_name, ns->iface, sizeof(base_name));
176 if (strlen(base_name) > 0 &&
177 (base_name[strlen(base_name) - 1] == '^' || base_name[strlen(base_name) - 1] == '*')) {
178 base_name[strlen(base_name) - 1] = '\0';
179 }
180
181 /* prefixed with netmap or vale means it's not a real interface
182 * and we don't check offloading. */
183 if (strncmp(ns->iface, "netmap:", 7) != 0 &&
184 strncmp(ns->iface, "vale", 4) != 0) {
185 ns->real = true;
186 }
187
188 if (if_root == NULL && if_default == NULL) {
189 SCLogInfo("%s: unable to find netmap config for interface \"%s\" or \"default\", using "
190 "default values",
191 iface, iface);
192 goto finalize;
193
194 /* If there is no setting for current interface use default one as main iface */
195 } else if (if_root == NULL) {
196 if_root = if_default;
197 if_default = NULL;
198 }
199
200 const char *threadsstr = NULL;
201 if (SCConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
202 ns->threads = 0;
203 ns->threads_auto = true;
204 } else {
205 if (strcmp(threadsstr, "auto") == 0) {
206 ns->threads = 0;
207 ns->threads_auto = true;
208 } else {
209 if (StringParseUint16(&ns->threads, 10, 0, threadsstr) < 0) {
210 SCLogWarning("%s: invalid config value for threads: %s, resetting to 0", iface,
211 threadsstr);
212 ns->threads = 0;
213 }
214 }
215 }
216
217 ConfSetBPFFilter(if_root, if_default, iface, &ns->bpf_filter);
218
219 int boolval = 0;
220 (void)SCConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", &boolval);
221 if (boolval) {
222 SCLogInfo("%s: disabling promiscuous mode", ns->iface);
223 ns->promisc = false;
224 }
225
226 const char *tmpctype;
227 if (SCConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
228 if (strcmp(tmpctype, "auto") == 0) {
230 } else if (SCConfValIsTrue(tmpctype)) {
232 } else if (SCConfValIsFalse(tmpctype)) {
234 } else {
235 SCLogWarning("%s: invalid value for checksum-checks '%s'", iface, tmpctype);
236 }
237 }
238
239 const char *copymodestr;
240 if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
241 if (strcmp(copymodestr, "ips") == 0) {
243 } else if (strcmp(copymodestr, "tap") == 0) {
245 } else {
246 SCLogWarning("%s: invalid copy-mode %s (valid are tap, ips)", iface, copymodestr);
247 }
248 }
249
250finalize:
251
252 ns->ips = (ns->copy_mode != NETMAP_COPY_MODE_NONE);
253
254 if (ns->threads_auto) {
255 /* As NetmapGetRSSCount used to be broken on Linux,
256 * fall back to GetIfaceRSSQueuesNum if needed. */
257 ns->threads = NetmapGetRSSCount(base_name);
258 if (ns->threads == 0) {
259 /* need to use base_name of interface here */
260 ns->threads = GetIfaceRSSQueuesNum(base_name);
261 }
262 }
263 if (ns->threads <= 0) {
264 ns->threads = 1;
265 }
266
267 return 0;
268}
269
270/**
271 * \brief extract information from config file
272 *
273 * The returned structure will be freed by the thread init function.
274 * This is thus necessary to copy the structure before giving it
275 * to thread or to reparse the file for each thread (and thus have
276 * new structure.
277 *
278 * \return a NetmapIfaceConfig corresponding to the interface name
279 */
280static void *ParseNetmapConfig(const char *iface_name)
281{
282 SCConfNode *if_root = NULL;
283 SCConfNode *if_default = NULL;
284 const char *out_iface = NULL;
285
286 if (iface_name == NULL) {
287 return NULL;
288 }
289
290 NetmapIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
291 if (unlikely(aconf == NULL)) {
292 return NULL;
293 }
294
295 aconf->DerefFunc = NetmapDerefConfig;
296 strlcpy(aconf->iface_name, iface_name, sizeof(aconf->iface_name));
297 SC_ATOMIC_INIT(aconf->ref);
298 (void) SC_ATOMIC_ADD(aconf->ref, 1);
299
300 /* Find initial node */
301 SCConfNode *netmap_node = SCConfGetNode("netmap");
302 if (netmap_node == NULL) {
303 SCLogInfo("%s: unable to find netmap config using default value", iface_name);
304 } else {
305 if_root = ConfFindDeviceConfig(netmap_node, aconf->iface_name);
306 if_default = ConfFindDeviceConfig(netmap_node, "default");
307 }
308
309 /* parse settings for capture iface */
310 ParseNetmapSettings(&aconf->in, aconf->iface_name, if_root, if_default);
311
312 /* if we have a copy iface, parse that as well */
313 if (netmap_node != NULL &&
314 SCConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
315 if (strlen(out_iface) > 0) {
316 if_root = ConfFindDeviceConfig(netmap_node, out_iface);
317 ParseNetmapSettings(&aconf->out, out_iface, if_root, if_default);
318 }
319 }
320
321 int ring_count = 0;
322 if (aconf->in.real)
323 ring_count = NetmapGetRSSCount(aconf->iface_name);
324 if (strlen(aconf->iface_name) > 0 &&
325 (aconf->iface_name[strlen(aconf->iface_name) - 1] == '^' ||
326 aconf->iface_name[strlen(aconf->iface_name) - 1] == '*')) {
327 SCLogDebug("%s -- using %d netmap host ring pair%s", aconf->iface_name, ring_count,
328 ring_count == 1 ? "" : "s");
329 } else {
330 SCLogDebug("%s -- using %d netmap ring pair%s", aconf->iface_name, ring_count,
331 ring_count == 1 ? "" : "s");
332 }
333
334 for (int i = 0; i < ring_count; i++) {
335 char live_buf[32] = { 0 };
336 snprintf(live_buf, sizeof(live_buf), "netmap%d", i);
337 LiveRegisterDevice(live_buf);
338 }
339
340 /* we need the base interface name with any trailing software
341 * ring marker stripped for HW offloading checks */
342 char base_name[sizeof(aconf->in.iface)];
343 strlcpy(base_name, aconf->in.iface, sizeof(base_name));
344 /* for a sw_ring enabled device name, strip the trailing char */
345 if (aconf->in.sw_ring) {
346 base_name[strlen(base_name) - 1] = '\0';
347 }
348
349 /* netmap needs all offloading to be disabled */
350 if (aconf->in.real) {
351 if (LiveGetOffload() == 0) {
352 (void)GetIfaceOffloading(base_name, 1, 1);
353 } else {
354 DisableIfaceOffloading(LiveGetDevice(base_name), 1, 1);
355 }
356 }
357
358 SC_ATOMIC_RESET(aconf->ref);
359 (void) SC_ATOMIC_ADD(aconf->ref, aconf->in.threads);
360 SCLogPerf("%s: using %d threads", aconf->iface_name, aconf->in.threads);
361
363 return aconf;
364}
365
366static int NetmapConfigGeThreadsCount(void *conf)
367{
368 NetmapIfaceConfig *aconf = (NetmapIfaceConfig *)conf;
369 return aconf->in.threads;
370}
371
372typedef enum { NETMAP_AUTOFP, NETMAP_WORKERS, NETMAP_SINGLE } NetmapRunMode_t;
373
374static int NetmapRunModeInit(NetmapRunMode_t runmode)
375{
376 SCEnter();
377
379
380 const char *live_dev = NULL;
381 (void)SCConfGet("netmap.live-interface", &live_dev);
382
383 const char *runmode_str = "unknown";
384 int ret;
385 switch (runmode) {
386 case NETMAP_AUTOFP:
387 runmode_str = "autofp";
388 ret = RunModeSetLiveCaptureAutoFp(ParseNetmapConfig, NetmapConfigGeThreadsCount,
389 "ReceiveNetmap", "DecodeNetmap", thread_name_autofp, live_dev);
390 break;
391 case NETMAP_WORKERS:
392 runmode_str = "workers";
393 ret = RunModeSetLiveCaptureWorkers(ParseNetmapConfig, NetmapConfigGeThreadsCount,
394 "ReceiveNetmap", "DecodeNetmap", thread_name_workers, live_dev);
395 break;
396 case NETMAP_SINGLE:
397 runmode_str = "single";
398 ret = RunModeSetLiveCaptureSingle(ParseNetmapConfig, NetmapConfigGeThreadsCount,
399 "ReceiveNetmap", "DecodeNetmap", thread_name_single, live_dev);
400 break;
401 }
402 if (ret != 0) {
403 FatalError("Unable to start runmode %s", runmode_str);
404 }
405
406 SCLogDebug("%s initialized", runmode_str);
407
408 SCReturnInt(0);
409}
410
412{
413 return NetmapRunModeInit(NETMAP_AUTOFP);
414}
415
416/**
417* \brief Single thread version of the netmap processing.
418*/
420{
421 return NetmapRunModeInit(NETMAP_SINGLE);
422}
423
424/**
425 * \brief Workers version of the netmap processing.
426 *
427 * Start N threads with each thread doing all the work.
428 *
429 */
431{
432 return NetmapRunModeInit(NETMAP_WORKERS);
433}
434#else
436{
437 SCEnter();
438 FatalError("Netmap not configured");
439 SCReturnInt(0);
440}
441
442/**
443 * \brief Single thread version of the netmap processing.
444 */
446{
447 SCEnter();
448 FatalError("Netmap not configured");
449 SCReturnInt(0);
450}
451
452/**
453* \brief Workers version of the netmap processing.
454*
455* Start N threads with each thread doing all the work.
456*
457*/
459{
460 SCEnter();
461 FatalError("Netmap not configured");
462 SCReturnInt(0);
463}
464#endif // #ifdef HAVE_NETMAP
465
466/**
467* @}
468*/
uint8_t len
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
int SCConfGetChildValueWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, const char **vptr)
Definition conf.c:393
SCConfNode * SCConfNodeLookupKeyValue(const SCConfNode *base, const char *key, const char *value)
Lookup for a key value under a specific node.
Definition conf.c:841
int SCConfValIsFalse(const char *val)
Check if a value is false.
Definition conf.c:576
int SCConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition conf.c:350
int SCConfGetChildValueBoolWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, int *val)
Definition conf.c:528
int SCConfGetChildValue(const SCConfNode *base, const char *name, const char **vptr)
Definition conf.c:363
@ CHECKSUM_VALIDATION_AUTO
Definition decode.h:45
@ CHECKSUM_VALIDATION_ENABLE
Definition decode.h:44
@ CHECKSUM_VALIDATION_DISABLE
Definition decode.h:43
int RunModeIdsNetmapWorkers(void)
Workers version of the netmap processing.
uint32_t max_pending_packets
Definition suricata.c:183
void RunModeIdsNetmapRegister(void)
int RunModeIdsNetmapAutoFp(void)
const char * RunModeNetmapGetDefaultMode(void)
int RunModeIdsNetmapSingle(void)
Single thread version of the netmap processing.
const char * thread_name_single
Definition runmodes.c:67
void RunModeRegisterNewRunMode(enum SCRunModes runmode, const char *name, const char *description, int(*RunModeFunc)(void), int(*RunModeIsIPSEnabled)(void))
Registers a new runmode.
Definition runmodes.c:486
const char * thread_name_autofp
Definition runmodes.c:66
const char * thread_name_workers
Definition runmodes.c:68
@ RUNMODE_NETMAP
Definition runmodes.h:38
@ NETMAP_COPY_MODE_NONE
@ NETMAP_COPY_MODE_TAP
@ NETMAP_COPY_MODE_IPS
int NetmapGetRSSCount(const char *ifname)
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
size_t strlcpy(char *dst, const char *src, size_t siz)
void EngineModeSetIPS(void)
Definition suricata.c:259
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
#define SC_ATOMIC_RESET(name)
wrapper for reinitializing an atomic variable.
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
void ConfSetBPFFilter(SCConfNode *if_root, SCConfNode *if_default, const char *iface, const char **bpf_filter)
Definition util-bpf.c:30
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition util-byte.c:337
SCConfNode * ConfFindDeviceConfig(SCConfNode *node, const char *iface)
Find the configuration node for a specific device.
Definition util-conf.c:121
#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 SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#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
void LiveDeviceHasNoStats(void)
LiveDevice * LiveGetDevice(const char *name)
Get a pointer to the device at idx.
int LiveGetDeviceCount(void)
Get the number of registered devices.
int LiveGetOffload(void)
Definition util-device.c:87
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
const char * LiveGetDeviceName(int number)
Get a pointer to the device name at idx.
int GetIfaceRSSQueuesNum(const char *dev)
Definition util-ioctl.c:713
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition util-ioctl.c:670
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition util-ioctl.c:683
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
int RunModeSetLiveCaptureAutoFp(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
void TimeModeSetLive(void)
Definition util-time.c:99