Coverage Report

Created: 2025-07-23 07:29

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