/src/suricata7/src/source-pcap-file.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2016 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 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * File based pcap packet acquisition support |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "source-pcap-file.h" |
28 | | #include "source-pcap-file-helper.h" |
29 | | #include "source-pcap-file-directory-helper.h" |
30 | | #include "flow-manager.h" |
31 | | #include "util-checksum.h" |
32 | | #include "runmode-unix-socket.h" |
33 | | #include "suricata.h" |
34 | | |
35 | | extern uint16_t max_pending_packets; |
36 | | PcapFileGlobalVars pcap_g; |
37 | | |
38 | | /** |
39 | | * Union determining whether the behavior of the thread is file or directory |
40 | | */ |
41 | | typedef union PcapFileBehaviorVar_ |
42 | | { |
43 | | PcapFileDirectoryVars *directory; |
44 | | PcapFileFileVars *file; |
45 | | } PcapFileBehaviorVar; |
46 | | |
47 | | /** |
48 | | * Data specific to the thread |
49 | | */ |
50 | | typedef struct PcapFileThreadVars_ |
51 | | { |
52 | | PcapFileBehaviorVar behavior; |
53 | | bool is_directory; |
54 | | |
55 | | PcapFileSharedVars shared; |
56 | | } PcapFileThreadVars; |
57 | | |
58 | | static TmEcode ReceivePcapFileLoop(ThreadVars *, void *, void *); |
59 | | static TmEcode ReceivePcapFileThreadInit(ThreadVars *, const void *, void **); |
60 | | static void ReceivePcapFileThreadExitStats(ThreadVars *, void *); |
61 | | static TmEcode ReceivePcapFileThreadDeinit(ThreadVars *, void *); |
62 | | |
63 | | static TmEcode DecodePcapFile(ThreadVars *, Packet *, void *); |
64 | | static TmEcode DecodePcapFileThreadInit(ThreadVars *, const void *, void **); |
65 | | static TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data); |
66 | | |
67 | | static void CleanupPcapDirectoryFromThreadVars(PcapFileThreadVars *tv, |
68 | | PcapFileDirectoryVars *ptv); |
69 | | static void CleanupPcapFileFromThreadVars(PcapFileThreadVars *tv, PcapFileFileVars *pfv); |
70 | | static void CleanupPcapFileThreadVars(PcapFileThreadVars *tv); |
71 | | static TmEcode PcapFileExit(TmEcode status, struct timespec *last_processed); |
72 | | |
73 | | void CleanupPcapFileFromThreadVars(PcapFileThreadVars *tv, PcapFileFileVars *pfv) |
74 | 18.4k | { |
75 | 18.4k | CleanupPcapFileFileVars(pfv); |
76 | 18.4k | if (tv->is_directory == 0) { |
77 | 18.4k | tv->behavior.file = NULL; |
78 | 18.4k | } |
79 | 18.4k | } |
80 | | |
81 | | void CleanupPcapDirectoryFromThreadVars(PcapFileThreadVars *tv, PcapFileDirectoryVars *ptv) |
82 | 0 | { |
83 | 0 | CleanupPcapFileDirectoryVars(ptv); |
84 | 0 | if (tv->is_directory == 1) { |
85 | 0 | tv->behavior.directory = NULL; |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | void CleanupPcapFileThreadVars(PcapFileThreadVars *ptv) |
90 | 22.5k | { |
91 | 22.5k | if (ptv != NULL) { |
92 | 22.5k | if (ptv->is_directory == 0) { |
93 | 22.5k | if (ptv->behavior.file != NULL) { |
94 | 0 | CleanupPcapFileFromThreadVars(ptv, ptv->behavior.file); |
95 | 0 | } |
96 | 22.5k | ptv->behavior.file = NULL; |
97 | 22.5k | } else { |
98 | 0 | if (ptv->behavior.directory != NULL) { |
99 | 0 | CleanupPcapDirectoryFromThreadVars(ptv, ptv->behavior.directory); |
100 | 0 | } |
101 | 0 | ptv->behavior.directory = NULL; |
102 | 0 | } |
103 | 22.5k | if (ptv->shared.bpf_string != NULL) { |
104 | 0 | SCFree(ptv->shared.bpf_string); |
105 | 0 | ptv->shared.bpf_string = NULL; |
106 | 0 | } |
107 | 22.5k | SCFree(ptv); |
108 | 22.5k | } |
109 | 22.5k | } |
110 | | |
111 | | /** |
112 | | * Pcap File Functionality |
113 | | */ |
114 | | void TmModuleReceivePcapFileRegister (void) |
115 | 71 | { |
116 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].name = "ReceivePcapFile"; |
117 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadInit = ReceivePcapFileThreadInit; |
118 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].Func = NULL; |
119 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqLoop = ReceivePcapFileLoop; |
120 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].PktAcqBreakLoop = NULL; |
121 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadExitPrintStats = ReceivePcapFileThreadExitStats; |
122 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].ThreadDeinit = ReceivePcapFileThreadDeinit; |
123 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].cap_flags = 0; |
124 | 71 | tmm_modules[TMM_RECEIVEPCAPFILE].flags = TM_FLAG_RECEIVE_TM; |
125 | 71 | } |
126 | | |
127 | | void TmModuleDecodePcapFileRegister (void) |
128 | 71 | { |
129 | 71 | tmm_modules[TMM_DECODEPCAPFILE].name = "DecodePcapFile"; |
130 | 71 | tmm_modules[TMM_DECODEPCAPFILE].ThreadInit = DecodePcapFileThreadInit; |
131 | 71 | tmm_modules[TMM_DECODEPCAPFILE].Func = DecodePcapFile; |
132 | 71 | tmm_modules[TMM_DECODEPCAPFILE].ThreadExitPrintStats = NULL; |
133 | 71 | tmm_modules[TMM_DECODEPCAPFILE].ThreadDeinit = DecodePcapFileThreadDeinit; |
134 | 71 | tmm_modules[TMM_DECODEPCAPFILE].cap_flags = 0; |
135 | 71 | tmm_modules[TMM_DECODEPCAPFILE].flags = TM_FLAG_DECODE_TM; |
136 | 71 | } |
137 | | |
138 | | void PcapFileGlobalInit(void) |
139 | 1 | { |
140 | 1 | memset(&pcap_g, 0x00, sizeof(pcap_g)); |
141 | 1 | SC_ATOMIC_INIT(pcap_g.invalid_checksums); |
142 | 1 | } |
143 | | |
144 | | TmEcode PcapFileExit(TmEcode status, struct timespec *last_processed) |
145 | 18.4k | { |
146 | 18.4k | if(RunModeUnixSocketIsActive()) { |
147 | 0 | status = UnixSocketPcapFile(status, last_processed); |
148 | 0 | SCReturnInt(status); |
149 | 18.4k | } else { |
150 | 18.4k | EngineStop(); |
151 | 18.4k | SCReturnInt(status); |
152 | 18.4k | } |
153 | 18.4k | } |
154 | | |
155 | | TmEcode ReceivePcapFileLoop(ThreadVars *tv, void *data, void *slot) |
156 | 18.4k | { |
157 | 18.4k | SCEnter(); |
158 | | |
159 | 18.4k | if(unlikely(data == NULL)) { |
160 | 0 | SCLogError("pcap file reader thread failed to initialize"); |
161 | |
|
162 | 0 | PcapFileExit(TM_ECODE_FAILED, NULL); |
163 | |
|
164 | 0 | SCReturnInt(TM_ECODE_DONE); |
165 | 0 | } |
166 | | |
167 | 18.4k | TmEcode status = TM_ECODE_OK; |
168 | 18.4k | PcapFileThreadVars *ptv = (PcapFileThreadVars *) data; |
169 | 18.4k | TmSlot *s = (TmSlot *)slot; |
170 | | |
171 | 18.4k | ptv->shared.slot = s->slot_next; |
172 | 18.4k | ptv->shared.cb_result = TM_ECODE_OK; |
173 | | |
174 | | // Indicate that the thread is actually running its application level code (i.e., it can poll |
175 | | // packets) |
176 | 18.4k | TmThreadsSetFlag(tv, THV_RUNNING); |
177 | | |
178 | 18.4k | if(ptv->is_directory == 0) { |
179 | 18.4k | SCLogInfo("Starting file run for %s", ptv->behavior.file->filename); |
180 | 18.4k | status = PcapFileDispatch(ptv->behavior.file); |
181 | 18.4k | CleanupPcapFileFromThreadVars(ptv, ptv->behavior.file); |
182 | 18.4k | } else { |
183 | 0 | SCLogInfo("Starting directory run for %s", ptv->behavior.directory->filename); |
184 | 0 | PcapDirectoryDispatch(ptv->behavior.directory); |
185 | 0 | CleanupPcapDirectoryFromThreadVars(ptv, ptv->behavior.directory); |
186 | 0 | } |
187 | | |
188 | 18.4k | SCLogDebug("Pcap file loop complete with status %u", status); |
189 | | |
190 | 18.4k | status = PcapFileExit(status, &ptv->shared.last_processed); |
191 | 18.4k | SCReturnInt(status); |
192 | 18.4k | } |
193 | | |
194 | | TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **data) |
195 | 11.4k | { |
196 | 11.4k | SCEnter(); |
197 | | |
198 | 11.4k | TmEcode status = TM_ECODE_OK; |
199 | 11.4k | const char *tmpstring = NULL; |
200 | 11.4k | const char *tmp_bpf_string = NULL; |
201 | | |
202 | 11.4k | if (initdata == NULL) { |
203 | 0 | SCLogError("error: initdata == NULL"); |
204 | |
|
205 | 0 | SCReturnInt(TM_ECODE_OK); |
206 | 0 | } |
207 | | |
208 | 11.4k | PcapFileThreadVars *ptv = SCMalloc(sizeof(PcapFileThreadVars)); |
209 | 11.4k | if (unlikely(ptv == NULL)) { |
210 | 0 | SCReturnInt(TM_ECODE_OK); |
211 | 0 | } |
212 | 11.4k | memset(ptv, 0, sizeof(PcapFileThreadVars)); |
213 | 11.4k | memset(&ptv->shared.last_processed, 0, sizeof(struct timespec)); |
214 | | |
215 | 11.4k | intmax_t tenant = 0; |
216 | 11.4k | if (ConfGetInt("pcap-file.tenant-id", &tenant) == 1) { |
217 | 0 | if (tenant > 0 && tenant < UINT_MAX) { |
218 | 0 | ptv->shared.tenant_id = (uint32_t)tenant; |
219 | 0 | SCLogInfo("tenant %u", ptv->shared.tenant_id); |
220 | 0 | } else { |
221 | 0 | SCLogError("tenant out of range"); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | 11.4k | if (ConfGet("bpf-filter", &(tmp_bpf_string)) != 1) { |
226 | 11.4k | SCLogDebug("could not get bpf or none specified"); |
227 | 11.4k | } else { |
228 | 0 | ptv->shared.bpf_string = SCStrdup(tmp_bpf_string); |
229 | 0 | if (unlikely(ptv->shared.bpf_string == NULL)) { |
230 | 0 | SCLogError("Failed to allocate bpf_string"); |
231 | |
|
232 | 0 | CleanupPcapFileThreadVars(ptv); |
233 | |
|
234 | 0 | SCReturnInt(TM_ECODE_OK); |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | 11.4k | int should_delete = 0; |
239 | 11.4k | ptv->shared.should_delete = false; |
240 | 11.4k | if (ConfGetBool("pcap-file.delete-when-done", &should_delete) == 1) { |
241 | 0 | ptv->shared.should_delete = should_delete == 1; |
242 | 0 | } |
243 | | |
244 | 11.4k | DIR *directory = NULL; |
245 | 11.4k | SCLogDebug("checking file or directory %s", (char*)initdata); |
246 | 11.4k | if(PcapDetermineDirectoryOrFile((char *)initdata, &directory) == TM_ECODE_FAILED) { |
247 | 47 | CleanupPcapFileThreadVars(ptv); |
248 | 47 | SCReturnInt(TM_ECODE_OK); |
249 | 47 | } |
250 | | |
251 | 11.3k | if(directory == NULL) { |
252 | 11.3k | SCLogDebug("argument %s was a file", (char *)initdata); |
253 | 11.3k | PcapFileFileVars *pv = SCMalloc(sizeof(PcapFileFileVars)); |
254 | 11.3k | if (unlikely(pv == NULL)) { |
255 | 0 | SCLogError("Failed to allocate file vars"); |
256 | 0 | CleanupPcapFileThreadVars(ptv); |
257 | 0 | SCReturnInt(TM_ECODE_OK); |
258 | 0 | } |
259 | 11.3k | memset(pv, 0, sizeof(PcapFileFileVars)); |
260 | | |
261 | 11.3k | pv->filename = SCStrdup((char *)initdata); |
262 | 11.3k | if (unlikely(pv->filename == NULL)) { |
263 | 0 | SCLogError("Failed to allocate filename"); |
264 | 0 | CleanupPcapFileFileVars(pv); |
265 | 0 | CleanupPcapFileThreadVars(ptv); |
266 | 0 | SCReturnInt(TM_ECODE_OK); |
267 | 0 | } |
268 | | |
269 | 11.3k | pv->shared = &ptv->shared; |
270 | 11.3k | status = InitPcapFile(pv); |
271 | 11.3k | if(status == TM_ECODE_OK) { |
272 | 9.37k | ptv->is_directory = 0; |
273 | 9.37k | ptv->behavior.file = pv; |
274 | 9.37k | } else { |
275 | 2.01k | SCLogWarning("Failed to init pcap file %s, skipping", pv->filename); |
276 | 2.01k | CleanupPcapFileFileVars(pv); |
277 | 2.01k | CleanupPcapFileThreadVars(ptv); |
278 | 2.01k | SCReturnInt(TM_ECODE_OK); |
279 | 2.01k | } |
280 | 11.3k | } else { |
281 | 0 | SCLogInfo("Argument %s was a directory", (char *)initdata); |
282 | 0 | PcapFileDirectoryVars *pv = SCMalloc(sizeof(PcapFileDirectoryVars)); |
283 | 0 | if (unlikely(pv == NULL)) { |
284 | 0 | SCLogError("Failed to allocate directory vars"); |
285 | 0 | closedir(directory); |
286 | 0 | CleanupPcapFileThreadVars(ptv); |
287 | 0 | SCReturnInt(TM_ECODE_OK); |
288 | 0 | } |
289 | 0 | memset(pv, 0, sizeof(PcapFileDirectoryVars)); |
290 | |
|
291 | 0 | pv->filename = SCStrdup((char*)initdata); |
292 | 0 | if (unlikely(pv->filename == NULL)) { |
293 | 0 | SCLogError("Failed to allocate filename"); |
294 | 0 | CleanupPcapFileDirectoryVars(pv); |
295 | 0 | CleanupPcapFileThreadVars(ptv); |
296 | 0 | SCReturnInt(TM_ECODE_OK); |
297 | 0 | } |
298 | 0 | pv->cur_dir_depth = 0; |
299 | |
|
300 | 0 | int should_recurse; |
301 | 0 | pv->should_recurse = false; |
302 | 0 | if (ConfGetBool("pcap-file.recursive", &should_recurse) == 1) { |
303 | 0 | pv->should_recurse = (should_recurse == 1); |
304 | 0 | } |
305 | |
|
306 | 0 | int should_loop = 0; |
307 | 0 | pv->should_loop = false; |
308 | 0 | if (ConfGetBool("pcap-file.continuous", &should_loop) == 1) { |
309 | 0 | pv->should_loop = (should_loop == 1); |
310 | 0 | } |
311 | |
|
312 | 0 | if (pv->should_recurse == true && pv->should_loop == true) { |
313 | 0 | SCLogError("Error, --pcap-file-continuous and --pcap-file-recursive " |
314 | 0 | "cannot be used together."); |
315 | 0 | CleanupPcapFileDirectoryVars(pv); |
316 | 0 | CleanupPcapFileThreadVars(ptv); |
317 | 0 | SCReturnInt(TM_ECODE_FAILED); |
318 | 0 | } |
319 | | |
320 | 0 | pv->delay = 30; |
321 | 0 | intmax_t delay = 0; |
322 | 0 | if (ConfGetInt("pcap-file.delay", &delay) == 1) { |
323 | 0 | if (delay > 0 && delay < UINT_MAX) { |
324 | 0 | pv->delay = (time_t)delay; |
325 | 0 | SCLogDebug("delay %lu", pv->delay); |
326 | 0 | } else { |
327 | 0 | SCLogError("delay out of range"); |
328 | 0 | } |
329 | 0 | } |
330 | |
|
331 | 0 | pv->poll_interval = 5; |
332 | 0 | intmax_t poll_interval = 0; |
333 | 0 | if (ConfGetInt("pcap-file.poll-interval", &poll_interval) == 1) { |
334 | 0 | if (poll_interval > 0 && poll_interval < UINT_MAX) { |
335 | 0 | pv->poll_interval = (time_t)poll_interval; |
336 | 0 | SCLogDebug("poll-interval %lu", pv->delay); |
337 | 0 | } else { |
338 | 0 | SCLogError("poll-interval out of range"); |
339 | 0 | } |
340 | 0 | } |
341 | |
|
342 | 0 | pv->shared = &ptv->shared; |
343 | 0 | pv->directory = directory; |
344 | 0 | TAILQ_INIT(&pv->directory_content); |
345 | |
|
346 | 0 | ptv->is_directory = 1; |
347 | 0 | ptv->behavior.directory = pv; |
348 | 0 | } |
349 | | |
350 | 9.37k | if (ConfGet("pcap-file.checksum-checks", &tmpstring) != 1) { |
351 | 0 | pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO; |
352 | 9.37k | } else { |
353 | 9.37k | if (strcmp(tmpstring, "auto") == 0) { |
354 | 0 | pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO; |
355 | 9.37k | } else if (ConfValIsTrue(tmpstring)){ |
356 | 0 | pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_ENABLE; |
357 | 9.37k | } else if (ConfValIsFalse(tmpstring)) { |
358 | 9.37k | pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_DISABLE; |
359 | 9.37k | } |
360 | 9.37k | } |
361 | 9.37k | pcap_g.checksum_mode = pcap_g.conf_checksum_mode; |
362 | | |
363 | 9.37k | ptv->shared.tv = tv; |
364 | 9.37k | *data = (void *)ptv; |
365 | | |
366 | 9.37k | SCReturnInt(TM_ECODE_OK); |
367 | 11.3k | } |
368 | | |
369 | | void ReceivePcapFileThreadExitStats(ThreadVars *tv, void *data) |
370 | 0 | { |
371 | 0 | SCEnter(); |
372 | 0 | if(data != NULL) { |
373 | 0 | PcapFileThreadVars *ptv = (PcapFileThreadVars *)data; |
374 | |
|
375 | 0 | if (pcap_g.conf_checksum_mode == CHECKSUM_VALIDATION_AUTO && |
376 | 0 | pcap_g.cnt < CHECKSUM_SAMPLE_COUNT && |
377 | 0 | SC_ATOMIC_GET(pcap_g.invalid_checksums)) { |
378 | 0 | uint64_t chrate = pcap_g.cnt / SC_ATOMIC_GET(pcap_g.invalid_checksums); |
379 | 0 | if (chrate < CHECKSUM_INVALID_RATIO) |
380 | 0 | SCLogWarning("1/%" PRIu64 "th of packets have an invalid checksum," |
381 | 0 | " consider setting pcap-file.checksum-checks variable to no" |
382 | 0 | " or use '-k none' option on command line.", |
383 | 0 | chrate); |
384 | 0 | else |
385 | 0 | SCLogInfo("1/%" PRIu64 "th of packets have an invalid checksum", |
386 | 0 | chrate); |
387 | 0 | } |
388 | 0 | SCLogNotice("read %" PRIu64 " file%s, %" PRIu64 " packets, %" PRIu64 " bytes", |
389 | 0 | ptv->shared.files, ptv->shared.files == 1 ? "" : "s", ptv->shared.pkts, |
390 | 0 | ptv->shared.bytes); |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | TmEcode ReceivePcapFileThreadDeinit(ThreadVars *tv, void *data) |
395 | 18.4k | { |
396 | 18.4k | SCEnter(); |
397 | 18.4k | if(data != NULL) { |
398 | 18.4k | PcapFileThreadVars *ptv = (PcapFileThreadVars *) data; |
399 | 18.4k | CleanupPcapFileThreadVars(ptv); |
400 | 18.4k | } |
401 | 18.4k | SCReturnInt(TM_ECODE_OK); |
402 | 18.4k | } |
403 | | |
404 | | static TmEcode DecodePcapFile(ThreadVars *tv, Packet *p, void *data) |
405 | 21.2M | { |
406 | 21.2M | SCEnter(); |
407 | 21.2M | DecodeThreadVars *dtv = (DecodeThreadVars *)data; |
408 | | |
409 | 21.2M | BUG_ON(PKT_IS_PSEUDOPKT(p)); |
410 | | |
411 | | /* update counters */ |
412 | 21.2M | DecodeUpdatePacketCounters(tv, dtv, p); |
413 | | |
414 | 21.2M | DecoderFunc decoder; |
415 | 21.2M | if(ValidateLinkType(p->datalink, &decoder) == TM_ECODE_OK) { |
416 | | |
417 | | /* call the decoder */ |
418 | 21.2M | decoder(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p)); |
419 | | |
420 | | #ifdef DEBUG |
421 | | BUG_ON(p->pkt_src != PKT_SRC_WIRE && p->pkt_src != PKT_SRC_FFR); |
422 | | #endif |
423 | | |
424 | 21.2M | PacketDecodeFinalize(tv, dtv, p); |
425 | | |
426 | 21.2M | SCReturnInt(TM_ECODE_OK); |
427 | 21.2M | } else { |
428 | 135 | SCReturnInt(TM_ECODE_FAILED); |
429 | 135 | } |
430 | 21.2M | } |
431 | | |
432 | | TmEcode DecodePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **data) |
433 | 2 | { |
434 | 2 | SCEnter(); |
435 | 2 | DecodeThreadVars *dtv = NULL; |
436 | 2 | dtv = DecodeThreadVarsAlloc(tv); |
437 | | |
438 | 2 | if (dtv == NULL) |
439 | 0 | SCReturnInt(TM_ECODE_FAILED); |
440 | | |
441 | 2 | DecodeRegisterPerfCounters(dtv, tv); |
442 | | |
443 | 2 | *data = (void *)dtv; |
444 | | |
445 | 2 | SCReturnInt(TM_ECODE_OK); |
446 | 2 | } |
447 | | |
448 | | TmEcode DecodePcapFileThreadDeinit(ThreadVars *tv, void *data) |
449 | 0 | { |
450 | 0 | if (data != NULL) |
451 | 0 | DecodeThreadVarsFree(tv, data); |
452 | 0 | SCReturnInt(TM_ECODE_OK); |
453 | 0 | } |
454 | | |
455 | | void PcapIncreaseInvalidChecksum(void) |
456 | 0 | { |
457 | | (void) SC_ATOMIC_ADD(pcap_g.invalid_checksums, 1); |
458 | 0 | } |
459 | | |
460 | | /* eof */ |