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