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