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