Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/runmode-pfring.c
Line
Count
Source
1
/* Copyright (C) 2007-2018 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
#include "suricata-common.h"
19
#include "runmode-pfring.h"
20
#include "tm-threads.h"
21
#include "conf.h"
22
#include "runmodes.h"
23
#include "source-pfring.h"
24
#include "suricata.h"
25
#include "util-bpf.h"
26
#include "util-debug.h"
27
#include "util-time.h"
28
#include "util-cpu.h"
29
#include "util-runmodes.h"
30
#include "util-device.h"
31
#include "util-ioctl.h"
32
#include "util-byte.h"
33
#include "util-conf.h"
34
35
#ifdef HAVE_PFRING
36
#include <pfring.h>
37
#endif
38
39
#define PFRING_CONF_V1 1
40
#define PFRING_CONF_V2 2
41
42
const char *RunModeIdsPfringGetDefaultMode(void)
43
0
{
44
#ifdef HAVE_PFRING
45
    return "workers";
46
#else
47
0
    return NULL;
48
0
#endif
49
0
}
50
51
void RunModeIdsPfringRegister(void)
52
37
{
53
37
    RunModeRegisterNewRunMode(RUNMODE_PFRING, "autofp",
54
37
            "Multi threaded pfring mode.  Packets from "
55
37
            "each flow are assigned to a single detect "
56
37
            "thread, unlike \"pfring_auto\" where packets "
57
37
            "from the same flow can be processed by any "
58
37
            "detect thread",
59
37
            RunModeIdsPfringAutoFp, NULL);
60
37
    RunModeRegisterNewRunMode(
61
37
            RUNMODE_PFRING, "single", "Single threaded pfring mode", RunModeIdsPfringSingle, NULL);
62
37
    RunModeRegisterNewRunMode(RUNMODE_PFRING, "workers",
63
37
            "Workers pfring mode, each thread does all"
64
37
            " tasks from acquisition to logging",
65
37
            RunModeIdsPfringWorkers, NULL);
66
37
    return;
67
37
}
68
69
#ifdef HAVE_PFRING
70
static void PfringDerefConfig(void *conf)
71
{
72
    PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
73
    if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) {
74
        SCFree(pfp);
75
    }
76
}
77
78
/**
79
 * \brief extract information from config file
80
 *
81
 * The returned structure will be freed by the thread init function.
82
 * This is thus necessary to or copy the structure before giving it
83
 * to thread or to reparse the file for each thread (and thus have
84
 * new structure.
85
 *
86
 * If old config system is used, then return the same parameters
87
 * value for each interface.
88
 *
89
 * \return a PfringIfaceConfig corresponding to the interface name
90
 */
91
static void *OldParsePfringConfig(const char *iface)
92
{
93
    const char *threadsstr = NULL;
94
    PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
95
    const char *tmpclusterid;
96
    const char *tmpctype = NULL;
97
    cluster_type default_ctype = CLUSTER_FLOW;
98
99
    if (unlikely(pfconf == NULL)) {
100
        return NULL;
101
    }
102
103
    if (iface == NULL) {
104
        SCFree(pfconf);
105
        return NULL;
106
    }
107
108
    strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
109
    pfconf->flags = 0;
110
    pfconf->threads = 1;
111
    pfconf->cluster_id = 1;
112
    pfconf->ctype = default_ctype;
113
    pfconf->DerefFunc = PfringDerefConfig;
114
    pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
115
    SC_ATOMIC_INIT(pfconf->ref);
116
    (void) SC_ATOMIC_ADD(pfconf->ref, 1);
117
118
    /* Find initial node */
119
    if (ConfGet("pfring.threads", &threadsstr) != 1) {
120
        pfconf->threads = 1;
121
    } else {
122
        if (threadsstr != NULL) {
123
            if (StringParseInt32(&pfconf->threads, 10, 0, threadsstr) < 0) {
124
                SCLogWarning("Invalid value for "
125
                             "pfring.threads: '%s'. Resetting to 1.",
126
                        threadsstr);
127
                pfconf->threads = 1;
128
            }
129
        }
130
    }
131
    if (pfconf->threads == 0) {
132
        pfconf->threads = 1;
133
    }
134
135
    SC_ATOMIC_RESET(pfconf->ref);
136
    (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
137
138
    if (strncmp(pfconf->iface, "zc", 2) == 0) {
139
        SCLogInfo("%s: ZC interface detected, not setting cluster-id", pfconf->iface);
140
    }
141
    else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
142
        SCLogInfo("DNA interface detected, not setting cluster-id");
143
    } else if (ConfGet("pfring.cluster-id", &tmpclusterid) != 1) {
144
        SCLogError("Could not get cluster-id from config");
145
    } else {
146
        if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
147
            SCLogWarning("Invalid value for "
148
                         "pfring.cluster_id: '%s'. Resetting to 1.",
149
                    tmpclusterid);
150
            pfconf->cluster_id = 1;
151
        }
152
        pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
153
        SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
154
    }
