/src/suricata/src/runmode-pcap.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2022 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-pcap.h" |
20 | | #include "runmodes.h" |
21 | | #include "output.h" |
22 | | |
23 | | #include "util-conf.h" |
24 | | #include "util-debug.h" |
25 | | #include "util-time.h" |
26 | | #include "util-cpu.h" |
27 | | #include "util-device-private.h" |
28 | | #include "util-runmodes.h" |
29 | | #include "util-misc.h" |
30 | | #include "util-byte.h" |
31 | | |
32 | | const char *RunModeIdsGetDefaultMode(void) |
33 | 0 | { |
34 | 0 | return "autofp"; |
35 | 0 | } |
36 | | |
37 | | int RunModeIdsPcapWorkers(void); |
38 | | |
39 | | void RunModeIdsPcapRegister(void) |
40 | 43 | { |
41 | 43 | RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "single", "Single threaded pcap live mode", |
42 | 43 | RunModeIdsPcapSingle, NULL); |
43 | 43 | RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "autofp", |
44 | 43 | "Multi-threaded pcap live mode. Packets from each flow are assigned to a consistent " |
45 | 43 | "detection thread", |
46 | 43 | RunModeIdsPcapAutoFp, NULL); |
47 | 43 | RunModeRegisterNewRunMode(RUNMODE_PCAP_DEV, "workers", |
48 | 43 | "Workers pcap live mode, each thread does all" |
49 | 43 | " tasks from acquisition to logging", |
50 | 43 | RunModeIdsPcapWorkers, NULL); |
51 | 43 | } |
52 | | |
53 | | static void PcapDerefConfig(void *conf) |
54 | 0 | { |
55 | 0 | PcapIfaceConfig *pfp = (PcapIfaceConfig *)conf; |
56 | | /* Pcap config is used only once but cost of this low. */ |
57 | 0 | if (SC_ATOMIC_SUB(pfp->ref, 1) == 1) { |
58 | 0 | SCFree(pfp); |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | static void *ParsePcapConfig(const char *iface) |
63 | 0 | { |
64 | 0 | const char *threadsstr = NULL; |
65 | 0 | SCConfNode *if_root; |
66 | 0 | SCConfNode *if_default = NULL; |
67 | 0 | SCConfNode *pcap_node; |
68 | 0 | PcapIfaceConfig *aconf = SCMalloc(sizeof(*aconf)); |
69 | 0 | const char *tmpbpf; |
70 | 0 | const char *tmpctype; |
71 | 0 | intmax_t value; |
72 | 0 | int promisc = 0; |
73 | 0 | intmax_t snaplen = 0; |
74 | |
|
75 | 0 | if (unlikely(aconf == NULL)) { |
76 | 0 | return NULL; |
77 | 0 | } |
78 | | |
79 | 0 | if (iface == NULL) { |
80 | 0 | SCFree(aconf); |
81 | 0 | return NULL; |
82 | 0 | } |
83 | | |
84 | 0 | memset(aconf, 0x00, sizeof(*aconf)); |
85 | 0 | strlcpy(aconf->iface, iface, sizeof(aconf->iface)); |
86 | |
|
87 | 0 | aconf->buffer_size = 0; |
88 | | /* If set command line option has precedence over config */ |
89 | 0 | if ((SCConfGetInt("pcap.buffer-size", &value)) == 1) { |
90 | 0 | if (value >= 0 && value <= INT_MAX) { |
91 | 0 | SCLogInfo("Pcap will use %d buffer size", (int)value); |
92 | 0 | aconf->buffer_size = (int)value; |
93 | 0 | } else { |
94 | 0 | SCLogWarning("pcap.buffer-size " |
95 | 0 | "value of %" PRIiMAX " is invalid. Valid range is " |
96 | 0 | "0-2147483647", |
97 | 0 | value); |
98 | 0 | } |
99 | 0 | } |
100 | |
|
101 | 0 | aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO; |
102 | 0 | aconf->bpf_filter = NULL; |
103 | 0 | if ((SCConfGet("bpf-filter", &tmpbpf)) == 1) { |
104 | 0 | aconf->bpf_filter = tmpbpf; |
105 | 0 | } |
106 | |
|
107 | 0 | SC_ATOMIC_INIT(aconf->ref); |
108 | 0 | aconf->DerefFunc = PcapDerefConfig; |
109 | 0 | aconf->threads = 1; |
110 | | |
111 | | /* Find initial node */ |
112 | 0 | pcap_node = SCConfGetNode("pcap"); |
113 | 0 | if (pcap_node == NULL) { |
114 | 0 | SCLogInfo("Unable to find pcap config using default value"); |
115 | 0 | return aconf; |
116 | 0 | } |
117 | | |
118 | 0 | if_root = ConfFindDeviceConfig(pcap_node, iface); |
119 | |
|
120 | 0 | if_default = ConfFindDeviceConfig(pcap_node, "default"); |
121 | |
|
122 | 0 | if (if_root == NULL && if_default == NULL) { |
123 | 0 | SCLogInfo("Unable to find pcap config for " |
124 | 0 | "interface %s, using default value", |
125 | 0 | iface); |
126 | 0 | return aconf; |
127 | 0 | } |
128 | | |
129 | | /* If there is no setting for current interface use default one as main iface */ |
130 | 0 | if (if_root == NULL) { |
131 | 0 | if_root = if_default; |
132 | 0 | if_default = NULL; |
133 | 0 | } |
134 | |
|
135 | 0 | if (SCConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) { |
136 | 0 | aconf->threads = 1; |
137 | 0 | } else { |
138 | 0 | if (threadsstr != NULL) { |
139 | 0 | if (StringParseUint16(&aconf->threads, 10, 0, (const char *)threadsstr) < 0) { |
140 | 0 | SCLogWarning("Invalid value for " |
141 | 0 | "pcap.threads: %s, resetting to 1", |
142 | 0 | threadsstr); |
143 | 0 | aconf->threads = 1; |
144 | 0 | } |
145 | 0 | } |
146 | 0 | } |
147 | 0 | if (aconf->threads == 0) { |
148 | 0 | aconf->threads = 1; |
149 | 0 | } |
150 | 0 | (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads); |
151 | |
|
152 | 0 | if (aconf->buffer_size == 0) { |
153 | 0 | const char *s_limit = NULL; |
154 | 0 | int ret; |
155 | 0 | ret = SCConfGetChildValueWithDefault(if_root, if_default, "buffer-size", &s_limit); |
156 | 0 | if (ret == 1 && s_limit) { |
157 | 0 | uint64_t bsize = 0; |
158 | |
|
159 | 0 | if (ParseSizeStringU64(s_limit, &bsize) < 0) { |
160 | 0 | SCLogError("Failed to parse pcap buffer size: %s", s_limit); |
161 | 0 | } else { |
162 | | /* the string 2gb returns 2147483648 which is 1 to high |
163 | | * for a int. */ |
164 | 0 | if (bsize == (uint64_t)((uint64_t)INT_MAX + (uint64_t)1)) |
165 | 0 | bsize = (uint64_t)INT_MAX; |
166 | |
|
167 | 0 | if (bsize > INT_MAX) { |
168 | 0 | SCLogError("Failed to set pcap buffer size: 2gb max. %" PRIu64 " > %d", bsize, |
169 | 0 | INT_MAX); |
170 | 0 | } else { |
171 | 0 | aconf->buffer_size = (int)bsize; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } |
175 | 0 | } |
176 | |
|
177 | 0 | if (aconf->bpf_filter == NULL) { |
178 | | /* set bpf filter if we have one */ |
179 | 0 | if (SCConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &tmpbpf) != 1) { |
180 | 0 | SCLogDebug("could not get bpf or none specified"); |
181 | 0 | } else { |
182 | 0 | aconf->bpf_filter = tmpbpf; |
183 | 0 | } |
184 | 0 | } else { |
185 | 0 | SCLogInfo("BPF filter set from command line or via old 'bpf-filter' option."); |
186 | 0 | } |
187 | |
|
188 | 0 | if (SCConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) { |
189 | 0 | if (strcmp(tmpctype, "auto") == 0) { |
190 | 0 | aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO; |
191 | 0 | } else if (SCConfValIsTrue(tmpctype)) { |
192 | 0 | aconf->checksum_mode = CHECKSUM_VALIDATION_ENABLE; |
193 | 0 | } else if (SCConfValIsFalse(tmpctype)) { |
194 | 0 | aconf->checksum_mode = CHECKSUM_VALIDATION_DISABLE; |
195 | 0 | } else { |
196 | 0 | SCLogError("Invalid value for checksum-checks for %s", aconf->iface); |
197 | 0 | } |
198 | 0 | } |
199 | |
|
200 | 0 | aconf->promisc = LIBPCAP_PROMISC; |
201 | 0 | if (SCConfGetChildValueBoolWithDefault(if_root, if_default, "promisc", &promisc) != 1) { |
202 | 0 | SCLogDebug("could not get promisc or none specified"); |
203 | 0 | } else { |
204 | 0 | aconf->promisc = promisc; |
205 | 0 | } |
206 | |
|
207 | 0 | aconf->snaplen = 0; |
208 | 0 | if (SCConfGetChildValueIntWithDefault(if_root, if_default, "snaplen", &snaplen) != 1) { |
209 | 0 | SCLogDebug("could not get snaplen or none specified"); |
210 | 0 | } else if (snaplen < INT_MIN || snaplen > INT_MAX) { |
211 | 0 | SCLogDebug("snaplen value is not in the accepted range"); |
212 | 0 | } else { |
213 | 0 | aconf->snaplen = (int)snaplen; |
214 | 0 | } |
215 | |
|
216 | 0 | return aconf; |
217 | 0 | } |
218 | | |
219 | | static uint16_t PcapConfigGeThreadsCount(void *conf) |
220 | 0 | { |
221 | 0 | PcapIfaceConfig *pfp = (PcapIfaceConfig *)conf; |
222 | 0 | return pfp->threads; |
223 | 0 | } |
224 | | |
225 | | /** |
226 | | * \brief Single thread version of the Pcap live processing. |
227 | | */ |
228 | | int RunModeIdsPcapSingle(void) |
229 | 0 | { |
230 | 0 | int ret; |
231 | 0 | const char *live_dev = NULL; |
232 | |
|
233 | 0 | SCEnter(); |
234 | |
|
235 | 0 | TimeModeSetLive(); |
236 | |
|
237 | 0 | (void)SCConfGet("pcap.single-pcap-dev", &live_dev); |
238 | |
|
239 | 0 | ret = RunModeSetLiveCaptureSingle(ParsePcapConfig, |
240 | 0 | PcapConfigGeThreadsCount, |
241 | 0 | "ReceivePcap", |
242 | 0 | "DecodePcap", thread_name_single, |
243 | 0 | live_dev); |
244 | 0 | if (ret != 0) { |
245 | 0 | FatalError("Runmode start failed"); |
246 | 0 | } |
247 | | |
248 | 0 | SCLogDebug("RunModeIdsPcapSingle initialised"); |
249 | |
|
250 | 0 | SCReturnInt(0); |
251 | 0 | } |
252 | | |
253 | | /** |
254 | | * \brief RunModIdsPcapAutoFp set up the following thread packet handlers: |
255 | | * - Receive thread (from pcap device) |
256 | | * - Decode thread |
257 | | * - Stream thread |
258 | | * - Detect: If we have only 1 cpu, it will setup one Detect thread |
259 | | * If we have more than one, it will setup num_cpus - 1 |
260 | | * starting from the second cpu available. |
261 | | * - Outputs thread |
262 | | * By default the threads will use the first cpu available |
263 | | * except the Detection threads if we have more than one cpu. |
264 | | * |
265 | | * \retval 0 If all goes well. (If any problem is detected the engine will |
266 | | * exit()). |
267 | | */ |
268 | | int RunModeIdsPcapAutoFp(void) |
269 | 0 | { |
270 | 0 | int ret; |
271 | 0 | const char *live_dev = NULL; |
272 | |
|
273 | 0 | SCEnter(); |
274 | 0 | TimeModeSetLive(); |
275 | |
|
276 | 0 | (void)SCConfGet("pcap.single-pcap-dev", &live_dev); |
277 | |
|
278 | 0 | ret = RunModeSetLiveCaptureAutoFp(ParsePcapConfig, PcapConfigGeThreadsCount, "ReceivePcap", |
279 | 0 | "DecodePcap", thread_name_autofp, live_dev); |
280 | 0 | if (ret != 0) { |
281 | 0 | FatalError("Runmode start failed"); |
282 | 0 | } |
283 | | |
284 | 0 | SCLogDebug("RunModeIdsPcapAutoFp initialised"); |
285 | |
|
286 | 0 | SCReturnInt(0); |
287 | 0 | } |
288 | | |
289 | | /** |
290 | | * \brief Workers version of the PCAP LIVE processing. |
291 | | * |
292 | | * Start N threads with each thread doing all the work. |
293 | | * |
294 | | */ |
295 | | int RunModeIdsPcapWorkers(void) |
296 | 0 | { |
297 | 0 | int ret; |
298 | 0 | const char *live_dev = NULL; |
299 | 0 | SCEnter(); |
300 | |
|
301 | 0 | TimeModeSetLive(); |
302 | |
|
303 | 0 | (void)SCConfGet("pcap.single-pcap-dev", &live_dev); |
304 | |
|
305 | 0 | ret = RunModeSetLiveCaptureWorkers(ParsePcapConfig, PcapConfigGeThreadsCount, "ReceivePcap", |
306 | 0 | "DecodePcap", thread_name_workers, live_dev); |
307 | 0 | if (ret != 0) { |
308 | 0 | FatalError("Unable to start runmode"); |
309 | 0 | } |
310 | | |
311 | 0 | SCLogDebug("RunModeIdsPcapWorkers initialised"); |
312 | |
|
313 | 0 | SCReturnInt(0); |
314 | 0 | } |