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