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 
49 
50 void 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 
59 void CopyTime(struct timespec *from, struct timespec *to)
60 {
61  to->tv_sec = from->tv_sec;
62  to->tv_nsec = from->tv_nsec;
63 }
64 
65 int 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  */
85 TmEcode 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 
134 TmEcode 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 
154 TmEcode 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 
174 TmEcode 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 
223 int 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 
247 TmEcode 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 
292 TmEcode 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 
386 TmEcode 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 */
PcapFileDispatch
TmEcode PcapFileDispatch(PcapFileFileVars *ptv)
Main PCAP file reading Loop function.
Definition: source-pcap-file-helper.c:126
PcapFileGlobalVars_
Definition: source-pcap-file-helper.h:30
source-pcap-file.h
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:82
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:248
SURICATA_STOP
#define SURICATA_STOP
Definition: suricata.h:89
PcapFileFileVars
struct PcapFileFileVars_ PcapFileFileVars
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:81
runmode-unix-socket.h
PcapDetermineDirectoryOrFile
TmEcode PcapDetermineDirectoryOrFile(char *filename, DIR **directory)
Definition: source-pcap-file-directory-helper.c:174
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:80
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:100
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:1717
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
PcapFileFileVars_
Definition: source-pcap-file-helper.h:70
TmEcode
TmEcode
Definition: tm-threads-common.h:79
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:109
PcapDirectoryDispatch
TmEcode PcapDirectoryDispatch(PcapFileDirectoryVars *ptv)
Definition: source-pcap-file-directory-helper.c:480
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:47
pcap_g
PcapFileGlobalVars pcap_g
Definition: source-pcap-file.c:38
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:568
suricata.h
PcapFileDirectoryVars_::filename
char * filename
Definition: source-pcap-file-directory-helper.h:42
StatsSyncCountersIfSignalled
void StatsSyncCountersIfSignalled(ThreadVars *tv)
Definition: counters.c:449
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:51
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
PcapFileGlobalVars_::read_buffer_size
uint32_t read_buffer_size
Definition: source-pcap-file-helper.h:35
PendingFile_::filename
char * filename
Definition: source-pcap-file-directory-helper.h:33
suricata_ctl_flags
volatile uint8_t suricata_ctl_flags
Definition: suricata.c:169