155
156
    if (strncmp(pfconf->iface, "zc", 2) == 0) {
157
        SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING", pfconf->iface);
158
    } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
159
        SCLogInfo(
160
                "%s: DNA interface detected, not setting cluster type for PF_RING", pfconf->iface);
161
    } else if (ConfGet("pfring.cluster-type", &tmpctype) != 1) {
162
        SCLogError("Could not get cluster-type from config");
163
    } else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
164
        SCLogInfo("%s: Using round-robin cluster mode for PF_RING", pfconf->iface);
165
        pfconf->ctype = (cluster_type)tmpctype;
166
    } else if (strcmp(tmpctype, "cluster_flow") == 0) {
167
        SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
168
        pfconf->ctype = (cluster_type)tmpctype;
169
    } else {
170
        SCLogError("invalid cluster-type %s", tmpctype);
171
        SCFree(pfconf);
172
        return NULL;
173
    }
174
175
    return pfconf;
176
}
177
178
/**
179
 * \brief extract information from config file
180
 *
181
 * The returned structure will be freed by the thread init function.
182
 * This is thus necessary to or copy the structure before giving it
183
 * to thread or to reparse the file for each thread (and thus have
184
 * new structure.
185
 *
186
 * If old config system is used, then return the same parameters
187
 * value for each interface.
188
 *
189
 * \return a PfringIfaceConfig corresponding to the interface name
190
 */
