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