suricata
runmode-af-packet.c
Go to the documentation of this file.
1/* Copyright (C) 2011-2024 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 afppacket
20 *
21 * @{
22 */
23
24/**
25 * \file
26 *
27 * \author Eric Leblond <eric@regit.org>
28 *
29 * AF_PACKET socket runmode
30 *
31 */
32
33#include "suricata-common.h"
34#include "suricata.h"
35#include "tm-threads.h"
36#include "conf.h"
37#include "runmodes.h"
38#include "runmode-af-packet.h"
39#include "output.h"
40#include "log-httplog.h"
41#include "detect-engine-mpm.h"
42
43#include "alert-fastlog.h"
44#include "alert-debuglog.h"
45
46#include "flow-bypass.h"
47
48#include "util-conf.h"
49#include "util-debug.h"
50#include "util-time.h"
51#include "util-cpu.h"
52#include "util-affinity.h"
53#include "util-device-private.h"
54#include "util-runmodes.h"
55#include "util-ioctl.h"
56#include "util-ebpf.h"
57#include "util-byte.h"
58
59#include "source-af-packet.h"
60#include "util-bpf.h"
61
62extern uint32_t max_pending_packets;
63
64const char *RunModeAFPGetDefaultMode(void)
65{
66 return "workers";
67}
68
69static bool AFPRunModeIsIPS(void)
70{
71 int nlive = LiveGetDeviceCount();
72 bool has_ips = false;
73 bool has_ids = false;
74
75 SCConfNode *af_packet_node = SCConfGetNode("af-packet");
76 if (af_packet_node == NULL) {
77 SCLogConfig("no 'af-packet' section in the yaml, default to IDS");
78 return false;
79 }
80
81 SCConfNode *if_default = SCConfNodeLookupKeyValue(af_packet_node, "interface", "default");
82
83 for (int ldev = 0; ldev < nlive; ldev++) {
84 const char *live_dev = LiveGetDeviceName(ldev);
85 if (live_dev == NULL) {
86 SCLogConfig("invalid livedev at index %d, default to IDS", ldev);
87 return false;
88 }
89 SCConfNode *if_root = ConfFindDeviceConfig(af_packet_node, live_dev);
90 if (if_root == NULL) {
91 if (if_default == NULL) {
93 "no 'af-packet' section for '%s' or 'default' in the yaml, default to IDS",
94 live_dev);
95 return false;
96 }
97 if_root = if_default;
98 }
99
100 const char *copymodestr = NULL;
101 const char *copyifacestr = NULL;
102 if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1 &&
103 SCConfGetChildValue(if_root, "copy-iface", &copyifacestr) == 1) {
104 if (strcmp(copymodestr, "ips") == 0) {
105 has_ips = true;
106 } else {
107 has_ids = true;
108 }
109 } else {
110 has_ids = true;
111 }
112 }
113
114 if (has_ids && has_ips) {
115 SCLogError("using both IPS and TAP/IDS mode is not allowed due to undefined behavior. See "
116 "ticket #5588.");
117 return false;
118 }
119
120 return has_ips;
121}
122
123static int AFPRunModeEnableIPS(void)
124{
125 bool r = AFPRunModeIsIPS();
126 if (r) {
127 SCLogInfo("Setting IPS mode");
129 }
130 return r;
131}
132
134{
135 RunModeRegisterNewRunMode(RUNMODE_AFP_DEV, "single", "Single threaded af-packet mode",
136 RunModeIdsAFPSingle, AFPRunModeEnableIPS);
138 "Workers af-packet mode, each thread does all"
139 " tasks from acquisition to logging",
140 RunModeIdsAFPWorkers, AFPRunModeEnableIPS);
142 "Multi socket AF_PACKET mode. Packets from "
143 "each flow are assigned to a single detect "
144 "thread.",
145 RunModeIdsAFPAutoFp, AFPRunModeEnableIPS);
146}
147
148
149#ifdef HAVE_AF_PACKET
150
151static void AFPDerefConfig(void *conf)
152{
153 AFPIfaceConfig *pfp = (AFPIfaceConfig *)conf;
154 /* Pcap config is used only once but cost of this low. */
155 if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) {
156 SCFree(pfp);
157 }
158}
159
160/* if cluster id is not set, assign it automagically, uniq value per
161 * interface. */
162static int cluster_id_auto = 1;
163
164/**
165 * \brief extract information from config file
166 *
167 * The returned structure will be freed by the thread init function.
168 * This is thus necessary to or copy the structure before giving it
169 * to thread or to reparse the file for each thread (and thus have
170 * new structure.
171 *
172 * \return a AFPIfaceConfig corresponding to the interface name
173 */
174static void *ParseAFPConfig(const char *iface)
175{
176 const char *threadsstr = NULL;
177 SCConfNode *if_root;
178 SCConfNode *if_default = NULL;
179 SCConfNode *af_packet_node;
180 const char *tmpclusterid;
181 const char *tmpctype;
182 const char *copymodestr;
183 intmax_t value;
184 int boolval;
185 const char *out_iface = NULL;
186 int cluster_type = PACKET_FANOUT_HASH;
187 const char *ebpf_file = NULL;
188 const char *active_runmode = RunmodeGetActive();
189
190 if (iface == NULL) {
191 return NULL;
192 }
193
194 AFPIfaceConfig *aconf = SCCalloc(1, sizeof(*aconf));
195 if (unlikely(aconf == NULL)) {
196 return NULL;
197 }
198
199 strlcpy(aconf->iface, iface, sizeof(aconf->iface));
200 aconf->threads = 0;
201 SC_ATOMIC_INIT(aconf->ref);
202 (void) SC_ATOMIC_ADD(aconf->ref, 1);
203 aconf->buffer_size = 0;
204 aconf->cluster_id = 1;
205 aconf->cluster_type = cluster_type | PACKET_FANOUT_FLAG_DEFRAG;
206 aconf->promisc = 1;
208 aconf->DerefFunc = AFPDerefConfig;
209 aconf->flags = 0;
210 aconf->bpf_filter = NULL;
211 aconf->ebpf_lb_file = NULL;
212 aconf->ebpf_lb_fd = -1;
213 aconf->ebpf_filter_file = NULL;
214 aconf->ebpf_filter_fd = -1;
215 aconf->out_iface = NULL;
217 aconf->block_timeout = 10;
218 aconf->block_size = getpagesize() << AFP_BLOCK_SIZE_DEFAULT_ORDER;
219 aconf->v2_block_size = 0;
220#ifdef HAVE_PACKET_EBPF
221 aconf->ebpf_t_config.cpus_count = UtilCpuGetNumProcessorsConfigured();
222#endif
223
224 /* Find initial node */
225 af_packet_node = SCConfGetNode("af-packet");
226 if (af_packet_node == NULL) {
227 SCLogInfo("%s: unable to find af-packet config using default values", iface);
228 goto finalize;
229 }
230
231 if_root = ConfFindDeviceConfig(af_packet_node, iface);
232 if_default = ConfFindDeviceConfig(af_packet_node, "default");
233
234 if (if_root == NULL && if_default == NULL) {
235 SCLogInfo("%s: unable to find af-packet config for "
236 "interface \"%s\" or \"default\", using default values",
237 iface, iface);
238 goto finalize;
239 }
240
241 /* If there is no setting for current interface use default one as main iface */
242 if (if_root == NULL) {
243 if_root = if_default;
244 if_default = NULL;
245 }
246
247 if (active_runmode && !strcmp("single", active_runmode)) {
248 aconf->threads = 1;
249 } else if (SCConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
250 aconf->threads = 0;
251 } else {
252 if (threadsstr != NULL) {
253 if (strcmp(threadsstr, "auto") == 0) {
254 aconf->threads = 0;
255 } else {
256 if (StringParseInt32(&aconf->threads, 10, 0, (const char *)threadsstr) < 0) {
257 SCLogWarning("%s: invalid number of "
258 "threads, resetting to default",
259 iface);
260 aconf->threads = 0;
261 }
262 }
263 }
264 }
265
266 if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-iface", &out_iface) == 1) {
267 if (out_iface != NULL) {
268 if (strlen(out_iface) > 0) {
269 aconf->out_iface = out_iface;
270 if (strcmp(iface, out_iface) == 0) {
272 "Invalid config: interface (%s) and copy-iface (%s) can't be the same",
273 iface, out_iface);
274 }
275 }
276 } else {
277 SCLogWarning("copy-iface corresponding to %s interface cannot be NULL", iface);
278 }
279 }
280
281 (void)SCConfGetChildValueBoolWithDefault(if_root, if_default, "mmap-locked", &boolval);
282 if (boolval) {
283 SCLogConfig("%s: enabling locked memory for mmap", aconf->iface);
284 aconf->flags |= AFP_MMAP_LOCKED;
285 }
286
287 (void)SCConfGetChildValueBoolWithDefault(if_root, if_default, "use-emergency-flush", &boolval);
288 if (boolval) {
289 SCLogConfig("%s: using emergency ring flush", aconf->iface);
290 aconf->flags |= AFP_EMERGENCY_MODE;
291 }
292
293 if (SCConfGetChildValueWithDefault(if_root, if_default, "copy-mode", &copymodestr) == 1) {
294 if (aconf->out_iface == NULL) {
295 SCLogWarning("%s: copy mode activated but no destination"
296 " iface. Disabling feature",
297 iface);
298 } else if (strlen(copymodestr) <= 0) {
299 aconf->out_iface = NULL;
300 } else if (strcmp(copymodestr, "ips") == 0) {
301 SCLogInfo("%s: AF_PACKET IPS mode activated %s->%s", iface, iface, aconf->out_iface);
303 } else if (strcmp(copymodestr, "tap") == 0) {
304 SCLogInfo("%s: AF_PACKET TAP mode activated %s->%s", iface, iface, aconf->out_iface);
306 } else {
307 SCLogWarning("Invalid 'copy-mode' (not in tap, ips)");
308 }
309 }
310
311 if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "tpacket-v3", &boolval) == 1) {
312 if (boolval) {
313 if (strcasecmp(RunmodeGetActive(), "workers") == 0) {
314 SCLogConfig("%s: enabling tpacket v3", aconf->iface);
315 aconf->flags |= AFP_TPACKET_V3;
316 } else {
317 SCLogWarning("%s: tpacket v3 is only implemented for 'workers' runmode."
318 " Switching to tpacket v2.",
319 iface);
320 aconf->flags &= ~AFP_TPACKET_V3;
321 }
322 } else {
323 aconf->flags &= ~AFP_TPACKET_V3;
324 }
325 } else if (aconf->copy_mode == AFP_COPY_MODE_NONE) {
326 // If copy mode is none (passive IDS) and "tpacket-v3" is not
327 // present, default to TPACKET_V3.
328 SCLogConfig("%s: enabling tpacket v3", aconf->iface);
329 aconf->flags |= AFP_TPACKET_V3;
330 }
331
332 if (aconf->flags & AFP_TPACKET_V3 && aconf->copy_mode) {
333 SCLogWarning("%s: using tpacket-v3 in IPS or TAP mode will result in high latency", iface);
334 }
335
336 if (SCConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
337 aconf->cluster_id = (uint16_t)(cluster_id_auto++);
338 } else {
339 if (StringParseUint16(&aconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
340 SCLogWarning("%s: invalid cluster_id, resetting to 0", iface);
341 aconf->cluster_id = 0;
342 }
343 SCLogDebug("Going to use cluster-id %" PRIu16, aconf->cluster_id);
344 }
345
346 if (SCConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
347 SCLogConfig("%s: using default cluster-type of \"cluster_flow\"", aconf->iface);
348 tmpctype = "cluster_flow";
349 }
350
351 if (strcmp(tmpctype, "cluster_rollover") == 0) {
353 "%s: cluster_rollover deprecated; using \"cluster_flow\" instead. See ticket #6128",
354 aconf->iface);
355 tmpctype = "cluster_flow";
356 }
357
358 if (strcmp(tmpctype, "cluster_round_robin") == 0) {
359 SCLogConfig("%s: using round-robin cluster mode for AF_PACKET", aconf->iface);
361 cluster_type = PACKET_FANOUT_LB;
362 } else if (strcmp(tmpctype, "cluster_flow") == 0) {
363 /* Check for defrag. If not set by the user enable it when not
364 * inline. */
365 uint16_t defrag = 0;
366 int conf_val = 0;
367 SCLogConfig("%s: using flow cluster mode for AF_PACKET", aconf->iface);
368 if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "defrag", &conf_val)) {
369 if (conf_val) {
370 SCLogConfig("%s: using defrag kernel functionality for AF_PACKET", aconf->iface);
372 }
373 } else {
374 /* "defrag" not provided in config, default to true if not inline. */
375 if (aconf->copy_mode == 0) {
376 SCLogConfig("%s: enabling defrag for AF_PACKET cluster_flow", aconf->iface);
378 }
379 }
380 aconf->cluster_type = PACKET_FANOUT_HASH | defrag;
381 cluster_type = PACKET_FANOUT_HASH;
382 } else if (strcmp(tmpctype, "cluster_cpu") == 0) {
383 SCLogConfig("%s: using cpu cluster mode for AF_PACKET", aconf->iface);
385 cluster_type = PACKET_FANOUT_CPU;
386 } else if (strcmp(tmpctype, "cluster_qm") == 0) {
387 SCLogConfig("%s: using queue based cluster mode for AF_PACKET", aconf->iface);
389 cluster_type = PACKET_FANOUT_QM;
390 } else if (strcmp(tmpctype, "cluster_random") == 0) {
391 SCLogConfig("%s: using random based cluster mode for AF_PACKET", aconf->iface);
393 cluster_type = PACKET_FANOUT_RND;
394#ifdef HAVE_PACKET_EBPF
395 } else if (strcmp(tmpctype, "cluster_ebpf") == 0) {
396 SCLogInfo("%s: using ebpf based cluster mode for AF_PACKET", aconf->iface);
397 aconf->cluster_type = PACKET_FANOUT_EBPF;
398 cluster_type = PACKET_FANOUT_EBPF;
399#endif
400 } else {
401 SCLogWarning("invalid cluster-type %s", tmpctype);
402 }
403
404 int conf_val = 0;
405 SCConfGetChildValueBoolWithDefault(if_root, if_default, "rollover", &conf_val);
406 if (conf_val) {
407 SCLogConfig("%s: Rollover requested for AF_PACKET but ignored -- see ticket #6128.",
408 aconf->iface);
409 SCLogWarning("%s: rollover option has been deprecated and will be ignored as it can cause "
410 "severe flow "
411 "tracking issues; see ticket #6128.",
412 iface);
413 }
414
415 ConfSetBPFFilter(if_root, if_default, iface, &aconf->bpf_filter);
416
417 if (SCConfGetChildValueWithDefault(if_root, if_default, "ebpf-lb-file", &ebpf_file) != 1) {
418 aconf->ebpf_lb_file = NULL;
419 } else {
420#ifdef HAVE_PACKET_EBPF
421 SCLogConfig("%s: af-packet will use '%s' as eBPF load balancing file", iface, ebpf_file);
422 aconf->ebpf_lb_file = ebpf_file;
423 aconf->ebpf_t_config.flags |= EBPF_SOCKET_FILTER;
424#endif
425 }
426
427#ifdef HAVE_PACKET_EBPF
428 boolval = false;
429 if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "pinned-maps", &boolval) == 1) {
430 if (boolval) {
431 SCLogConfig("%s: using pinned maps", aconf->iface);
432 aconf->ebpf_t_config.flags |= EBPF_PINNED_MAPS;
433 }
434 const char *pinned_maps_name = NULL;
436 if_root, if_default, "pinned-maps-name", &pinned_maps_name) != 1) {
437 aconf->ebpf_t_config.pinned_maps_name = pinned_maps_name;
438 } else {
439 aconf->ebpf_t_config.pinned_maps_name = NULL;
440 }
441 } else {
442 aconf->ebpf_t_config.pinned_maps_name = NULL;
443 }
444#endif
445
446#ifdef HAVE_PACKET_EBPF
447 /* One shot loading of the eBPF file */
448 if (aconf->ebpf_lb_file && cluster_type == PACKET_FANOUT_EBPF) {
449 int ret = EBPFLoadFile(aconf->iface, aconf->ebpf_lb_file, "loadbalancer",
450 &aconf->ebpf_lb_fd,
451 &aconf->ebpf_t_config);
452 if (ret != 0) {
453 SCLogWarning("%s: failed to load eBPF lb file", iface);
454 }
455 }
456#else
457 if (aconf->ebpf_lb_file) {
458 SCLogError("%s: eBPF support is not built-in", iface);
459 }
460#endif
461
462 if (SCConfGetChildValueWithDefault(if_root, if_default, "ebpf-filter-file", &ebpf_file) != 1) {
463 aconf->ebpf_filter_file = NULL;
464 } else {
465#ifdef HAVE_PACKET_EBPF
466 SCLogConfig("%s: af-packet will use '%s' as eBPF filter file", iface, ebpf_file);
467 aconf->ebpf_filter_file = ebpf_file;
468 aconf->ebpf_t_config.mode = AFP_MODE_EBPF_BYPASS;
469 aconf->ebpf_t_config.flags |= EBPF_SOCKET_FILTER;
470#endif
471 SCConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &conf_val);
472 if (conf_val) {
473#ifdef HAVE_PACKET_EBPF
474 SCLogConfig("%s: using bypass kernel functionality for AF_PACKET", aconf->iface);
475 aconf->flags |= AFP_BYPASS;
476 BypassedFlowManagerRegisterUpdateFunc(EBPFUpdateFlow, NULL);
477#else
478 SCLogError("%s: bypass set but eBPF support is not built-in", iface);
479#endif
480 }
481 }
482
483 /* One shot loading of the eBPF file */
484 if (aconf->ebpf_filter_file) {
485#ifdef HAVE_PACKET_EBPF
486 int ret = EBPFLoadFile(aconf->iface, aconf->ebpf_filter_file, "filter",
487 &aconf->ebpf_filter_fd,
488 &aconf->ebpf_t_config);
489 if (ret != 0) {
490 SCLogWarning("%s: failed to load eBPF filter file", iface);
491 }
492#else
493 SCLogError("%s: eBPF support is not built-in", iface);
494#endif
495 }
496
497 if (SCConfGetChildValueWithDefault(if_root, if_default, "xdp-filter-file", &ebpf_file) != 1) {
498 aconf->xdp_filter_file = NULL;
499 } else {
500#ifdef HAVE_PACKET_XDP
501 aconf->ebpf_t_config.mode = AFP_MODE_XDP_BYPASS;
502 aconf->ebpf_t_config.flags |= EBPF_XDP_CODE;
503 aconf->xdp_filter_file = ebpf_file;
504 SCConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &conf_val);
505 if (conf_val) {
506 SCLogConfig("%s: using bypass kernel functionality for AF_PACKET", aconf->iface);
507 aconf->flags |= AFP_XDPBYPASS;
508 /* if maps are pinned we need to read them at start */
509 if (aconf->ebpf_t_config.flags & EBPF_PINNED_MAPS) {
511 struct ebpf_timeout_config *ebt = SCCalloc(1, sizeof(struct ebpf_timeout_config));
512 if (ebt == NULL) {
513 SCLogError("%s: flow bypass alloc error", iface);
514 } else {
515 memcpy(ebt, &(aconf->ebpf_t_config), sizeof(struct ebpf_timeout_config));
517 EBPFCheckBypassedFlowCreate,
518 (void *)ebt);
519 }
520 }
521 BypassedFlowManagerRegisterUpdateFunc(EBPFUpdateFlow, NULL);
522 }
523#else
524 SCLogWarning("%s: XDP filter set but XDP support is not built-in", iface);
525#endif
526#ifdef HAVE_PACKET_XDP
527 const char *xdp_mode;
528 if (SCConfGetChildValueWithDefault(if_root, if_default, "xdp-mode", &xdp_mode) != 1) {
529 aconf->xdp_mode = XDP_FLAGS_SKB_MODE;
530 } else {
531 if (!strcmp(xdp_mode, "soft")) {
532 aconf->xdp_mode = XDP_FLAGS_SKB_MODE;
533 } else if (!strcmp(xdp_mode, "driver")) {
534 aconf->xdp_mode = XDP_FLAGS_DRV_MODE;
535 } else if (!strcmp(xdp_mode, "hw")) {
536 aconf->xdp_mode = XDP_FLAGS_HW_MODE;
537 aconf->ebpf_t_config.flags |= EBPF_XDP_HW_MODE;
538 } else {
539 SCLogWarning("Invalid xdp-mode value: '%s'", xdp_mode);
540 }
541 }
542
543 boolval = true;
544 if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "use-percpu-hash", &boolval) ==
545 1) {
546 if (!boolval) {
547 SCLogConfig("%s: not using percpu hash", aconf->iface);
548 aconf->ebpf_t_config.cpus_count = 1;
549 }
550 }
551#endif
552 }
553
554 /* One shot loading of the eBPF file */
555 if (aconf->xdp_filter_file) {
556#ifdef HAVE_PACKET_XDP
557 int ret = EBPFLoadFile(aconf->iface, aconf->xdp_filter_file, "xdp",
558 &aconf->xdp_filter_fd,
559 &aconf->ebpf_t_config);
560 switch (ret) {
561 case 1:
562 SCLogInfo("%s: loaded pinned maps from sysfs", iface);
563 break;
564 case -1:
565 SCLogWarning("%s: failed to load XDP filter file", iface);
566 break;
567 case 0:
568 ret = EBPFSetupXDP(aconf->iface, aconf->xdp_filter_fd, aconf->xdp_mode);
569 if (ret != 0) {
570 SCLogWarning("%s: failed to set up XDP", iface);
571 } else {
572 /* Try to get the xdp-cpu-redirect key */
573 const char *cpuset;
575 if_root, if_default, "xdp-cpu-redirect", &cpuset) == 1) {
576 SCLogConfig("%s: Setting up CPU map XDP", iface);
577 SCConfNode *node =
578 SCConfGetChildWithDefault(if_root, if_default, "xdp-cpu-redirect");
579 if (node == NULL) {
580 SCLogError("Previously found node has disappeared");
581 } else {
582 EBPFBuildCPUSet(node, aconf->iface);
583 }
584 } else {
585 /* It will just set CPU count to 0 */
586 EBPFBuildCPUSet(NULL, aconf->iface);
587 }
588 }
589 /* we have a peer and we use bypass so we can set up XDP iface redirect */
590 if (aconf->out_iface) {
591 EBPFSetPeerIface(aconf->iface, aconf->out_iface);
592 }
593 }
594#else
595 SCLogError("%s: XDP support is not built-in", iface);
596#endif
597 }
598
599 if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "buffer-size", &value)) == 1) {
600 aconf->buffer_size = (int)value;
601 } else {
602 aconf->buffer_size = 0;
603 }
604 if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "ring-size", &value)) == 1) {
605 aconf->ring_size = (int)value;
606 }
607
608 if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "block-size", &value)) == 1) {
609 if (value % getpagesize()) {
610 SCLogWarning("%s: block-size %" PRIuMAX " must be a multiple of pagesize (%u).", iface,
611 value, getpagesize());
612 } else {
613 aconf->block_size = (int)value;
614 }
615 }
616
617 if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "block-timeout", &value)) == 1) {
618 aconf->block_timeout = (int)value;
619 } else {
620 aconf->block_timeout = 10;
621 }
622
623 if ((SCConfGetChildValueIntWithDefault(if_root, if_default, "v2-block-size", &value)) == 1) {
624 if (value % getpagesize()) {
625 SCLogWarning("%s: v2-block-size %" PRIuMAX " must be a multiple of pagesize (%u).",
626 iface, value, getpagesize());
627 } else {
628 aconf->v2_block_size = (int)value;
629 }
630 }
631
632 (void)SCConfGetChildValueBoolWithDefault(if_root, if_default, "disable-promisc", &boolval);
633 if (boolval) {
634 SCLogConfig("%s: disabling promiscuous mode", aconf->iface);
635 aconf->promisc = 0;
636 }
637
638 if (SCConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
639 if (strcmp(tmpctype, "auto") == 0) {
641 } else if (SCConfValIsTrue(tmpctype)) {
643 } else if (SCConfValIsFalse(tmpctype)) {
645 } else if (strcmp(tmpctype, "kernel") == 0) {
647 } else {
648 SCLogWarning("%s: invalid value for checksum-checks", aconf->iface);
649 }
650 }
651
652finalize:
653
654 /* if the number of threads is not 1, we need to first check if fanout
655 * functions on this system. */
656 if (aconf->threads != 1) {
657 if (AFPIsFanoutSupported(aconf->cluster_id) == 0) {
658 if (aconf->threads != 0) {
659 SCLogNotice("%s: fanout not supported on this system, falling "
660 "back to 1 capture thread",
661 iface);
662 }
663 aconf->threads = 1;
664 }
665 }
666
667 /* try to automagically set the proper number of threads */
668 if (aconf->threads == 0) {
669 /* for cluster_flow use core count */
670 if (cluster_type == PACKET_FANOUT_HASH) {
672 SCLogPerf("%s: cluster_flow: %u cores, using %u threads", iface, aconf->threads,
673 aconf->threads);
674
675 /* for cluster_qm use RSS queue count */
676 } else if (cluster_type == PACKET_FANOUT_QM) {
677 int rss_queues = GetIfaceRSSQueuesNum(iface);
678 if (rss_queues > 0) {
679 aconf->threads = rss_queues;
680 SCLogPerf("%s: cluster_qm: %d RSS queues, using %u threads", iface, rss_queues,
681 aconf->threads);
682 }
683 }
684
685 if (aconf->threads) {
686 SCLogDebug("using %d threads for interface %s", aconf->threads, iface);
687 }
688 }
689 if (aconf->threads <= 0) {
690 aconf->threads = 1;
691 }
692 SC_ATOMIC_RESET(aconf->ref);
693 (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads);
694
695 if (aconf->ring_size != 0) {
696 if (aconf->ring_size * aconf->threads < (int)max_pending_packets) {
697 aconf->ring_size = max_pending_packets / aconf->threads + 1;
698 SCLogWarning("%s: inefficient setup: ring-size < max_pending_packets. "
699 "Resetting to decent value %d.",
700 iface, aconf->ring_size);
701 /* We want at least that max_pending_packets packets can be handled by the
702 * interface. This is generous if we have multiple interfaces listening. */
703 }
704 } else {
705 /* We want that max_pending_packets packets can be handled by suricata
706 * for this interface. To take burst into account we multiply the obtained
707 * size by 2. */
708 aconf->ring_size = max_pending_packets * 2 / aconf->threads;
709 }
710
711 int ltype = AFPGetLinkType(iface);
712 switch (ltype) {
714 /* af-packet can handle csum offloading */
715 if (LiveGetOffload() == 0) {
716 if (GetIfaceOffloading(iface, 0, 1) == 1) {
718 "%s: using AF_PACKET with offloads enabled leads to capture problems",
719 iface);
720 }
721 } else {
723 }
724 break;
725 case -1:
726 default:
727 break;
728 }
729
730 if (active_runmode == NULL || strcmp("workers", active_runmode) != 0) {
731 /* If we are using copy mode we need a lock */
732 aconf->flags |= AFP_SOCK_PROTECT;
733 aconf->flags |= AFP_NEED_PEER;
734 }
735
736 /* Warn if inline and defrag is enabled. */
739 "%s: AF_PACKET defrag is not recommended for inline use, please disable", iface);
740 }
741
742 /* Warn if not inline and defrag is disabled. */
743 if (aconf->copy_mode == AFP_COPY_MODE_NONE && cluster_type == PACKET_FANOUT_HASH &&
744 ((aconf->cluster_type & PACKET_FANOUT_FLAG_DEFRAG) == 0)) {
745 SCLogWarning("%s: AF_PACKET defrag is recommended for IDS cluster_flow", iface);
746 }
747
748 /* For tpacket-v2, warn if defrag is enabled and block-size is
749 * less than max defragmented packet size. */
750 if ((aconf->flags & AFP_TPACKET_V3) == 0 && (aconf->cluster_type & PACKET_FANOUT_FLAG_DEFRAG) &&
751 aconf->v2_block_size > 0 && aconf->v2_block_size < MAX_PACKET_SIZE) {
752 SCLogWarning("%s: AF_PACKET v2-block-size is not large enough for max fragmented IP packet "
753 "size (%u)",
754 iface, MAX_PACKET_SIZE);
755 }
756
757 /* For tpacket-v3, warn if defrag is enabled and block-block-size
758 * is less than max defragmented packet size. */
759 if ((aconf->flags & AFP_TPACKET_V3) && (aconf->cluster_type & PACKET_FANOUT_FLAG_DEFRAG) &&
760 (aconf->block_size < MAX_PACKET_SIZE)) {
761 SCLogWarning("%s: AF_PACKET block-size is not large enough for max fragmented IP packet "
762 "size (%u)",
763 iface, MAX_PACKET_SIZE);
764 }
765
766 /* Warn that if not-inline, tpacket-v3 is the better choice. */
767 if (aconf->copy_mode == AFP_COPY_MODE_NONE && (aconf->flags & AFP_TPACKET_V3) == 0) {
768 SCLogWarning("%s: AF_PACKET tpacket-v3 is recommended for non-inline operation", iface);
769 }
770
771 return aconf;
772}
773
774static int AFPConfigGeThreadsCount(void *conf)
775{
776 AFPIfaceConfig *afp = (AFPIfaceConfig *)conf;
777 return afp->threads;
778}
779
780#endif /* HAVE_AF_PACKET */
781
783{
784 SCEnter();
785
786/* We include only if AF_PACKET is enabled */
787#ifdef HAVE_AF_PACKET
788 int ret;
789 const char *live_dev = NULL;
790
792
793 (void)SCConfGet("af-packet.live-interface", &live_dev);
794
795 SCLogDebug("live_dev %s", live_dev);
796
797 if (AFPPeersListInit() != TM_ECODE_OK) {
798 FatalError("Unable to init peers list.");
799 }
800
801 ret = RunModeSetLiveCaptureAutoFp(ParseAFPConfig, AFPConfigGeThreadsCount, "ReceiveAFP",
802 "DecodeAFP", thread_name_autofp, live_dev);
803 if (ret != 0) {
804 FatalError("Unable to start runmode");
805 }
806
807 /* In IPS mode each threads must have a peer */
809 FatalError("Some IPS capture threads did not peer.");
810 }
811
812 SCLogDebug("RunModeIdsAFPAutoFp initialised");
813#endif /* HAVE_AF_PACKET */
814
815 SCReturnInt(0);
816}
817
818/**
819 * \brief Single thread version of the AF_PACKET processing.
820 */
822{
823 SCEnter();
824#ifdef HAVE_AF_PACKET
825 int ret;
826 const char *live_dev = NULL;
827
829
830 (void)SCConfGet("af-packet.live-interface", &live_dev);
831
832 if (AFPPeersListInit() != TM_ECODE_OK) {
833 FatalError("Unable to init peers list.");
834 }
835
836 ret = RunModeSetLiveCaptureSingle(ParseAFPConfig,
837 AFPConfigGeThreadsCount,
838 "ReceiveAFP",
839 "DecodeAFP", thread_name_single,
840 live_dev);
841 if (ret != 0) {
842 FatalError("Unable to start runmode");
843 }
844
845 /* In IPS mode each threads must have a peer */
847 FatalError("Some IPS capture threads did not peer.");
848 }
849
850 SCLogDebug("RunModeIdsAFPSingle initialised");
851
852#endif /* HAVE_AF_PACKET */
853 SCReturnInt(0);
854}
855
856/**
857 * \brief Workers version of the AF_PACKET processing.
858 *
859 * Start N threads with each thread doing all the work.
860 *
861 */
863{
864 SCEnter();
865#ifdef HAVE_AF_PACKET
866 int ret;
867 const char *live_dev = NULL;
868
870
871 (void)SCConfGet("af-packet.live-interface", &live_dev);
872
873 if (AFPPeersListInit() != TM_ECODE_OK) {
874 FatalError("Unable to init peers list.");
875 }
876
877 ret = RunModeSetLiveCaptureWorkers(ParseAFPConfig, AFPConfigGeThreadsCount, "ReceiveAFP",
878 "DecodeAFP", thread_name_workers, live_dev);
879 if (ret != 0) {
880 FatalError("Unable to start runmode");
881 }
882
883 /* In IPS mode each threads must have a peer */
885 FatalError("Some IPS capture threads did not peer.");
886 }
887
888 SCLogDebug("RunModeIdsAFPWorkers initialised");
889
890#endif /* HAVE_AF_PACKET */
891 SCReturnInt(0);
892}
893
894/**
895 * @}
896 */
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
SCConfNode * SCConfGetChildWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name)
Definition conf.c:379
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 SCConfGetChildValueIntWithDefault(const SCConfNode *base, const SCConfNode *dflt, const char *name, intmax_t *val)
Definition conf.c:476
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_KERNEL
Definition decode.h:47
@ CHECKSUM_VALIDATION_DISABLE
Definition decode.h:43
int BypassedFlowManagerRegisterUpdateFunc(BypassedUpdateFunc UpdateFunc, void *data)
int BypassedFlowManagerRegisterCheckFunc(BypassedCheckFunc CheckFunc, BypassedCheckFuncInit CheckFuncInit, void *data)
int AFPGetLinkType(const char *ifname)
int AFPIsFanoutSupported(uint16_t cluster_id)
test if we can use FANOUT. Older kernels like those in CentOS6 have HAVE_PACKET_FANOUT defined but fa...
TmEcode AFPPeersListInit(void)
Init the global list of AFPPeer.
TmEcode AFPPeersListCheck(void)
Check that all AFPPeer got a peer.
int RunModeIdsAFPWorkers(void)
Workers version of the AF_PACKET processing.
int RunModeIdsAFPSingle(void)
Single thread version of the AF_PACKET processing.
uint32_t max_pending_packets
Definition suricata.c:183
const char * RunModeAFPGetDefaultMode(void)
void RunModeIdsAFPRegister(void)
int RunModeIdsAFPAutoFp(void)
char * RunmodeGetActive(void)
Definition runmodes.c:199
const char * thread_name_single
Definition runmodes.c:67
void RunModeEnablesBypassManager(void)
Definition runmodes.c:465
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_AFP_DEV
Definition runmodes.h:36
#define AFP_BLOCK_SIZE_DEFAULT_ORDER
#define PACKET_FANOUT_CPU
#define AFP_MMAP_LOCKED
#define AFP_COPY_MODE_NONE
#define PACKET_FANOUT_HASH
#define PACKET_FANOUT_RND
#define AFP_NEED_PEER
#define AFP_EMERGENCY_MODE
#define AFP_BYPASS
#define MAX_PACKET_SIZE
#define AFP_XDPBYPASS
#define AFP_COPY_MODE_IPS
#define AFP_TPACKET_V3
#define PACKET_FANOUT_QM
#define AFP_SOCK_PROTECT
#define PACKET_FANOUT_FLAG_DEFRAG
#define PACKET_FANOUT_LB
#define AFP_COPY_MODE_TAP
const char * out_iface
void(* DerefFunc)(void *)
char iface[AFP_IFACE_NAME_LENGTH]
ChecksumValidationMode checksum_mode
const char * ebpf_filter_file
const char * xdp_filter_file
const char * bpf_filter
const char * ebpf_lb_file
size_t strlcpy(char *dst, const char *src, size_t siz)
void EngineModeSetIPS(void)
Definition suricata.c:259
@ TM_ECODE_OK
#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
int StringParseInt32(int32_t *res, int base, size_t len, const char *str)
Definition util-byte.c:622
SCConfNode * ConfFindDeviceConfig(SCConfNode *node, const char *iface)
Find the configuration node for a specific device.
Definition util-conf.c:121
uint16_t UtilCpuGetNumProcessorsConfigured(void)
Get the number of cpus configured in the system.
Definition util-cpu.c:59
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition util-cpu.c:108
#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 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
#define SCLogConfig(...)
Definition util-debug.h:229
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
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