191
static void *ParsePfringConfig(const char *iface)
192
{
193
    const char *threadsstr = NULL;
194
    ConfNode *if_root;
195
    ConfNode *if_default = NULL;
196
    ConfNode *pf_ring_node;
197
    PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
198
    const char *tmpclusterid;
199
    const char *tmpctype = NULL;
200
    cluster_type default_ctype = CLUSTER_FLOW;
201
    int getctype = 0;
202
    int bool_val;
203
    const char *active_runmode = RunmodeGetActive();
204
205
    if (unlikely(pfconf == NULL)) {
206
        return NULL;
207
    }
208
209
    if (iface == NULL) {
210
        SCFree(pfconf);
211
        return NULL;
212
    }
213
214
    memset(pfconf, 0, sizeof(PfringIfaceConfig));
215
    strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
216
    pfconf->threads = 1;
217
    pfconf->cluster_id = 1;
218
    pfconf->ctype = (cluster_type)default_ctype;
219
    pfconf->DerefFunc = PfringDerefConfig;
220
    SC_ATOMIC_INIT(pfconf->ref);
221
    (void) SC_ATOMIC_ADD(pfconf->ref, 1);
222
223
    /* Find initial node */
224
    pf_ring_node = ConfGetNode("pfring");
225
    if (pf_ring_node == NULL) {
226
        SCLogInfo("Unable to find pfring config using default value");
227
        return pfconf;
228
    }
229
230
    if_root = ConfFindDeviceConfig(pf_ring_node, iface);
231
232
    if_default = ConfFindDeviceConfig(pf_ring_node, "default");
233
234
    if (if_root == NULL && if_default == NULL) {
235
        SCLogInfo("Unable to find pfring config for "
236
                  "interface %s, using default value or 1.0 "
237
                  "configuration system. ",
238
                  iface);
239
        return pfconf;
240
    }
241
242
    /* If there is no setting for current interface use default one as main iface */
243
    if (if_root == NULL) {
244
        if_root = if_default;
245
        if_default = NULL;
246
    }
247
248
    if (active_runmode && !strcmp("single", active_runmode)) {
249
        pfconf->threads = 1;
250
    } else if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
251
        pfconf->threads = 1;
252
    } else if (threadsstr != NULL) {
253
        if (strcmp(threadsstr, "auto") == 0) {
254
            pfconf->threads = (int)UtilCpuGetNumProcessorsOnline();
255
            if (pfconf->threads > 0) {
256
                SCLogPerf("%u cores, so using %u threads", pfconf->threads, pfconf->threads);
257
            } else {
258
                pfconf->threads = GetIfaceRSSQueuesNum(iface);
259
                if (pfconf->threads > 0) {
260
                    SCLogPerf("%d RSS queues, so using %u threads", pfconf->threads, pfconf->threads);
261
                }
262
            }
263
        } else {
264
            uint16_t threads = 0;
265
            if (StringParseUint16(&threads, 10, 0, (const char *)threadsstr) < 0) {
266
                SCLogWarning("Invalid value for "
267
                             "pfring.threads: '%s'. Resetting to 1.",
268
                        threadsstr);
269
                pfconf->threads = 1;
270
            } else {
271
                pfconf->threads = threads;
272
            }
273
        }
274
    }
275
    if (pfconf->threads <= 0) {
276
        pfconf->threads = 1;
277
    }
278
279
    SC_ATOMIC_RESET(pfconf->ref);
280
    (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
281
282
    /* command line value has precedence */
283
    if (ConfGet("pfring.cluster-id", &tmpclusterid) == 1) {
284
        if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
285
            SCLogWarning("Invalid value for "
286
                         "pfring.cluster-id: '%s'. Resetting to 1.",
287
                    tmpclusterid);
288
            pfconf->cluster_id = 1;
289
        }
290
        pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
291
        SCLogDebug("Going to use command-line provided cluster-id %" PRId32,
292
                   pfconf->cluster_id);
293
    } else {
294
295
        if (strncmp(pfconf->iface, "zc", 2) == 0) {
296
            SCLogInfo(
297
                    "%s: ZC interface detected, not setting cluster-id for PF_RING", pfconf->iface);
298
        } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
299
            SCLogInfo("%s: DNA interface detected, not setting cluster-id for PF_RING",
300
                    pfconf->iface);
301
        } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
302
            SCLogError("Could not get cluster-id from config");
303
        } else {
304
            if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) {
305
                SCLogWarning("Invalid value for "
306
                             "pfring.cluster-id: '%s'. Resetting to 1.",
307
                        tmpclusterid);
308
                pfconf->cluster_id = 1;
309
            }
310
            pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
311
            SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
312
        }
313
    }
314
315
    ConfSetBPFFilter(if_root, if_default, pfconf->iface, &pfconf->bpf_filter);
316
317
    if (EngineModeIsIPS()) {
318
        FatalError("IPS mode not supported in PF_RING.");
319
    }
320
321
    if (ConfGet("pfring.cluster-type", &tmpctype) == 1) {
322
        SCLogDebug("Going to use command-line provided cluster-type");
323
        getctype = 1;
324
    } else {
325
        if (strncmp(pfconf->iface, "zc", 2) == 0) {
326
            SCLogInfo("%s: ZC interface detected, not setting cluster type for PF_RING",
327
                    pfconf->iface);
328
        } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
329
            SCLogInfo("%s: DNA interface detected, not setting cluster type for PF_RING",
330
                    pfconf->iface);
331
        } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
332
            SCLogError("Could not get cluster-type from config");
