suricata
source-pcap-file-directory-helper.c
Go to the documentation of this file.
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 Danny Browning <danny.browning@protectwise.com>
22 *
23 * Helper methods for directory based packet acquisition
24 */
25
27#include "suricata.h"
28#include "runmode-unix-socket.h"
29#include "util-mem.h"
30#include "util-time.h"
31#include "util-path.h"
32#include "source-pcap-file.h"
33
34static void GetTime(struct timespec *tm);
35static void CopyTime(struct timespec *from, struct timespec *to);
36static int CompareTimes(struct timespec *left, struct timespec *right);
37static TmEcode PcapRunStatus(PcapFileDirectoryVars *);
38static TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv);
39static TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv);
40static int PcapDirectoryGetModifiedTime(char const * file, struct timespec * out);
41static TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
42 PendingFile *file_to_add);
43static TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *ptv,
44 struct timespec * older_than);
45static TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
46 struct timespec *older_than);
47
49
50void GetTime(struct timespec *tm)
51{
52 struct timeval now;
53 if(gettimeofday(&now, NULL) == 0) {
54 tm->tv_sec = now.tv_sec;
55 tm->tv_nsec = now.tv_usec * 1000L;
56 }
57}
58
59void CopyTime(struct timespec *from, struct timespec *to)
60{
61 to->tv_sec = from->tv_sec;
62 to->tv_nsec = from->tv_nsec;
63}
64
65int CompareTimes(struct timespec *left, struct timespec *right)
66{
67 if (left->tv_sec < right->tv_sec) {
68 return -1;
69 } else if (left->tv_sec > right->tv_sec) {
70 return 1;
71 } else {
72 if (left->tv_nsec < right->tv_nsec) {
73 return -1;
74 } else if (left->tv_nsec > right->tv_nsec) {
75 return 1;
76 } else {
77 return 0;
78 }
79 }
80}
81
82/**
83 * Pcap Folder Utilities
84 */
85TmEcode PcapRunStatus(PcapFileDirectoryVars *ptv)
86{
89 if ( (suricata_ctl_flags & SURICATA_STOP) || done != TM_ECODE_OK) {
91 }
92 } else {
95 }
96 }
98}
99
101 if (pending != NULL) {
102 if (pending->filename != NULL) {
103 SCFree(pending->filename);
104 }
105 SCFree(pending);
106 }
107}
108
110{
111 if (ptv != NULL) {
112 if (ptv->current_file != NULL) {
114 ptv->current_file = NULL;
115 }
116 if (ptv->directory != NULL) {
117 closedir(ptv->directory);
118 ptv->directory = NULL;
119 }
120 if (ptv->filename != NULL) {
121 SCFree(ptv->filename);
122 }
123 ptv->shared = NULL;
124 PendingFile *current_file = NULL;
125 while (!TAILQ_EMPTY(&ptv->directory_content)) {
126 current_file = TAILQ_FIRST(&ptv->directory_content);
127 TAILQ_REMOVE(&ptv->directory_content, current_file, next);
128 CleanupPendingFile(current_file);
129 }
130 SCFree(ptv);
131 }
132}
133
134TmEcode PcapDirectoryFailure(PcapFileDirectoryVars *ptv)
135{
136 TmEcode status = TM_ECODE_FAILED;
137
138 if (unlikely(ptv == NULL)) {
139 SCLogError("Directory vars was null");
141 }
142 if (unlikely(ptv->shared == NULL)) {
143 SCLogError("Directory shared vars was null");
145 }
146
148 status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
149 }
150
151 SCReturnInt(status);
152}
153
154TmEcode PcapDirectoryDone(PcapFileDirectoryVars *ptv)
155{
156 TmEcode status = TM_ECODE_DONE;
157
158 if (unlikely(ptv == NULL)) {
159 SCLogError("Directory vars was null");
161 }
162 if (unlikely(ptv->shared == NULL)) {
163 SCLogError("Directory shared vars was null");
165 }
166
168 status = UnixSocketPcapFile(status, &ptv->shared->last_processed);
169 }
170
171 SCReturnInt(status);
172}
173
174TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory)
175{
176 DIR *temp_dir = NULL;
177 TmEcode return_code = TM_ECODE_FAILED;
178
179 temp_dir = opendir(filename);
180
181 if (temp_dir == NULL) {//if null, our filename may just be a normal file
182 switch (errno) {
183 case EACCES:
184 SCLogError("%s: Permission denied", filename);
185 break;
186
187 case EBADF:
188 SCLogError("%s: Not a valid file descriptor opened for reading", filename);
189 break;
190
191 case EMFILE:
192 SCLogError("%s: Per process open file descriptor limit reached", filename);
193 break;
194
195 case ENFILE:
196 SCLogError("%s: System wide open file descriptor limit reached", filename);
197 break;
198
199 case ENOENT:
200 SCLogError("%s: Does not exist, or name is an empty string", filename);
201 break;
202 case ENOMEM:
203 SCLogError("%s: Insufficient memory to complete the operation", filename);
204 break;
205
206 case ENOTDIR: //no error checking the directory, just is a plain file
207 SCLogDebug("%s: plain file, not a directory", filename);
208 return_code = TM_ECODE_OK;
209 break;
210
211 default:
212 SCLogError("%s: %" PRId32, filename, errno);
213 }
214 } else {
215 //no error, filename references a directory
216 *directory = temp_dir;
217 return_code = TM_ECODE_OK;
218 }
219
220 return return_code;
221}
222
223int PcapDirectoryGetModifiedTime(char const *file, struct timespec *out)
224{
225 SCStat buf;
226 int ret;
227
228 if (file == NULL)
229 return -1;
230
231 if ((ret = SCStatFn(file, &buf)) != 0)
232 return ret;
233
234#ifdef OS_DARWIN
235 out->tv_sec = buf.st_mtimespec.tv_sec;
236 out->tv_nsec = buf.st_mtimespec.tv_nsec;
237#elif OS_WIN32
238 out->tv_sec = buf.st_mtime;
239#else
240 out->tv_sec = buf.st_mtim.tv_sec;
241 out->tv_nsec = buf.st_mtim.tv_nsec;
242#endif
243
244 return ret;
245}
246
247TmEcode PcapDirectoryInsertFile(PcapFileDirectoryVars *pv,
248 PendingFile *file_to_add
249) {
250 PendingFile *file_to_compare = NULL;
251 PendingFile *next_file_to_compare = NULL;
252
253 if (unlikely(pv == NULL)) {
254 SCLogError("No directory vars passed");
256 }
257
258 if (unlikely(file_to_add == NULL)) {
259 SCLogError("File passed was null");
261 }
262
263 if (unlikely(file_to_add->filename == NULL)) {
264 SCLogError("File was passed with null filename");
266 }
267
268 SCLogDebug("Inserting %s into directory buffer", file_to_add->filename);
269
270 if (TAILQ_EMPTY(&pv->directory_content)) {
271 TAILQ_INSERT_TAIL(&pv->directory_content, file_to_add, next);
272 } else {
273 file_to_compare = TAILQ_FIRST(&pv->directory_content);
274 while(file_to_compare != NULL) {
275 if (CompareTimes(&file_to_add->modified_time, &file_to_compare->modified_time) < 0) {
276 TAILQ_INSERT_BEFORE(file_to_compare, file_to_add, next);
277 file_to_compare = NULL;
278 } else {
279 next_file_to_compare = TAILQ_NEXT(file_to_compare, next);
280 if (next_file_to_compare == NULL) {
281 TAILQ_INSERT_AFTER(&pv->directory_content, file_to_compare,
282 file_to_add, next);
283 }
284 file_to_compare = next_file_to_compare;
285 }
286 }
287 }
288
290}
291
292TmEcode PcapDirectoryPopulateBuffer(PcapFileDirectoryVars *pv,
293 struct timespec *older_than
294) {
295 if (unlikely(pv == NULL)) {
296 SCLogError("No directory vars passed");
298 }
299 if (unlikely(pv->filename == NULL)) {
300 SCLogError("No directory filename was passed");
302 }
303 struct dirent * dir = NULL;
304 PendingFile *file_to_add = NULL;
305
306 while ((dir = readdir(pv->directory)) != NULL) {
307#ifndef OS_WIN32
308 if (dir->d_type != DT_REG) {
309 continue;
310 }
311#endif
312 if (strcmp(dir->d_name, ".") == 0 ||
313 strcmp(dir->d_name, "..") == 0) {
314 continue;
315 }
316
317 char pathbuff[PATH_MAX] = {0};
318
319 int written = 0;
320
321 written = snprintf(pathbuff, PATH_MAX, "%s/%s", pv->filename, dir->d_name);
322
323 if (written <= 0 || written >= PATH_MAX) {
324 SCLogError("Could not write path");
325
327 } else {
328 struct timespec temp_time;
329 memset(&temp_time, 0, sizeof(struct timespec));
330
331 if (PcapDirectoryGetModifiedTime(pathbuff, &temp_time) == 0) {
332 SCLogDebug("%" PRIuMAX " < %" PRIuMAX "(%s) < %" PRIuMAX ")",
334 (uintmax_t)SCTimespecAsEpochMillis(&temp_time),
335 pathbuff,
336 (uintmax_t)SCTimespecAsEpochMillis(older_than));
337
338 // Skip files outside of our time range
339 if (CompareTimes(&temp_time, &pv->shared->last_processed) <= 0) {
340 SCLogDebug("Skipping old file %s", pathbuff);
341 continue;
342 }
343 else if (CompareTimes(&temp_time, older_than) >= 0) {
344 SCLogDebug("Skipping new file %s", pathbuff);
345 continue;
346 }
347 } else {
348 SCLogDebug("Unable to get modified time on %s, skipping", pathbuff);
349 continue;
350 }
351
352 file_to_add = SCCalloc(1, sizeof(PendingFile));
353 if (unlikely(file_to_add == NULL)) {
354 SCLogError("Failed to allocate pending file");
355
357 }
358
359 file_to_add->filename = SCStrdup(pathbuff);
360 if (unlikely(file_to_add->filename == NULL)) {
361 SCLogError("Failed to copy filename");
362 CleanupPendingFile(file_to_add);
363
365 }
366
367 memset(&file_to_add->modified_time, 0, sizeof(struct timespec));
368 CopyTime(&temp_time, &file_to_add->modified_time);
369
370 SCLogInfo("Found \"%s\" at %" PRIuMAX, file_to_add->filename,
371 (uintmax_t)SCTimespecAsEpochMillis(&file_to_add->modified_time));
372
373 if (PcapDirectoryInsertFile(pv, file_to_add) == TM_ECODE_FAILED) {
374 SCLogError("Failed to add file");
375 CleanupPendingFile(file_to_add);
376
378 }
379 }
380 }
381
383}
384
385
386TmEcode PcapDirectoryDispatchForTimeRange(PcapFileDirectoryVars *pv,
387 struct timespec *older_than)
388{
389 if (PcapDirectoryPopulateBuffer(pv, older_than) == TM_ECODE_FAILED) {
390 SCLogError("Failed to populate directory buffer");
392 }
393
394 TmEcode status = TM_ECODE_OK;
395
396 if (TAILQ_EMPTY(&pv->directory_content)) {
397 SCLogDebug("Directory %s has no files to process", pv->filename);
398 GetTime(older_than);
399 older_than->tv_sec = older_than->tv_sec - pv->delay;
400 rewinddir(pv->directory);
401 status = TM_ECODE_OK;
402 } else {
403 PendingFile *current_file = NULL;
404
405 struct timespec last_time_seen;
406 memset(&last_time_seen, 0, sizeof(struct timespec));
407
408 while (status == TM_ECODE_OK && !TAILQ_EMPTY(&pv->directory_content)) {
409 current_file = TAILQ_FIRST(&pv->directory_content);
410 TAILQ_REMOVE(&pv->directory_content, current_file, next);
411
412 if (unlikely(current_file == NULL)) {
413 SCLogWarning("Current file was null");
414 } else if (unlikely(current_file->filename == NULL)) {
415 SCLogWarning("Current file filename was null");
416 } else {
417 SCLogDebug("Processing file %s", current_file->filename);
418
419 const size_t toalloc = sizeof(PcapFileFileVars) + pcap_g.read_buffer_size;
420 PcapFileFileVars *pftv = SCCalloc(1, toalloc);
421 if (unlikely(pftv == NULL)) {
422 SCLogError("Failed to allocate PcapFileFileVars");
424 }
425
426 pftv->filename = SCStrdup(current_file->filename);
427 if (unlikely(pftv->filename == NULL)) {
428 SCLogError("Failed to allocate filename");
431 }
432 pftv->shared = pv->shared;
433
434 if (InitPcapFile(pftv) == TM_ECODE_FAILED) {
435 SCLogWarning("Failed to init pcap file %s, skipping", current_file->filename);
436 CleanupPendingFile(current_file);
438 status = TM_ECODE_OK;
439 } else {
440 pv->current_file = pftv;
441
442 status = PcapFileDispatch(pftv);
443
445
446 if (status == TM_ECODE_FAILED) {
447 CleanupPendingFile(current_file);
448 SCReturnInt(status);
449 }
450
451 SCLogInfo("Processed file %s, processed up to %" PRIuMAX,
452 current_file->filename,
453 (uintmax_t)SCTimespecAsEpochMillis(&current_file->modified_time));
454
455 if(CompareTimes(&current_file->modified_time, &last_time_seen) > 0) {
456 CopyTime(&current_file->modified_time, &last_time_seen);
457 }
458
459 CleanupPendingFile(current_file);
460 pv->current_file = NULL;
461
462 status = PcapRunStatus(pv);
463 }
464 }
465 }
466
467 if(CompareTimes(&last_time_seen, &pv->shared->last_processed) > 0) {
468 SCLogInfo("Updating processed to %" PRIuMAX,
469 (uintmax_t)SCTimespecAsEpochMillis(&last_time_seen));
470 CopyTime(&last_time_seen, &pv->shared->last_processed);
471 status = PcapRunStatus(pv);
472 }
473 }
474 GetTime(older_than);
475 older_than->tv_sec = older_than->tv_sec - pv->delay;
476
477 SCReturnInt(status);
478}
479
481{
482 SCEnter();
483
484 DIR *directory_check = NULL;
485
486 struct timespec older_than;
487 memset(&older_than, 0, sizeof(struct timespec));
488 older_than.tv_sec = LONG_MAX;
489 uint32_t poll_seconds;
490#ifndef OS_WIN32
491 struct tm safe_tm;
492 memset(&safe_tm, 0, sizeof(safe_tm));
493 poll_seconds = (uint32_t)localtime_r(&ptv->poll_interval, &safe_tm)->tm_sec;
494#else
495 /* windows localtime is threadsafe */
496 poll_seconds = (uint32_t)localtime(&ptv->poll_interval)->tm_sec;
497#endif
498
499 if (ptv->should_loop) {
500 GetTime(&older_than);
501 older_than.tv_sec = older_than.tv_sec - ptv->delay;
502 }
503 TmEcode status = TM_ECODE_OK;
504
505 while (status == TM_ECODE_OK) {
506 //loop while directory is ok
507 SCLogInfo("Processing pcaps directory %s, files must be newer than %" PRIuMAX " and older than %" PRIuMAX,
508 ptv->filename, (uintmax_t)SCTimespecAsEpochMillis(&ptv->shared->last_processed),
509 (uintmax_t)SCTimespecAsEpochMillis(&older_than));
510 status = PcapDirectoryDispatchForTimeRange(ptv, &older_than);
511 if (ptv->should_loop && status == TM_ECODE_OK) {
512 sleep(poll_seconds);
513 //update our status based on suricata control flags or unix command socket
514 status = PcapRunStatus(ptv);
515 if (status == TM_ECODE_OK) {
516 SCLogDebug("Checking if directory %s still exists", ptv->filename);
517 //check directory
519 &directory_check) == TM_ECODE_FAILED) {
520 SCLogInfo("Directory %s no longer exists, stopping",
521 ptv->filename);
522 status = TM_ECODE_DONE;
523 } else if(directory_check != NULL) {
524 closedir(directory_check);
525 directory_check = NULL;
526 }
527 }
528 } else if (status == TM_ECODE_OK) { //not looping, mark done
529 SCLogDebug("Not looping, stopping directory mode");
530 status = TM_ECODE_DONE;
531 }
532 }
533
535
536 if (status == TM_ECODE_FAILED) {
537 SCLogError("Directory %s run mode failed", ptv->filename);
538 status = PcapDirectoryFailure(ptv);
539 } else {
540 SCLogInfo("Directory run mode complete");
541 status = PcapDirectoryDone(ptv);
542 }
543
544 SCReturnInt(status);
545}
546
547/* eof */
struct HtpBodyChunk_ * next
void StatsSyncCountersIfSignalled(ThreadVars *tv)
Definition counters.c:450
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:294
#define TAILQ_FIRST(head)
Definition queue.h:250
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:312
#define TAILQ_NEXT(elm, field)
Definition queue.h:307
#define TAILQ_EMPTY(head)
Definition queue.h:248
#define TAILQ_INSERT_BEFORE(listelm, elm, field)
Definition queue.h:277
#define TAILQ_INSERT_AFTER(head, listelm, elm, field)
Definition queue.h:267
TmEcode UnixSocketPcapFile(TmEcode tm, struct timespec *last_processed)
int RunModeUnixSocketIsActive(void)
TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory)
void CleanupPendingFile(PendingFile *pending)
PcapFileGlobalVars pcap_g
void CleanupPcapFileDirectoryVars(PcapFileDirectoryVars *ptv)
TmEcode PcapDirectoryDispatch(PcapFileDirectoryVars *ptv)
TmEcode InitPcapFile(PcapFileFileVars *pfv)
TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
Main PCAP file reading Loop function.
void CleanupPcapFileFileVars(PcapFileFileVars *pfv)
struct PcapFileFileVars_ PcapFileFileVars
PcapFileSharedVars * shared
volatile uint8_t suricata_ctl_flags
Definition suricata.c:172
#define SURICATA_STOP
Definition suricata.h:94
@ TM_ECODE_FAILED
@ TM_ECODE_OK
@ TM_ECODE_DONE
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define SCStrdup(s)
Definition util-mem.h:56
#define unlikely(expr)
#define SCStatFn(pathname, statbuf)
Definition util-path.h:35
struct stat SCStat
Definition util-path.h:33
uint64_t SCTimespecAsEpochMillis(const struct timespec *ts)
Definition util-time.c:639