333
        } else {
334
            getctype = 1;
335
        }
336
    }
337
338
    if (getctype) {
339
        if (strcmp(tmpctype, "cluster_round_robin") == 0) {
340
            SCLogInfo("%s: Using round-robin cluster mode for PF_RING."
341
                      " This mode is not recommended.",
342
                    pfconf->iface);
343
            pfconf->ctype = CLUSTER_ROUND_ROBIN;
344
        } else if (strcmp(tmpctype, "cluster_flow") == 0) {
345
            SCLogInfo("%s: Using flow cluster mode for PF_RING", pfconf->iface);
346
            pfconf->ctype = CLUSTER_FLOW;
347
        } else if (strcmp(tmpctype, "cluster_inner_flow") == 0) {
348
            SCLogInfo("%s: Using flow cluster mode inner mode for PF_RING", pfconf->iface);
349
            pfconf->ctype = CLUSTER_INNER_FLOW;
350
        } else if (strcmp(tmpctype, "cluster_inner_flow_2_tuple") == 0) {
351
            SCLogInfo("%s: Using flow cluster inner 2 tuple mode for PF_RING", pfconf->iface);
352
            pfconf->ctype = CLUSTER_INNER_FLOW_2_TUPLE;
353
        } else if (strcmp(tmpctype, "cluster_inner_flow_4_tuple") == 0) {
354
            SCLogInfo("%s: Using flow cluster inner 4 tuple mode for PF_RING", pfconf->iface);
355
            pfconf->ctype = CLUSTER_INNER_FLOW_4_TUPLE;
356
        } else if (strcmp(tmpctype, "cluster_inner_flow_5_tuple") == 0) {
357
            SCLogInfo("%s: Using flow cluster inner 5 tuple mode for PF_RING", pfconf->iface);
358
            pfconf->ctype = CLUSTER_INNER_FLOW_5_TUPLE;
359
        } else {
360
            SCLogError("invalid cluster-type %s", tmpctype);
361
            SCFree(pfconf);
362
            return NULL;
363
        }
364
    }
365
    if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
366
        if (strcmp(tmpctype, "auto") == 0) {
367
            pfconf->checksum_mode = CHECKSUM_VALIDATION_AUTO;
368
        } else if (ConfValIsTrue(tmpctype)) {
369
            pfconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE;
370
        } else if (ConfValIsFalse(tmpctype)) {
371
            pfconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE;
372
        } else if (strcmp(tmpctype, "rx-only") == 0) {
373
            pfconf->checksum_mode = CHECKSUM_VALIDATION_RXONLY;
374
        } else {
375
            SCLogError("%s: Invalid value for checksum-checks", pfconf->iface);
376
        }
377
    }
378
379
    if (ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &bool_val) == 1) {
380
        if (bool_val) {
381
#ifdef HAVE_PF_RING_FLOW_OFFLOAD
382
            SCLogConfig("%s: Enabling bypass support in PF_RING (if supported by underlying hw)",
383
                    pfconf->iface);
384
            pfconf->flags |= PFRING_CONF_FLAGS_BYPASS;
385
#else
386
            SCLogError("Bypass is not supported by this Pfring version, please upgrade");
387
            SCFree(pfconf);
388
            return NULL;
389
#endif
390
        }
391
    }
392
393
    if (LiveGetOffload() == 0) {
394
        if (GetIfaceOffloading(iface, 0, 1) == 1) {
395
            SCLogWarning("Using PF_RING with offloading activated leads to capture problems");
396
        }
397
    } else {
398
        DisableIfaceOffloading(LiveGetDevice(iface), 0, 1);
399
    }
400
    return pfconf;
401
}
402
403
static int PfringConfigGetThreadsCount(void *conf)
404
{
405
    PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
406
    return pfp->threads;
407
}
408
409
static int PfringConfLevel(void)
410
{
411
    const char *def_dev = NULL;
412
    /* 1.0 config should return a string */
413
    if (ConfGet("pfring.interface", &def_dev) != 1) {
414
        return PFRING_CONF_V2;
415
    } else {
416
        return PFRING_CONF_V1;
417
    }
418
}
419
420
static int GetDevAndParser(const char **live_dev, ConfigIfaceParserFunc *parser)
421
{
422
     ConfGet("pfring.live-interface", live_dev);
423
424
    /* determine which config type we have */
425
    if (PfringConfLevel() > PFRING_CONF_V1) {
426
        *parser = ParsePfringConfig;
427
    } else {
428
        SCLogInfo("Using 1.0 style configuration for pfring");
429
        *parser = OldParsePfringConfig;
430
        /* In v1: try to get interface name from config */
431
        if (*live_dev == NULL) {
432
            if (ConfGet("pfring.interface", live_dev) == 1) {
433
                SCLogInfo("Using interface %s", *live_dev);
434
                LiveRegisterDevice(*live_dev);
435
            } else {
436
                SCLogInfo("No interface found, problem incoming");
437
                *live_dev = NULL;
438
            }
439
        }
440
    }
441
442
    return 0;
443
}
444
#endif
445
446
int RunModeIdsPfringAutoFp(void)
447
0
{
448
0
    SCEnter();
449
450
/* We include only if pfring is enabled */
451
#ifdef HAVE_PFRING
452
    int ret;
453
    const char *live_dev = NULL;
454
    ConfigIfaceParserFunc tparser;
455
456
    TimeModeSetLive();
457
458
    ret = GetDevAndParser(&live_dev, &tparser);
459
    if (ret != 0) {
460
        FatalError("Unable to get parser and interface params");
461
    }
462
463
    ret = RunModeSetLiveCaptureAutoFp(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
464
            "DecodePfring", thread_name_autofp, live_dev);
465
    if (ret != 0) {
466
        FatalError("Runmode start failed");
467
    }
468
469
    SCLogInfo("RunModeIdsPfringAutoFp initialised");
470
#endif /* HAVE_PFRING */
471
472
0
    return 0;
473
0
}
474
475
int RunModeIdsPfringSingle(void)
476
0
{
477
0
    SCEnter();
478
479
/* We include only if pfring is enabled */
480
#ifdef HAVE_PFRING
481
    int ret;
482
    const char *live_dev = NULL;
483
    ConfigIfaceParserFunc tparser;
484
485
    TimeModeSetLive();
486
487
    ret = GetDevAndParser(&live_dev, &tparser);
488
    if (ret != 0) {
489
        FatalError("Unable to get parser and interface params");
490
    }
491
492
    ret = RunModeSetLiveCaptureSingle(tparser,
493
                              PfringConfigGetThreadsCount,
494
                              "ReceivePfring",
495
                              "DecodePfring", thread_name_single,
496
                              live_dev);
497
    if (ret != 0) {
498
        FatalError("Runmode start failed");
499
    }
500
501
    SCLogInfo("RunModeIdsPfringSingle initialised");
502
#endif /* HAVE_PFRING */
503
504
0
    return 0;
505
0
}
506
507
int RunModeIdsPfringWorkers(void)
508
0
{
509
0
    SCEnter();
510
511
/* We include only if pfring is enabled */
512
#ifdef HAVE_PFRING
513
    int ret;
514
    const char *live_dev = NULL;
515
    ConfigIfaceParserFunc tparser;
516
517
    TimeModeSetLive();
518
519
    ret = GetDevAndParser(&live_dev, &tparser);
520
    if (ret != 0) {
521
        FatalError("Unable to get parser and interface params");
522
    }
523
524
    ret = RunModeSetLiveCaptureWorkers(tparser, PfringConfigGetThreadsCount, "ReceivePfring",
525
            "DecodePfring", thread_name_workers, live_dev);
526
    if (ret != 0) {
527
        FatalError("Runmode start failed");
528
    }
529
530
    SCLogInfo("RunModeIdsPfringWorkers initialised");
531
#endif /* HAVE_PFRING */
532
533
0
    return 0;
534
0
}