suricata
log-pcap.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2021 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 William Metcalf <William.Metcalf@gmail.com>
22  * \author Victor Julien <victor@inliniac.net>
23  *
24  * Pcap packet logging module.
25  */
26 
27 #include "suricata-common.h"
28 #ifdef HAVE_LIBLZ4
29 #include <lz4frame.h>
30 #include "util-fmemopen.h"
31 #endif /* HAVE_LIBLZ4 */
32 
33 #if defined(HAVE_DIRENT_H) && defined(HAVE_FNMATCH_H)
34 #define INIT_RING_BUFFER
35 #include <dirent.h>
36 #include <fnmatch.h>
37 #endif
38 
39 #include "log-pcap.h"
40 
41 #include "threads.h"
42 #include "threadvars.h"
43 #include "decode.h"
44 #include "stream.h"
45 #include "stream-tcp-reassemble.h"
46 
47 #include "output.h"
48 
49 #include "util-buffer.h"
50 #include "util-byte.h"
51 #include "util-conf.h"
52 #include "util-cpu.h"
53 #include "util-datalink.h"
54 #include "util-misc.h"
55 #include "util-path.h"
56 #include "util-profiling.h"
57 #include "util-time.h"
58 
59 #define DEFAULT_LOG_FILENAME "pcaplog"
60 #define MODULE_NAME "PcapLog"
61 #define MIN_LIMIT 4 * 1024 * 1024
62 #define DEFAULT_LIMIT 100 * 1024 * 1024
63 #define DEFAULT_FILE_LIMIT 0
64 
65 #define LOGMODE_NORMAL 0
66 #define LOGMODE_MULTI 1
67 
73 
74 #define RING_BUFFER_MODE_DISABLED 0
75 #define RING_BUFFER_MODE_ENABLED 1
76 
77 #define TS_FORMAT_SEC 0
78 #define TS_FORMAT_USEC 1
79 
80 #define USE_STREAM_DEPTH_DISABLED 0
81 #define USE_STREAM_DEPTH_ENABLED 1
82 
83 #define HONOR_PASS_RULES_DISABLED 0
84 #define HONOR_PASS_RULES_ENABLED 1
85 
86 #define PCAP_SNAPLEN 262144
87 #define PCAP_BUFFER_TIMEOUT 1000000 // microseconds
88 
89 SC_ATOMIC_DECLARE(uint32_t, thread_cnt);
90 
91 typedef struct PcapFileName_ {
92  char *filename;
93  char *dirname;
94 
95  /* Like a struct timeval, but with fixed size. This is only used when
96  * seeding the ring buffer on start. */
97  struct {
98  uint64_t secs;
99  uint32_t usecs;
100  };
101 
102  TAILQ_ENTRY(PcapFileName_) next; /**< Pointer to next Pcap File for tailq. */
104 
105 thread_local char *pcap_file_thread = NULL;
106 
107 typedef struct PcapLogProfileData_ {
108  uint64_t total;
109  uint64_t cnt;
111 
112 #define MAX_TOKS 9
113 #define MAX_FILENAMELEN 513
114 
118 };
119 
120 typedef struct PcapLogCompressionData_ {
122  uint8_t *buffer;
123  uint64_t buffer_size;
124 #ifdef HAVE_LIBLZ4
125  LZ4F_compressionContext_t lz4f_context;
126  LZ4F_preferences_t lz4f_prefs;
127 #endif /* HAVE_LIBLZ4 */
128  FILE *file;
129  uint8_t *pcap_buf;
130  uint64_t pcap_buf_size;
132  uint64_t bytes_in_block;
134 
135 /**
136  * PcapLog thread vars
137  *
138  * Used for storing file options.
139  */
140 typedef struct PcapLogData_ {
141  int use_stream_depth; /**< use stream depth i.e. ignore packets that reach limit */
142  int honor_pass_rules; /**< don't log if pass rules have matched */
144  uint64_t pkt_cnt; /**< total number of packets */
145  struct pcap_pkthdr *h; /**< pcap header struct */
146  char *filename; /**< current filename */
147  int mode; /**< normal or multi */
148  int prev_day; /**< last day, for finding out when */
149  uint64_t size_current; /**< file current size */
150  uint64_t size_limit; /**< file size limit */
151  pcap_t *pcap_dead_handle; /**< pcap_dumper_t needs a handle */
152  pcap_dumper_t *pcap_dumper; /**< actually writes the packets */
153  uint64_t profile_data_size; /**< track in bytes how many bytes we wrote */
154  uint32_t file_cnt; /**< count of pcap files we currently have */
155  uint32_t max_files; /**< maximum files to use in ring buffer mode */
156  bool is_private; /**< true if ctx is thread local */
158  conditional; /**< log all packets or just packets and flows with alerts */
159 
167 
168  TAILQ_HEAD(, PcapFileName_) pcap_file_list;
169 
170  uint32_t thread_number; /**< thread number, first thread is 1, second 2, etc */
171  int use_ringbuffer; /**< ring buffer mode enabled or disabled */
172  int timestamp_format; /**< timestamp format sec or usec */
173  char *prefix; /**< filename prefix */
174  const char *suffix; /**< filename suffix */
175  char dir[PATH_MAX]; /**< pcap log directory */
176  int reported;
177  int threads; /**< number of threads (only set in the global) */
178  char *filename_parts[MAX_TOKS];
179  int filename_part_cnt;
180  struct timeval last_pcap_dump;
181  int fopen_err; /**< set to the last fopen error */
182  bool pcap_open_err; /**< true if the last pcap open errored */
183 
184  PcapLogCompressionData compression;
186 
187 typedef struct PcapLogThreadData_ {
191 
192 /* Pattern for extracting timestamp from pcap log files. */
193 static const char timestamp_pattern[] = ".*?(\\d+)(\\.(\\d+))?";
194 static pcre2_code *pcre_timestamp_code = NULL;
195 static pcre2_match_data *pcre_timestamp_match = NULL;
196 
197 /* global pcap data for when we're using multi mode. At exit we'll
198  * merge counters into this one and then report counters. */
199 static PcapLogData *g_pcap_data = NULL;
200 
201 static int PcapLogOpenFileCtx(PcapLogData *);
202 static int PcapLog(ThreadVars *, void *, const Packet *);
203 static TmEcode PcapLogDataInit(ThreadVars *, const void *, void **);
204 static TmEcode PcapLogDataDeinit(ThreadVars *, void *);
205 static void PcapLogFileDeInitCtx(OutputCtx *);
206 static OutputInitResult PcapLogInitCtx(ConfNode *);
207 static void PcapLogProfilingDump(PcapLogData *);
208 static bool PcapLogCondition(ThreadVars *, void *, const Packet *);
209 
210 void PcapLogRegister(void)
211 {
213  PcapLogInitCtx, PcapLog, PcapLogCondition, PcapLogDataInit,
214  PcapLogDataDeinit, NULL);
216  SC_ATOMIC_INIT(thread_cnt);
217  SC_ATOMIC_SET(thread_cnt, 1); /* first id is 1 */
218  return;
219 }
220 
221 #define PCAPLOG_PROFILE_START \
222  uint64_t pcaplog_profile_ticks = UtilCpuGetTicks()
223 
224 #define PCAPLOG_PROFILE_END(prof) \
225  (prof).total += (UtilCpuGetTicks() - pcaplog_profile_ticks); \
226  (prof).cnt++
227 
228 static bool PcapLogCondition(ThreadVars *tv, void *thread_data, const Packet *p)
229 {
230  PcapLogThreadData *ptd = (PcapLogThreadData *)thread_data;
231 
232  /* Log alerted flow or tagged flow */
233  switch (ptd->pcap_log->conditional) {
234  case LOGMODE_COND_ALL:
235  break;
236  case LOGMODE_COND_ALERTS:
237  return (p->alerts.cnt || (p->flow && FlowHasAlerts(p->flow)));
238  break;
239  case LOGMODE_COND_TAG:
240  return (p->flags & (PKT_HAS_TAG | PKT_FIRST_TAG));
241  break;
242  }
243 
244  if (p->flags & PKT_PSEUDO_STREAM_END) {
245  return false;
246  }
247 
248  if (p->ttype == PacketTunnelChild) {
249  return false;
250  }
251  return true;
252 }
253 
254 /**
255  * \brief Function to close pcaplog file
256  *
257  * \param t Thread Variable containing input/output queue, cpu affinity etc.
258  * \param pl PcapLog thread variable.
259  */
260 static int PcapLogCloseFile(ThreadVars *t, PcapLogData *pl)
261 {
262  if (pl != NULL) {
264 
265  if (pl->pcap_dumper != NULL) {
266  pcap_dump_close(pl->pcap_dumper);
267 #ifdef HAVE_LIBLZ4
268  PcapLogCompressionData *comp = &pl->compression;
270  /* pcap_dump_close() has closed its output ``file'',
271  * so we need to call fmemopen again. */
272 
273  comp->pcap_buf_wrapper = SCFmemopen(comp->pcap_buf,
274  comp->pcap_buf_size, "w");
275  if (comp->pcap_buf_wrapper == NULL) {
276  SCLogError("SCFmemopen failed: %s", strerror(errno));
277  return TM_ECODE_FAILED;
278  }
279  }
280 #endif /* HAVE_LIBLZ4 */
281  }
282  pl->size_current = 0;
283  pl->pcap_dumper = NULL;
284 
285  if (pl->pcap_dead_handle != NULL)
286  pcap_close(pl->pcap_dead_handle);
287  pl->pcap_dead_handle = NULL;
288 
289 #ifdef HAVE_LIBLZ4
290  PcapLogCompressionData *comp = &pl->compression;
292  /* pcap_dump_close did not write any data because we call
293  * pcap_dump_flush() after every write when writing
294  * compressed output. */
295  uint64_t bytes_written = LZ4F_compressEnd(comp->lz4f_context,
296  comp->buffer, comp->buffer_size, NULL);
297  if (LZ4F_isError(bytes_written)) {
298  SCLogError("LZ4F_compressEnd: %s", LZ4F_getErrorName(bytes_written));
299  return TM_ECODE_FAILED;
300  }
301  if (fwrite(comp->buffer, 1, bytes_written, comp->file) < bytes_written) {
302  SCLogError("fwrite failed: %s", strerror(errno));
303  return TM_ECODE_FAILED;
304  }
305  fclose(comp->file);
306  comp->bytes_in_block = 0;
307  }
308 #endif /* HAVE_LIBLZ4 */
309 
311  }
312 
313  return 0;
314 }
315 
316 static void PcapFileNameFree(PcapFileName *pf)
317 {
318  if (pf != NULL) {
319  if (pf->filename != NULL) {
320  SCFree(pf->filename);
321  }
322  if (pf->dirname != NULL) {
323  SCFree(pf->dirname);
324  }
325  SCFree(pf);
326  }
327 
328  return;
329 }
330 
331 /**
332  * \brief Function to rotate pcaplog file
333  *
334  * \param t Thread Variable containing input/output queue, cpu affinity etc.
335  * \param pl PcapLog thread variable.
336  *
337  * \retval 0 on success
338  * \retval -1 on failure
339  */
340 static int PcapLogRotateFile(ThreadVars *t, PcapLogData *pl)
341 {
342  PcapFileName *pf;
343 
345 
346  if (PcapLogCloseFile(t,pl) < 0) {
347  SCLogDebug("PcapLogCloseFile failed");
348  return -1;
349  }
350 
351  if (pl->use_ringbuffer == RING_BUFFER_MODE_ENABLED && pl->file_cnt >= pl->max_files) {
352  pf = TAILQ_FIRST(&pl->pcap_file_list);
353  SCLogDebug("Removing pcap file %s", pf->filename);
354 
355  if (remove(pf->filename) != 0) {
356  // VJ remove can fail because file is already gone
357  // SCLogWarning("failed to remove log file %s: %s",
358  // pf->filename, strerror( errno ));
359  }
360 
361  TAILQ_REMOVE(&pl->pcap_file_list, pf, next);
362  PcapFileNameFree(pf);
363  pl->file_cnt--;
364  }
365 
366  if (PcapLogOpenFileCtx(pl) < 0) {
367  SCLogError("opening new pcap log file failed");
368  return -1;
369  }
370  pl->file_cnt++;
371  SCLogDebug("file_cnt %u", pl->file_cnt);
372 
374  return 0;
375 }
376 
377 static int PcapLogOpenHandles(PcapLogData *pl, const Packet *p)
378 {
380 
381  int datalink = p->datalink;
382  if (p->ttype == PacketTunnelChild) {
383  Packet *real_p = p->root;
384  datalink = real_p->datalink;
385  }
386  if (pl->pcap_dead_handle == NULL) {
387  SCLogDebug("Setting pcap-log link type to %u", datalink);
388  if ((pl->pcap_dead_handle = pcap_open_dead(datalink, PCAP_SNAPLEN)) == NULL) {
389  SCLogDebug("Error opening dead pcap handle");
390  return TM_ECODE_FAILED;
391  }
392  }
393 
394  if (pl->pcap_dumper == NULL) {
395  if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_NONE) {
396  if ((pl->pcap_dumper = pcap_dump_open(pl->pcap_dead_handle,
397  pl->filename)) == NULL) {
398  if (!pl->pcap_open_err) {
399  SCLogError("Error opening dump file %s", pcap_geterr(pl->pcap_dead_handle));
400  pl->pcap_open_err = true;
401  }
402  return TM_ECODE_FAILED;
403  } else {
404  pl->pcap_open_err = false;
405  }
406  }
407 #ifdef HAVE_LIBLZ4
408  else if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) {
409  PcapLogCompressionData *comp = &pl->compression;
410 
411  comp->file = fopen(pl->filename, "w");
412  if (comp->file == NULL) {
413  if (errno != pl->fopen_err) {
414  SCLogError("Error opening file for compressed output: %s", strerror(errno));
415  pl->fopen_err = errno;
416  }
417  return TM_ECODE_FAILED;
418  } else {
419  pl->fopen_err = 0;
420  }
421 
422  if ((pl->pcap_dumper = pcap_dump_fopen(pl->pcap_dead_handle, comp->pcap_buf_wrapper)) ==
423  NULL) {
424  if (!pl->pcap_open_err) {
425  SCLogError("Error opening dump file %s", pcap_geterr(pl->pcap_dead_handle));
426  pl->pcap_open_err = true;
427  }
428  fclose(comp->file);
429  comp->file = NULL;
430  return TM_ECODE_FAILED;
431  } else {
432  pl->pcap_open_err = false;
433  }
434 
435  uint64_t bytes_written = LZ4F_compressBegin(comp->lz4f_context,
436  comp->buffer, comp->buffer_size, NULL);
437  if (LZ4F_isError(bytes_written)) {
438  SCLogError("LZ4F_compressBegin: %s", LZ4F_getErrorName(bytes_written));
439  return TM_ECODE_FAILED;
440  }
441  if (fwrite(comp->buffer, 1, bytes_written, comp->file) < bytes_written) {
442  SCLogError("fwrite failed: %s", strerror(errno));
443  return TM_ECODE_FAILED;
444  }
445  }
446 #endif /* HAVE_LIBLZ4 */
447  }
448 
450  return TM_ECODE_OK;
451 }
452 
453 /** \internal
454  * \brief lock wrapper for main PcapLog() function
455  * NOTE: only meant for use in main PcapLog() function.
456  */
457 static void PcapLogLock(PcapLogData *pl)
458 {
459  if (!(pl->is_private)) {
461  SCMutexLock(&pl->plog_lock);
463  }
464 }
465 
466 /** \internal
467  * \brief unlock wrapper for main PcapLog() function
468  * NOTE: only meant for use in main PcapLog() function.
469  */
470 static void PcapLogUnlock(PcapLogData *pl)
471 {
472  if (!(pl->is_private)) {
474  SCMutexUnlock(&pl->plog_lock);
476  }
477 }
478 
479 static inline int PcapWrite(
480  PcapLogData *pl, PcapLogCompressionData *comp, const uint8_t *data, const size_t len)
481 {
482  struct timeval current_dump;
483  gettimeofday(&current_dump, NULL);
484  pcap_dump((u_char *)pl->pcap_dumper, pl->h, data);
485  if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_NONE) {
486  pl->size_current += len;
487  }
488 #ifdef HAVE_LIBLZ4
489  else if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) {
490  pcap_dump_flush(pl->pcap_dumper);
491  long in_size = ftell(comp->pcap_buf_wrapper);
492  if (in_size < 0) {
493  SCLogError("ftell failed with: %s", strerror(errno));
494  return TM_ECODE_FAILED;
495  }
496  uint64_t out_size = LZ4F_compressUpdate(comp->lz4f_context, comp->buffer, comp->buffer_size,
497  comp->pcap_buf, (uint64_t)in_size, NULL);
498  if (LZ4F_isError(len)) {
499  SCLogError("LZ4F_compressUpdate: %s", LZ4F_getErrorName(len));
500  return TM_ECODE_FAILED;
501  }
502  if (fseek(pl->compression.pcap_buf_wrapper, 0, SEEK_SET) != 0) {
503  SCLogError("fseek failed: %s", strerror(errno));
504  return TM_ECODE_FAILED;
505  }
506  if (fwrite(comp->buffer, 1, out_size, comp->file) < out_size) {
507  SCLogError("fwrite failed: %s", strerror(errno));
508  return TM_ECODE_FAILED;
509  }
510  if (out_size > 0) {
511  pl->size_current += out_size;
512  comp->bytes_in_block = len;
513  } else {
514  comp->bytes_in_block += len;
515  }
516  }
517 #endif /* HAVE_LIBLZ4 */
518  if (TimeDifferenceMicros(pl->last_pcap_dump, current_dump) >= PCAP_BUFFER_TIMEOUT) {
519  pcap_dump_flush(pl->pcap_dumper);
520  }
521  pl->last_pcap_dump = current_dump;
522  return TM_ECODE_OK;
523 }
524 
529 };
530 
531 static int PcapLogSegmentCallback(
532  const Packet *p, TcpSegment *seg, void *data, const uint8_t *buf, uint32_t buflen)
533 {
534  struct PcapLogCallbackContext *pctx = (struct PcapLogCallbackContext *)data;
535 
536  if (seg->pcap_hdr_storage->pktlen) {
537  pctx->pl->h->ts.tv_sec = seg->pcap_hdr_storage->ts.tv_sec;
538  pctx->pl->h->ts.tv_usec = seg->pcap_hdr_storage->ts.tv_usec;
539  pctx->pl->h->len = seg->pcap_hdr_storage->pktlen + buflen;
540  pctx->pl->h->caplen = seg->pcap_hdr_storage->pktlen + buflen;
541  MemBufferReset(pctx->buf);
543  MemBufferWriteRaw(pctx->buf, buf, buflen);
544 
545  PcapWrite(pctx->pl, pctx->connp, (uint8_t *)pctx->buf->buffer, pctx->pl->h->len);
546  }
547  return 1;
548 }
549 
550 static void PcapLogDumpSegments(
552 {
553  uint8_t flag;
554  flag = STREAM_DUMP_HEADERS;
555 
556  /* Loop on segment from this side */
557  struct PcapLogCallbackContext data = { td->pcap_log, connp, td->buf };
558  StreamSegmentForSession(p, flag, PcapLogSegmentCallback, (void *)&data);
559 }
560 
561 /**
562  * \brief Pcap logging main function
563  *
564  * \param t threadvar
565  * \param p packet
566  * \param thread_data thread module specific data
567  *
568  * \retval TM_ECODE_OK on succes
569  * \retval TM_ECODE_FAILED on serious error
570  */
571 static int PcapLog (ThreadVars *t, void *thread_data, const Packet *p)
572 {
573  size_t len;
574  int rotate = 0;
575  int ret = 0;
576  Packet *rp = NULL;
577 
578  PcapLogThreadData *td = (PcapLogThreadData *)thread_data;
579  PcapLogData *pl = td->pcap_log;
580 
583  return TM_ECODE_OK;
584  }
585 
586  PcapLogLock(pl);
587 
588  pl->pkt_cnt++;
589  pl->h->ts.tv_sec = SCTIME_SECS(p->ts);
590  pl->h->ts.tv_usec = SCTIME_USECS(p->ts);
591  if (p->ttype == PacketTunnelChild) {
592  rp = p->root;
593  pl->h->caplen = GET_PKT_LEN(rp);
594  pl->h->len = GET_PKT_LEN(rp);
595  len = sizeof(*pl->h) + GET_PKT_LEN(rp);
596  } else {
597  pl->h->caplen = GET_PKT_LEN(p);
598  pl->h->len = GET_PKT_LEN(p);
599  len = sizeof(*pl->h) + GET_PKT_LEN(p);
600  }
601 
602  if (pl->filename == NULL) {
603  ret = PcapLogOpenFileCtx(pl);
604  if (ret < 0) {
605  PcapLogUnlock(pl);
606  return TM_ECODE_FAILED;
607  }
608  SCLogDebug("Opening PCAP log file %s", pl->filename);
609  }
610 
611  PcapLogCompressionData *comp = &pl->compression;
613  if ((pl->size_current + len) > pl->size_limit || rotate) {
614  if (PcapLogRotateFile(t,pl) < 0) {
615  PcapLogUnlock(pl);
616  SCLogDebug("rotation of pcap failed");
617  return TM_ECODE_FAILED;
618  }
619  }
620  }
621 #ifdef HAVE_LIBLZ4
622  else if (comp->format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) {
623  /* When writing compressed pcap logs, we have no way of knowing
624  * for sure whether adding this packet would cause the current
625  * file to exceed the size limit. Thus, we record the number of
626  * bytes that have been fed into lz4 since the last write, and
627  * act as if they would be written uncompressed. */
628 
629  if ((pl->size_current + comp->bytes_in_block + len) > pl->size_limit ||
630  rotate) {
631  if (PcapLogRotateFile(t,pl) < 0) {
632  PcapLogUnlock(pl);
633  SCLogDebug("rotation of pcap failed");
634  return TM_ECODE_FAILED;
635  }
636  }
637  }
638 #endif /* HAVE_LIBLZ4 */
639 
640  /* XXX pcap handles, nfq, pfring, can only have one link type ipfw? we do
641  * this here as we don't know the link type until we get our first packet */
642  if (pl->pcap_dead_handle == NULL || pl->pcap_dumper == NULL) {
643  if (PcapLogOpenHandles(pl, p) != TM_ECODE_OK) {
644  PcapLogUnlock(pl);
645  return TM_ECODE_FAILED;
646  }
647  }
648 
650 
651  /* if we are using alerted logging and if packet is first one with alert in flow
652  * then we need to dump in the pcap the stream acked by the packet */
653  if ((p->flags & PKT_FIRST_ALERTS) && (td->pcap_log->conditional != LOGMODE_COND_ALL)) {
654  if (PKT_IS_TCP(p)) {
655  /* dump fake packets for all segments we have on acked by packet */
656 #ifdef HAVE_LIBLZ4
657  PcapLogDumpSegments(td, comp, p);
658 #else
659  PcapLogDumpSegments(td, NULL, p);
660 #endif
661  if (p->flags & PKT_PSEUDO_STREAM_END) {
662  PcapLogUnlock(pl);
663  return TM_ECODE_OK;
664  }
665 
666  /* PcapLogDumpSegment has written over the PcapLogData variables so need to update */
667  pl->h->ts.tv_sec = SCTIME_SECS(p->ts);
668  pl->h->ts.tv_usec = SCTIME_USECS(p->ts);
669  if (p->ttype == PacketTunnelChild) {
670  rp = p->root;
671  pl->h->caplen = GET_PKT_LEN(rp);
672  pl->h->len = GET_PKT_LEN(rp);
673  len = sizeof(*pl->h) + GET_PKT_LEN(rp);
674  } else {
675  pl->h->caplen = GET_PKT_LEN(p);
676  pl->h->len = GET_PKT_LEN(p);
677  len = sizeof(*pl->h) + GET_PKT_LEN(p);
678  }
679  }
680  }
681 
682  if (p->ttype == PacketTunnelChild) {
683  rp = p->root;
684 #ifdef HAVE_LIBLZ4
685  ret = PcapWrite(pl, comp, GET_PKT_DATA(rp), len);
686 #else
687  ret = PcapWrite(pl, NULL, GET_PKT_DATA(rp), len);
688 #endif
689  } else {
690 #ifdef HAVE_LIBLZ4
691  ret = PcapWrite(pl, comp, GET_PKT_DATA(p), len);
692 #else
693  ret = PcapWrite(pl, NULL, GET_PKT_DATA(p), len);
694 #endif
695  }
696  if (ret != TM_ECODE_OK) {
698  PcapLogUnlock(pl);
699  return ret;
700  }
701 
704 
705  SCLogDebug("pl->size_current %"PRIu64", pl->size_limit %"PRIu64,
707 
708  PcapLogUnlock(pl);
709  return TM_ECODE_OK;
710 }
711 
712 static PcapLogData *PcapLogDataCopy(const PcapLogData *pl)
713 {
715  PcapLogData *copy = SCCalloc(1, sizeof(*copy));
716  if (unlikely(copy == NULL)) {
717  return NULL;
718  }
719 
720  copy->h = SCCalloc(1, sizeof(*copy->h));
721  if (unlikely(copy->h == NULL)) {
722  SCFree(copy);
723  return NULL;
724  }
725 
726  copy->prefix = SCStrdup(pl->prefix);
727  if (unlikely(copy->prefix == NULL)) {
728  SCFree(copy->h);
729  SCFree(copy);
730  return NULL;
731  }
732 
733  copy->suffix = pl->suffix;
734 
735  /* settings TODO move to global cfg struct */
736  copy->is_private = true;
737  copy->mode = pl->mode;
738  copy->max_files = pl->max_files;
739  copy->use_ringbuffer = pl->use_ringbuffer;
740  copy->timestamp_format = pl->timestamp_format;
742  copy->size_limit = pl->size_limit;
743  copy->conditional = pl->conditional;
744 
745  const PcapLogCompressionData *comp = &pl->compression;
746  PcapLogCompressionData *copy_comp = &copy->compression;
747  copy_comp->format = comp->format;
748 #ifdef HAVE_LIBLZ4
750  /* We need to allocate a new compression context and buffers for
751  * the copy. First copy the things that can simply be copied. */
752 
753  copy_comp->buffer_size = comp->buffer_size;
754  copy_comp->pcap_buf_size = comp->pcap_buf_size;
755  copy_comp->lz4f_prefs = comp->lz4f_prefs;
756 
757  /* Allocate the buffers. */
758 
759  copy_comp->buffer = SCMalloc(copy_comp->buffer_size);
760  if (copy_comp->buffer == NULL) {
761  SCLogError("SCMalloc failed: %s", strerror(errno));
762  SCFree(copy->h);
763  SCFree(copy);
764  return NULL;
765  }
766  copy_comp->pcap_buf = SCMalloc(copy_comp->pcap_buf_size);
767  if (copy_comp->pcap_buf == NULL) {
768  SCLogError("SCMalloc failed: %s", strerror(errno));
769  SCFree(copy_comp->buffer);
770  SCFree(copy->h);
771  SCFree(copy);
772  return NULL;
773  }
774  copy_comp->pcap_buf_wrapper = SCFmemopen(copy_comp->pcap_buf,
775  copy_comp->pcap_buf_size, "w");
776  if (copy_comp->pcap_buf_wrapper == NULL) {
777  SCLogError("SCFmemopen failed: %s", strerror(errno));
778  SCFree(copy_comp->buffer);
779  SCFree(copy_comp->pcap_buf);
780  SCFree(copy->h);
781  SCFree(copy);
782  return NULL;
783  }
784 
785  /* Initialize a new compression context. */
786 
787  LZ4F_errorCode_t errcode =
788  LZ4F_createCompressionContext(&copy_comp->lz4f_context, 1);
789  if (LZ4F_isError(errcode)) {
790  SCLogError("LZ4F_createCompressionContext failed: %s", LZ4F_getErrorName(errcode));
791  fclose(copy_comp->pcap_buf_wrapper);
792  SCFree(copy_comp->buffer);
793  SCFree(copy_comp->pcap_buf);
794  SCFree(copy->h);
795  SCFree(copy);
796  return NULL;
797  }
798 
799  /* Initialize the rest. */
800 
801  copy_comp->file = NULL;
802  copy_comp->bytes_in_block = 0;
803  }
804 #endif /* HAVE_LIBLZ4 */
805 
806  TAILQ_INIT(&copy->pcap_file_list);
807  SCMutexInit(&copy->plog_lock, NULL);
808 
809  strlcpy(copy->dir, pl->dir, sizeof(copy->dir));
810 
811  int i;
812  for (i = 0; i < pl->filename_part_cnt && i < MAX_TOKS; i++)
813  copy->filename_parts[i] = pl->filename_parts[i];
814  copy->filename_part_cnt = pl->filename_part_cnt;
815 
816  /* set thread number, first thread is 1 */
817  copy->thread_number = SC_ATOMIC_ADD(thread_cnt, 1);
818 
819  SCLogDebug("copied, returning %p", copy);
820  return copy;
821 }
822 
823 #ifdef INIT_RING_BUFFER
824 static int PcapLogGetTimeOfFile(const char *filename, uint64_t *secs,
825  uint32_t *usecs)
826 {
827  char buf[PATH_MAX];
828  size_t copylen;
829 
830  int n = pcre2_match(pcre_timestamp_code, (PCRE2_SPTR8)filename, strlen(filename), 0, 0,
831  pcre_timestamp_match, NULL);
832  if (n != 2 && n != 4) {
833  /* No match. */
834  return 0;
835  }
836 
837  if (n >= 2) {
838  /* Extract seconds. */
839  copylen = sizeof(buf);
840  if (pcre2_substring_copy_bynumber(pcre_timestamp_match, 1, (PCRE2_UCHAR8 *)buf, &copylen) <
841  0) {
842  return 0;
843  }
844  if (StringParseUint64(secs, 10, 0, buf) < 0) {
845  return 0;
846  }
847  }
848  if (n == 4) {
849  /* Extract microseconds. */
850  copylen = sizeof(buf);
851  if (pcre2_substring_copy_bynumber(pcre_timestamp_match, 3, (PCRE2_UCHAR8 *)buf, &copylen) <
852  0) {
853  return 0;
854  }
855  if (StringParseUint32(usecs, 10, 0, buf) < 0) {
856  return 0;
857  }
858  }
859 
860  return 1;
861 }
862 
863 static TmEcode PcapLogInitRingBuffer(PcapLogData *pl)
864 {
865  char pattern[PATH_MAX];
866 
867  SCLogInfo("Initializing PCAP ring buffer for %s/%s.",
868  pl->dir, pl->prefix);
869 
870  strlcpy(pattern, pl->dir, PATH_MAX);
871  if (pattern[strlen(pattern) - 1] != '/') {
872  strlcat(pattern, "/", PATH_MAX);
873  }
874  if (pl->mode == LOGMODE_MULTI) {
875  for (int i = 0; i < pl->filename_part_cnt; i++) {
876  char *part = pl->filename_parts[i];
877  if (part == NULL || strlen(part) == 0) {
878  continue;
879  }
880  if (part[0] != '%' || strlen(part) < 2) {
881  strlcat(pattern, part, PATH_MAX);
882  continue;
883  }
884  switch (part[1]) {
885  case 'i':
886  SCLogError("Thread ID not allowed in ring buffer mode.");
887  return TM_ECODE_FAILED;
888  case 'n': {
889  char tmp[PATH_MAX];
890  snprintf(tmp, PATH_MAX, "%"PRIu32, pl->thread_number);
891  strlcat(pattern, tmp, PATH_MAX);
892  break;
893  }
894  case 't':
895  strlcat(pattern, "*", PATH_MAX);
896  break;
897  default:
898  SCLogError("Unsupported format character: %%%s", part);
899  return TM_ECODE_FAILED;
900  }
901  }
902  } else {
903  strlcat(pattern, pl->prefix, PATH_MAX);
904  strlcat(pattern, ".*", PATH_MAX);
905  }
906  strlcat(pattern, pl->suffix, PATH_MAX);
907 
908  char *basename = strrchr(pattern, '/');
909  *basename++ = '\0';
910 
911  /* Pattern is now just the directory name. */
912  DIR *dir = opendir(pattern);
913  if (dir == NULL) {
914  SCLogWarning("Failed to open directory %s: %s", pattern, strerror(errno));
915  return TM_ECODE_FAILED;
916  }
917 
918  for (;;) {
919  struct dirent *entry = readdir(dir);
920  if (entry == NULL) {
921  break;
922  }
923  if (fnmatch(basename, entry->d_name, 0) != 0) {
924  continue;
925  }
926 
927  uint64_t secs = 0;
928  uint32_t usecs = 0;
929 
930  if (!PcapLogGetTimeOfFile(entry->d_name, &secs, &usecs)) {
931  /* Failed to get time stamp out of file name. Not necessarily a
932  * failure as the file might just not be a pcap log file. */
933  continue;
934  }
935 
936  PcapFileName *pf = SCCalloc(sizeof(*pf), 1);
937  if (unlikely(pf == NULL)) {
938  goto fail;
939  }
940  char path[PATH_MAX];
941  if (snprintf(path, PATH_MAX, "%s/%s", pattern, entry->d_name) == PATH_MAX)
942  goto fail;
943 
944  if ((pf->filename = SCStrdup(path)) == NULL) {
945  goto fail;
946  }
947  if ((pf->dirname = SCStrdup(pattern)) == NULL) {
948  goto fail;
949  }
950  pf->secs = secs;
951  pf->usecs = usecs;
952 
953  if (TAILQ_EMPTY(&pl->pcap_file_list)) {
954  TAILQ_INSERT_TAIL(&pl->pcap_file_list, pf, next);
955  } else {
956  /* Ordered insert. */
957  PcapFileName *it = NULL;
958  TAILQ_FOREACH(it, &pl->pcap_file_list, next) {
959  if (pf->secs < it->secs) {
960  break;
961  } else if (pf->secs == it->secs && pf->usecs < it->usecs) {
962  break;
963  }
964  }
965  if (it == NULL) {
966  TAILQ_INSERT_TAIL(&pl->pcap_file_list, pf, next);
967  } else {
968  TAILQ_INSERT_BEFORE(it, pf, next);
969  }
970  }
971  pl->file_cnt++;
972  continue;
973 
974  fail:
975  if (pf != NULL) {
976  if (pf->filename != NULL) {
977  SCFree(pf->filename);
978  }
979  if (pf->dirname != NULL) {
980  SCFree(pf->dirname);
981  }
982  SCFree(pf);
983  }
984  break;
985  }
986 
987  if (pl->file_cnt > pl->max_files) {
988  PcapFileName *pf = TAILQ_FIRST(&pl->pcap_file_list);
989  while (pf != NULL && pl->file_cnt > pl->max_files) {
990  TAILQ_REMOVE(&pl->pcap_file_list, pf, next);
991 
992  SCLogDebug("Removing PCAP file %s", pf->filename);
993  if (remove(pf->filename) != 0) {
994  SCLogWarning("Failed to remove PCAP file %s: %s", pf->filename, strerror(errno));
995  }
996  PcapFileNameFree(pf);
997  pl->file_cnt--;
998 
999  pf = TAILQ_FIRST(&pl->pcap_file_list);
1000  }
1001  }
1002 
1003  closedir(dir);
1004 
1005  /* For some reason file count is initialized at one, instead of 0. */
1006  SCLogNotice("Ring buffer initialized with %d files.", pl->file_cnt - 1);
1007 
1008  return TM_ECODE_OK;
1009 }
1010 #endif /* INIT_RING_BUFFER */
1011 
1012 static TmEcode PcapLogDataInit(ThreadVars *t, const void *initdata, void **data)
1013 {
1014  if (initdata == NULL) {
1015  SCLogDebug("Error getting context for LogPcap. \"initdata\" argument NULL");
1016  return TM_ECODE_FAILED;
1017  }
1018 
1019  PcapLogData *pl = ((OutputCtx *)initdata)->data;
1020 
1021  PcapLogThreadData *td = SCCalloc(1, sizeof(*td));
1022  if (unlikely(td == NULL))
1023  return TM_ECODE_FAILED;
1024 
1025  if (pl->mode == LOGMODE_MULTI)
1026  td->pcap_log = PcapLogDataCopy(pl);
1027  else
1028  td->pcap_log = pl;
1029  BUG_ON(td->pcap_log == NULL);
1030 
1031  if (DatalinkHasMultipleValues()) {
1032  if (pl->mode != LOGMODE_MULTI) {
1033  FatalError("Pcap logging with multiple link type is not supported.");
1034  } else {
1035  /* In multi mode, only pcap conditional is not supported as a flow timeout
1036  * will trigger packet logging with potentially invalid datalink. In regular
1037  * pcap logging, the logging should be done in the same thread if we
1038  * have a proper load balancing. So no mix of datalink should occur. But we need a
1039  * proper load balancing so this needs at least a warning.
1040  */
1041  switch (pl->conditional) {
1042  case LOGMODE_COND_ALERTS:
1043  case LOGMODE_COND_TAG:
1044  FatalError("Can't have multiple link types in pcap conditional mode.");
1045  break;
1046  default:
1047  SCLogWarning("Using multiple link types can result in invalid pcap output");
1048  }
1049  }
1050  }
1051 
1052  PcapLogLock(td->pcap_log);
1053 
1054  /** Use the Output Context (file pointer and mutex) */
1055  td->pcap_log->pkt_cnt = 0;
1056  td->pcap_log->pcap_dead_handle = NULL;
1057  td->pcap_log->pcap_dumper = NULL;
1058  if (td->pcap_log->file_cnt < 1) {
1059  td->pcap_log->file_cnt = 1;
1060  }
1061 
1062  SCTime_t ts = TimeGet();
1063  struct tm local_tm;
1064  struct tm *tms = SCLocalTime(SCTIME_SECS(ts), &local_tm);
1065  td->pcap_log->prev_day = tms->tm_mday;
1066 
1067  PcapLogUnlock(td->pcap_log);
1068 
1069  /* count threads in the global structure */
1070  SCMutexLock(&pl->plog_lock);
1071  pl->threads++;
1072  SCMutexUnlock(&pl->plog_lock);
1073 
1074  *data = (void *)td;
1075 
1078  } else {
1079  td->buf = NULL;
1080  }
1081 
1082  if (pl->max_files && (pl->mode == LOGMODE_MULTI || pl->threads == 1)) {
1083 #ifdef INIT_RING_BUFFER
1084  if (PcapLogInitRingBuffer(td->pcap_log) == TM_ECODE_FAILED) {
1085  return TM_ECODE_FAILED;
1086  }
1087 #else
1088  SCLogInfo("Unable to initialize ring buffer on this platform.");
1089 #endif /* INIT_RING_BUFFER */
1090  }
1091 
1092  /* Don't early initialize output files if in a PCAP file (offline)
1093  * mode. */
1095  if (pl->mode == LOGMODE_MULTI) {
1096  PcapLogOpenFileCtx(td->pcap_log);
1097  } else {
1098  if (pl->filename == NULL) {
1099  PcapLogOpenFileCtx(pl);
1100  }
1101  }
1102  }
1103 
1104  return TM_ECODE_OK;
1105 }
1106 
1107 static void StatsMerge(PcapLogData *dst, PcapLogData *src)
1108 {
1109  dst->profile_open.total += src->profile_open.total;
1110  dst->profile_open.cnt += src->profile_open.cnt;
1111 
1112  dst->profile_close.total += src->profile_close.total;
1113  dst->profile_close.cnt += src->profile_close.cnt;
1114 
1115  dst->profile_write.total += src->profile_write.total;
1116  dst->profile_write.cnt += src->profile_write.cnt;
1117 
1118  dst->profile_rotate.total += src->profile_rotate.total;
1119  dst->profile_rotate.cnt += src->profile_rotate.cnt;
1120 
1121  dst->profile_handles.total += src->profile_handles.total;
1122  dst->profile_handles.cnt += src->profile_handles.cnt;
1123 
1124  dst->profile_lock.total += src->profile_lock.total;
1125  dst->profile_lock.cnt += src->profile_lock.cnt;
1126 
1127  dst->profile_unlock.total += src->profile_unlock.total;
1128  dst->profile_unlock.cnt += src->profile_unlock.cnt;
1129 
1130  dst->profile_data_size += src->profile_data_size;
1131 }
1132 
1133 static void PcapLogDataFree(PcapLogData *pl)
1134 {
1135 
1136  PcapFileName *pf;
1137  while ((pf = TAILQ_FIRST(&pl->pcap_file_list)) != NULL) {
1138  TAILQ_REMOVE(&pl->pcap_file_list, pf, next);
1139  PcapFileNameFree(pf);
1140  }
1141  if (pl == g_pcap_data) {
1142  for (int i = 0; i < MAX_TOKS; i++) {
1143  if (pl->filename_parts[i] != NULL) {
1144  SCFree(pl->filename_parts[i]);
1145  }
1146  }
1147  }
1148  SCFree(pl->h);
1149  SCFree(pl->filename);
1150  SCFree(pl->prefix);
1151 
1152  if (pl->pcap_dead_handle) {
1153  pcap_close(pl->pcap_dead_handle);
1154  }
1155 
1156 #ifdef HAVE_LIBLZ4
1157  if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) {
1158  SCFree(pl->compression.buffer);
1159  fclose(pl->compression.pcap_buf_wrapper);
1160  SCFree(pl->compression.pcap_buf);
1161  LZ4F_errorCode_t errcode =
1162  LZ4F_freeCompressionContext(pl->compression.lz4f_context);
1163  if (LZ4F_isError(errcode)) {
1164  SCLogWarning("Error freeing lz4 context.");
1165  }
1166  }
1167 #endif /* HAVE_LIBLZ4 */
1168  SCFree(pl);
1169 }
1170 
1171 /**
1172  * \brief Thread deinit function.
1173  *
1174  * \param t Thread Variable containing input/output queue, cpu affinity etc.
1175  * \param data PcapLog thread data.
1176  * \retval TM_ECODE_OK on success
1177  * \retval TM_ECODE_FAILED on failure
1178  */
1179 static TmEcode PcapLogDataDeinit(ThreadVars *t, void *thread_data)
1180 {
1181  PcapLogThreadData *td = (PcapLogThreadData *)thread_data;
1182  PcapLogData *pl = td->pcap_log;
1183 
1184  if (pl->pcap_dumper != NULL) {
1185  if (PcapLogCloseFile(t,pl) < 0) {
1186  SCLogDebug("PcapLogCloseFile failed");
1187  }
1188  }
1189 
1190  if (pl->mode == LOGMODE_MULTI) {
1191  SCMutexLock(&g_pcap_data->plog_lock);
1192  StatsMerge(g_pcap_data, pl);
1193  g_pcap_data->reported++;
1194  if (g_pcap_data->threads == g_pcap_data->reported)
1195  PcapLogProfilingDump(g_pcap_data);
1196  SCMutexUnlock(&g_pcap_data->plog_lock);
1197  } else {
1198  if (pl->reported == 0) {
1199  PcapLogProfilingDump(pl);
1200  pl->reported = 1;
1201  }
1202  }
1203 
1204  if (pl != g_pcap_data) {
1205  PcapLogDataFree(pl);
1206  }
1207 
1208  if (td->buf)
1209  MemBufferFree(td->buf);
1210 
1211  SCFree(td);
1212  return TM_ECODE_OK;
1213 }
1214 
1215 
1216 static int ParseFilename(PcapLogData *pl, const char *filename)
1217 {
1218  char *toks[MAX_TOKS] = { NULL };
1219  int tok = 0;
1220  char str[MAX_FILENAMELEN] = "";
1221  int s = 0;
1222  int i, x;
1223  char *p = NULL;
1224  size_t filename_len = 0;
1225 
1226  if (filename) {
1227  filename_len = strlen(filename);
1228  if (filename_len > (MAX_FILENAMELEN-1)) {
1229  SCLogError("invalid filename option. Max filename-length: %d", MAX_FILENAMELEN - 1);
1230  goto error;
1231  }
1232 
1233  for (i = 0; i < (int)strlen(filename); i++) {
1234  if (tok >= MAX_TOKS) {
1235  SCLogError("invalid filename option. Max 2 %%-sign options");
1236  goto error;
1237  }
1238 
1239  str[s++] = filename[i];
1240 
1241  if (filename[i] == '%') {
1242  str[s-1] = '\0';
1243  SCLogDebug("filename with %%-sign: %s", str);
1244 
1245  p = SCStrdup(str);
1246  if (p == NULL)
1247  goto error;
1248  toks[tok++] = p;
1249 
1250  s = 0;
1251 
1252  if (i+1 < (int)strlen(filename)) {
1253  if (tok >= MAX_TOKS) {
1254  SCLogError("invalid filename option. Max 2 %%-sign options");
1255  goto error;
1256  }
1257 
1258  if (filename[i+1] != 'n' && filename[i+1] != 't' && filename[i+1] != 'i') {
1259  SCLogError(
1260  "invalid filename option. Valid %%-sign options: %%n, %%i and %%t");
1261  goto error;
1262  }
1263  str[0] = '%';
1264  str[1] = filename[i+1];
1265  str[2] = '\0';
1266  p = SCStrdup(str);
1267  if (p == NULL)
1268  goto error;
1269  toks[tok++] = p;
1270  i++;
1271  }
1272  }
1273  }
1274 
1275  if ((tok == 0) && (pl->mode == LOGMODE_MULTI)) {
1276  SCLogError("Invalid filename for multimode. Need at least one %%-sign option");
1277  goto error;
1278  }
1279 
1280  if (s) {
1281  if (tok >= MAX_TOKS) {
1282  SCLogError("invalid filename option. Max 3 %%-sign options");
1283  goto error;
1284 
1285  }
1286  str[s++] = '\0';
1287  p = SCStrdup(str);
1288  if (p == NULL)
1289  goto error;
1290  toks[tok++] = p;
1291  }
1292 
1293  /* finally, store tokens in the pl */
1294  for (i = 0; i < tok; i++) {
1295  if (toks[i] == NULL)
1296  goto error;
1297 
1298  SCLogDebug("toks[%d] %s", i, toks[i]);
1299  pl->filename_parts[i] = toks[i];
1300  }
1301  pl->filename_part_cnt = tok;
1302  }
1303  return 0;
1304 error:
1305  for (x = 0; x < MAX_TOKS; x++) {
1306  if (toks[x] != NULL)
1307  SCFree(toks[x]);
1308  }
1309  return -1;
1310 }
1311 
1312 /** \brief Fill in pcap logging struct from the provided ConfNode.
1313  * \param conf The configuration node for this output.
1314  * \retval output_ctx
1315  * */
1316 static OutputInitResult PcapLogInitCtx(ConfNode *conf)
1317 {
1318  OutputInitResult result = { NULL, false };
1319  int en;
1320  PCRE2_SIZE eo = 0;
1321 
1322  PcapLogData *pl = SCCalloc(1, sizeof(PcapLogData));
1323  if (unlikely(pl == NULL)) {
1324  FatalError("Failed to allocate Memory for PcapLogData");
1325  }
1326 
1327  pl->h = SCMalloc(sizeof(*pl->h));
1328  if (pl->h == NULL) {
1329  FatalError("Failed to allocate Memory for pcap header struct");
1330  }
1331 
1332  /* Set the defaults */
1333  pl->mode = LOGMODE_NORMAL;
1335  pl->use_ringbuffer = RING_BUFFER_MODE_DISABLED;
1336  pl->timestamp_format = TS_FORMAT_SEC;
1340 
1341  TAILQ_INIT(&pl->pcap_file_list);
1342 
1343  SCMutexInit(&pl->plog_lock, NULL);
1344 
1345  /* Initialize PCREs. */
1346  pcre_timestamp_code =
1347  pcre2_compile((PCRE2_SPTR8)timestamp_pattern, PCRE2_ZERO_TERMINATED, 0, &en, &eo, NULL);
1348  if (pcre_timestamp_code == NULL) {
1349  PCRE2_UCHAR errbuffer[256];
1350  pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
1351  FatalError(
1352  "Failed to compile \"%s\" at offset %d: %s", timestamp_pattern, (int)eo, errbuffer);
1353  }
1354  pcre_timestamp_match = pcre2_match_data_create_from_pattern(pcre_timestamp_code, NULL);
1355 
1356  /* conf params */
1357 
1358  const char *filename = NULL;
1359 
1360  if (conf != NULL) { /* To facilitate unit tests. */
1361  filename = ConfNodeLookupChildValue(conf, "filename");
1362  }
1363 
1364  if (filename == NULL)
1365  filename = DEFAULT_LOG_FILENAME;
1366 
1367  if ((pl->prefix = SCStrdup(filename)) == NULL) {
1368  exit(EXIT_FAILURE);
1369  }
1370 
1371  pl->suffix = "";
1372 
1373  pl->size_limit = DEFAULT_LIMIT;
1374  if (conf != NULL) {
1375  const char *s_limit = NULL;
1376  s_limit = ConfNodeLookupChildValue(conf, "limit");
1377  if (s_limit != NULL) {
1378  if (ParseSizeStringU64(s_limit, &pl->size_limit) < 0) {
1379  SCLogError("Failed to initialize pcap output, invalid limit: %s", s_limit);
1380  exit(EXIT_FAILURE);
1381  }
1382  if (pl->size_limit < 4096) {
1383  SCLogInfo("pcap-log \"limit\" value of %"PRIu64" assumed to be pre-1.2 "
1384  "style: setting limit to %"PRIu64"mb", pl->size_limit, pl->size_limit);
1385  uint64_t size = pl->size_limit * 1024 * 1024;
1386  pl->size_limit = size;
1387  } else if (pl->size_limit < MIN_LIMIT) {
1388  FatalError("Fail to initialize pcap-log output, limit less than "
1389  "allowed minimum of %d bytes.",
1390  MIN_LIMIT);
1391  }
1392  }
1393  }
1394 
1395  if (conf != NULL) {
1396  const char *s_mode = NULL;
1397  s_mode = ConfNodeLookupChildValue(conf, "mode");
1398  if (s_mode != NULL) {
1399  if (strcasecmp(s_mode, "multi") == 0) {
1400  pl->mode = LOGMODE_MULTI;
1401  } else if (strcasecmp(s_mode, "normal") != 0) {
1402  FatalError("log-pcap: invalid mode \"%s\". Valid options: \"normal\""
1403  "or \"multi\" mode ",
1404  s_mode);
1405  }
1406  }
1407 
1408  const char *s_dir = NULL;
1409  s_dir = ConfNodeLookupChildValue(conf, "dir");
1410  if (s_dir == NULL) {
1411  const char *log_dir = NULL;
1412  log_dir = ConfigGetLogDirectory();
1413 
1414  strlcpy(pl->dir, log_dir, sizeof(pl->dir));
1415  SCLogInfo("Using log dir %s", pl->dir);
1416  } else {
1417  if (PathIsAbsolute(s_dir)) {
1418  strlcpy(pl->dir,
1419  s_dir, sizeof(pl->dir));
1420  } else {
1421  const char *log_dir = NULL;
1422  log_dir = ConfigGetLogDirectory();
1423 
1424  snprintf(pl->dir, sizeof(pl->dir), "%s/%s",
1425  log_dir, s_dir);
1426  }
1427 
1428  struct stat stat_buf;
1429  if (stat(pl->dir, &stat_buf) != 0) {
1430  FatalError("The dir directory \"%s\" "
1431  "supplied doesn't exist. Shutting down the engine",
1432  pl->dir);
1433  }
1434  SCLogInfo("Using log dir %s", pl->dir);
1435  }
1436 
1437  const char *compression_str = ConfNodeLookupChildValue(conf,
1438  "compression");
1439 
1440  PcapLogCompressionData *comp = &pl->compression;
1441  if (compression_str == NULL || strcmp(compression_str, "none") == 0) {
1443  comp->buffer = NULL;
1444  comp->buffer_size = 0;
1445  comp->file = NULL;
1446  comp->pcap_buf = NULL;
1447  comp->pcap_buf_size = 0;
1448  comp->pcap_buf_wrapper = NULL;
1449  } else if (strcmp(compression_str, "lz4") == 0) {
1450 #ifdef HAVE_LIBLZ4
1451  pl->compression.format = PCAP_LOG_COMPRESSION_FORMAT_LZ4;
1452 
1453  /* Use SCFmemopen so we can make pcap_dump write to a buffer. */
1454 
1455  comp->pcap_buf_size = sizeof(struct pcap_file_header) +
1456  sizeof(struct pcap_pkthdr) + PCAP_SNAPLEN;
1457  comp->pcap_buf = SCMalloc(comp->pcap_buf_size);
1458  if (comp->pcap_buf == NULL) {
1459  SCLogError("SCMalloc failed: %s", strerror(errno));
1460  exit(EXIT_FAILURE);
1461  }
1462  comp->pcap_buf_wrapper = SCFmemopen(comp->pcap_buf,
1463  comp->pcap_buf_size, "w");
1464  if (comp->pcap_buf_wrapper == NULL) {
1465  SCLogError("SCFmemopen failed: %s", strerror(errno));
1466  exit(EXIT_FAILURE);
1467  }
1468 
1469  /* Set lz4 preferences. */
1470 
1471  memset(&comp->lz4f_prefs, '\0', sizeof(comp->lz4f_prefs));
1472  comp->lz4f_prefs.frameInfo.blockSizeID = LZ4F_max4MB;
1473  comp->lz4f_prefs.frameInfo.blockMode = LZ4F_blockLinked;
1474  if (ConfNodeChildValueIsTrue(conf, "lz4-checksum")) {
1475  comp->lz4f_prefs.frameInfo.contentChecksumFlag = 1;
1476  }
1477  else {
1478  comp->lz4f_prefs.frameInfo.contentChecksumFlag = 0;
1479  }
1480  intmax_t lvl = 0;
1481  if (ConfGetChildValueInt(conf, "lz4-level", &lvl)) {
1482  if (lvl > 16) {
1483  lvl = 16;
1484  } else if (lvl < 0) {
1485  lvl = 0;
1486  }
1487  } else {
1488  lvl = 0;
1489  }
1490  comp->lz4f_prefs.compressionLevel = lvl;
1491 
1492  /* Allocate resources for lz4. */
1493 
1494  LZ4F_errorCode_t errcode =
1495  LZ4F_createCompressionContext(&pl->compression.lz4f_context, 1);
1496 
1497  if (LZ4F_isError(errcode)) {
1498  SCLogError("LZ4F_createCompressionContext failed: %s", LZ4F_getErrorName(errcode));
1499  exit(EXIT_FAILURE);
1500  }
1501 
1502  /* Calculate the size of the lz4 output buffer. */
1503 
1504  comp->buffer_size = LZ4F_compressBound(comp->pcap_buf_size,
1505  &comp->lz4f_prefs);
1506 
1507  comp->buffer = SCMalloc(comp->buffer_size);
1508  if (unlikely(comp->buffer == NULL)) {
1509  FatalError("Failed to allocate memory for "
1510  "lz4 output buffer.");
1511  }
1512 
1513  comp->bytes_in_block = 0;
1514 
1515  /* Add the lz4 file extension to the log files. */
1516 
1517  pl->suffix = ".lz4";
1518 #else
1519  SCLogError("lz4 compression was selected "
1520  "in pcap-log, but suricata was not compiled with lz4 "
1521  "support.");
1522  PcapLogDataFree(pl);
1523  return result;
1524 #endif /* HAVE_LIBLZ4 */
1525  }
1526  else {
1527  SCLogError("Unsupported pcap-log "
1528  "compression format: %s",
1529  compression_str);
1530  PcapLogDataFree(pl);
1531  return result;
1532  }
1533 
1534  SCLogInfo("Selected pcap-log compression method: %s",
1535  compression_str ? compression_str : "none");
1536 
1537  const char *s_conditional = ConfNodeLookupChildValue(conf, "conditional");
1538  if (s_conditional != NULL) {
1539  if (strcasecmp(s_conditional, "alerts") == 0) {
1542  } else if (strcasecmp(s_conditional, "tag") == 0) {
1545  } else if (strcasecmp(s_conditional, "all") != 0) {
1546  FatalError("log-pcap: invalid conditional \"%s\". Valid options: \"all\", "
1547  "\"alerts\", or \"tag\" mode ",
1548  s_conditional);
1549  }
1550  }
1551 
1552  SCLogInfo(
1553  "Selected pcap-log conditional logging: %s", s_conditional ? s_conditional : "all");
1554  }
1555 
1556  if (ParseFilename(pl, filename) != 0)
1557  exit(EXIT_FAILURE);
1558 
1559  SCLogInfo("using %s logging", (pl->mode == LOGMODE_MULTI ? "multi" : "normal"));
1560 
1561  uint32_t max_file_limit = DEFAULT_FILE_LIMIT;
1562  if (conf != NULL) {
1563  const char *max_number_of_files_s = NULL;
1564  max_number_of_files_s = ConfNodeLookupChildValue(conf, "max-files");
1565  if (max_number_of_files_s != NULL) {
1566  if (StringParseUint32(&max_file_limit, 10, 0,
1567  max_number_of_files_s) == -1) {
1568  SCLogError("Failed to initialize "
1569  "pcap-log output, invalid number of files limit: %s",
1570  max_number_of_files_s);
1571  exit(EXIT_FAILURE);
1572  } else if (max_file_limit < 1) {
1573  FatalError("Failed to initialize pcap-log output, limit less than "
1574  "allowed minimum.");
1575  } else {
1576  pl->max_files = max_file_limit;
1577  pl->use_ringbuffer = RING_BUFFER_MODE_ENABLED;
1578  }
1579  }
1580  }
1581 
1582  const char *ts_format = NULL;
1583  if (conf != NULL) { /* To facilitate unit tests. */
1584  ts_format = ConfNodeLookupChildValue(conf, "ts-format");
1585  }
1586  if (ts_format != NULL) {
1587  if (strcasecmp(ts_format, "usec") == 0) {
1588  pl->timestamp_format = TS_FORMAT_USEC;
1589  } else if (strcasecmp(ts_format, "sec") != 0) {
1590  SCLogError("log-pcap ts_format specified %s is invalid must be"
1591  " \"sec\" or \"usec\"",
1592  ts_format);
1593  exit(EXIT_FAILURE);
1594  }
1595  }
1596 
1597  const char *use_stream_depth = NULL;
1598  if (conf != NULL) { /* To facilitate unit tests. */
1599  use_stream_depth = ConfNodeLookupChildValue(conf, "use-stream-depth");
1600  }
1601  if (use_stream_depth != NULL) {
1602  if (ConfValIsFalse(use_stream_depth)) {
1604  } else if (ConfValIsTrue(use_stream_depth)) {
1606  } else {
1607  FatalError("log-pcap use_stream_depth specified is invalid must be");
1608  }
1609  }
1610 
1611  const char *honor_pass_rules = NULL;
1612  if (conf != NULL) { /* To facilitate unit tests. */
1613  honor_pass_rules = ConfNodeLookupChildValue(conf, "honor-pass-rules");
1614  }
1615  if (honor_pass_rules != NULL) {
1616  if (ConfValIsFalse(honor_pass_rules)) {
1618  } else if (ConfValIsTrue(honor_pass_rules)) {
1620  } else {
1621  FatalError("log-pcap honor-pass-rules specified is invalid");
1622  }
1623  }
1624 
1625  /* create the output ctx and send it back */
1626 
1627  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
1628  if (unlikely(output_ctx == NULL)) {
1629  FatalError("Failed to allocate memory for OutputCtx.");
1630  }
1631  output_ctx->data = pl;
1632  output_ctx->DeInit = PcapLogFileDeInitCtx;
1633  g_pcap_data = pl;
1634 
1635  result.ctx = output_ctx;
1636  result.ok = true;
1637  return result;
1638 }
1639 
1640 static void PcapLogFileDeInitCtx(OutputCtx *output_ctx)
1641 {
1642  if (output_ctx == NULL)
1643  return;
1644 
1645  PcapLogData *pl = output_ctx->data;
1646 
1647  PcapFileName *pf = NULL;
1648  TAILQ_FOREACH(pf, &pl->pcap_file_list, next) {
1649  SCLogDebug("PCAP files left at exit: %s\n", pf->filename);
1650  }
1651  PcapLogDataFree(pl);
1652  SCFree(output_ctx);
1653 
1654  pcre2_code_free(pcre_timestamp_code);
1655  pcre2_match_data_free(pcre_timestamp_match);
1656 
1657  return;
1658 }
1659 
1660 /**
1661  * \brief Read the config set the file pointer, open the file
1662  *
1663  * \param PcapLogData.
1664  *
1665  * \retval -1 if failure
1666  * \retval 0 if succesful
1667  */
1668 static int PcapLogOpenFileCtx(PcapLogData *pl)
1669 {
1670  char *filename = NULL;
1671 
1673 
1674  if (pl->filename != NULL)
1675  filename = pl->filename;
1676  else {
1677  filename = SCMalloc(PATH_MAX);
1678  if (unlikely(filename == NULL)) {
1679  return -1;
1680  }
1681  pl->filename = filename;
1682  }
1683 
1684  /** get the time so we can have a filename with seconds since epoch */
1685  SCTime_t ts = TimeGet();
1686 
1687  /* Place to store the name of our PCAP file */
1688  PcapFileName *pf = SCCalloc(1, sizeof(PcapFileName));
1689  if (unlikely(pf == NULL)) {
1690  return -1;
1691  }
1692 
1693  if (pl->mode == LOGMODE_NORMAL) {
1694  int ret;
1695  /* create the filename to use */
1696  if (pl->timestamp_format == TS_FORMAT_SEC) {
1697  ret = snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32 "%s", pl->dir, pl->prefix,
1698  (uint32_t)SCTIME_SECS(ts), pl->suffix);
1699  } else {
1700  ret = snprintf(filename, PATH_MAX, "%s/%s.%" PRIu32 ".%" PRIu32 "%s", pl->dir,
1701  pl->prefix, (uint32_t)SCTIME_SECS(ts), (uint32_t)SCTIME_USECS(ts), pl->suffix);
1702  }
1703  if (ret < 0 || (size_t)ret >= PATH_MAX) {
1704  SCLogError("failed to construct path");
1705  goto error;
1706  }
1707  } else if (pl->mode == LOGMODE_MULTI) {
1708  if (pl->filename_part_cnt > 0) {
1709  /* assemble filename from stored tokens */
1710 
1711  strlcpy(filename, pl->dir, PATH_MAX);
1712  strlcat(filename, "/", PATH_MAX);
1713 
1714  int i;
1715  for (i = 0; i < pl->filename_part_cnt; i++) {
1716  if (pl->filename_parts[i] == NULL ||strlen(pl->filename_parts[i]) == 0)
1717  continue;
1718 
1719  /* handle variables */
1720  if (pl->filename_parts[i][0] == '%') {
1721  char str[64] = "";
1722  if (strlen(pl->filename_parts[i]) < 2)
1723  continue;
1724 
1725  switch(pl->filename_parts[i][1]) {
1726  case 'n':
1727  snprintf(str, sizeof(str), "%u", pl->thread_number);
1728  break;
1729  case 'i':
1730  {
1731  long thread_id = SCGetThreadIdLong();
1732  snprintf(str, sizeof(str), "%"PRIu64, (uint64_t)thread_id);
1733  break;
1734  }
1735  case 't':
1736  /* create the filename to use */
1737  if (pl->timestamp_format == TS_FORMAT_SEC) {
1738  snprintf(str, sizeof(str), "%" PRIu32, (uint32_t)SCTIME_SECS(ts));
1739  } else {
1740  snprintf(str, sizeof(str), "%" PRIu32 ".%" PRIu32,
1741  (uint32_t)SCTIME_SECS(ts), (uint32_t)SCTIME_USECS(ts));
1742  }
1743  }
1744  strlcat(filename, str, PATH_MAX);
1745 
1746  /* copy the rest over */
1747  } else {
1748  strlcat(filename, pl->filename_parts[i], PATH_MAX);
1749  }
1750  }
1751  strlcat(filename, pl->suffix, PATH_MAX);
1752  } else {
1753  int ret;
1754  /* create the filename to use */
1755  if (pl->timestamp_format == TS_FORMAT_SEC) {
1756  ret = snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32 "%s", pl->dir, pl->prefix,
1757  pl->thread_number, (uint32_t)SCTIME_SECS(ts), pl->suffix);
1758  } else {
1759  ret = snprintf(filename, PATH_MAX, "%s/%s.%u.%" PRIu32 ".%" PRIu32 "%s", pl->dir,
1760  pl->prefix, pl->thread_number, (uint32_t)SCTIME_SECS(ts),
1761  (uint32_t)SCTIME_USECS(ts), pl->suffix);
1762  }
1763  if (ret < 0 || (size_t)ret >= PATH_MAX) {
1764  SCLogError("failed to construct path");
1765  goto error;
1766  }
1767  }
1768  SCLogDebug("multi-mode: filename %s", filename);
1769  }
1770 
1771  if ((pf->filename = SCStrdup(pl->filename)) == NULL) {
1772  SCLogError("Error allocating memory. For filename");
1773  goto error;
1774  }
1775  SCLogDebug("Opening pcap file log %s", pf->filename);
1776  TAILQ_INSERT_TAIL(&pl->pcap_file_list, pf, next);
1777 
1778  if (pl->mode == LOGMODE_MULTI || pl->mode == LOGMODE_NORMAL) {
1779  pcap_file_thread = pl->filename;
1780  }
1782  return 0;
1783 
1784 error:
1785  PcapFileNameFree(pf);
1786  return -1;
1787 }
1788 
1790 {
1791  /* return pcap filename per thread */
1792  if (pcap_file_thread != NULL) {
1793  return pcap_file_thread;
1794  }
1795  return NULL;
1796 }
1797 
1798 static int profiling_pcaplog_enabled = 0;
1799 static int profiling_pcaplog_output_to_file = 0;
1800 static char *profiling_pcaplog_file_name = NULL;
1801 static const char *profiling_pcaplog_file_mode = "a";
1802 
1803 static void FormatNumber(uint64_t num, char *str, size_t size)
1804 {
1805  if (num < 1000UL)
1806  snprintf(str, size, "%"PRIu64, num);
1807  else if (num < 1000000UL)
1808  snprintf(str, size, "%3.1fk", (float)num/1000UL);
1809  else if (num < 1000000000UL)
1810  snprintf(str, size, "%3.1fm", (float)num/1000000UL);
1811  else
1812  snprintf(str, size, "%3.1fb", (float)num/1000000000UL);
1813 }
1814 
1815 static void ProfileReportPair(FILE *fp, const char *name, PcapLogProfileData *p)
1816 {
1817  char ticks_str[32] = "n/a";
1818  char cnt_str[32] = "n/a";
1819  char avg_str[32] = "n/a";
1820 
1821  FormatNumber((uint64_t)p->cnt, cnt_str, sizeof(cnt_str));
1822  FormatNumber((uint64_t)p->total, ticks_str, sizeof(ticks_str));
1823  if (p->cnt && p->total)
1824  FormatNumber((uint64_t)(p->total/p->cnt), avg_str, sizeof(avg_str));
1825 
1826  fprintf(fp, "%-28s %-10s %-10s %-10s\n", name, cnt_str, avg_str, ticks_str);
1827 }
1828 
1829 static void ProfileReport(FILE *fp, PcapLogData *pl)
1830 {
1831  ProfileReportPair(fp, "open", &pl->profile_open);
1832  ProfileReportPair(fp, "close", &pl->profile_close);
1833  ProfileReportPair(fp, "write", &pl->profile_write);
1834  ProfileReportPair(fp, "rotate (incl open/close)", &pl->profile_rotate);
1835  ProfileReportPair(fp, "handles", &pl->profile_handles);
1836  ProfileReportPair(fp, "lock", &pl->profile_lock);
1837  ProfileReportPair(fp, "unlock", &pl->profile_unlock);
1838 }
1839 
1840 static void FormatBytes(uint64_t num, char *str, size_t size)
1841 {
1842  if (num < 1000UL)
1843  snprintf(str, size, "%"PRIu64, num);
1844  else if (num < 1048576UL)
1845  snprintf(str, size, "%3.1fKiB", (float)num/1000UL);
1846  else if (num < 1073741824UL)
1847  snprintf(str, size, "%3.1fMiB", (float)num/1000000UL);
1848  else
1849  snprintf(str, size, "%3.1fGiB", (float)num/1000000000UL);
1850 }
1851 
1852 static void PcapLogProfilingDump(PcapLogData *pl)
1853 {
1854  FILE *fp = NULL;
1855 
1856  if (profiling_pcaplog_enabled == 0)
1857  return;
1858 
1859  if (profiling_pcaplog_output_to_file == 1) {
1860  fp = fopen(profiling_pcaplog_file_name, profiling_pcaplog_file_mode);
1861  if (fp == NULL) {
1862  SCLogError("failed to open %s: %s", profiling_pcaplog_file_name, strerror(errno));
1863  return;
1864  }
1865  } else {
1866  fp = stdout;
1867  }
1868 
1869  /* counters */
1870  fprintf(fp, "\n\nOperation Cnt Avg ticks Total ticks\n");
1871  fprintf(fp, "---------------------------- ---------- ---------- -----------\n");
1872 
1873  ProfileReport(fp, pl);
1874  uint64_t total = pl->profile_write.total + pl->profile_rotate.total +
1877  pl->profile_unlock.total;
1878 
1879  /* overall stats */
1880  fprintf(fp, "\nOverall: %"PRIu64" bytes written, average %d bytes per write.\n",
1882  (int)(pl->profile_data_size / pl->profile_write.cnt) : 0);
1883  fprintf(fp, " PCAP data structure overhead: %"PRIuMAX" per write.\n",
1884  (uintmax_t)sizeof(struct pcap_pkthdr));
1885 
1886  /* print total bytes written */
1887  char bytes_str[32];
1888  FormatBytes(pl->profile_data_size, bytes_str, sizeof(bytes_str));
1889  fprintf(fp, " Size written: %s\n", bytes_str);
1890 
1891  /* ticks per MiB and GiB */
1892  uint64_t ticks_per_mib = 0, ticks_per_gib = 0;
1893  uint64_t mib = pl->profile_data_size/(1024*1024);
1894  if (mib)
1895  ticks_per_mib = total/mib;
1896  char ticks_per_mib_str[32] = "n/a";
1897  if (ticks_per_mib > 0)
1898  FormatNumber(ticks_per_mib, ticks_per_mib_str, sizeof(ticks_per_mib_str));
1899  fprintf(fp, " Ticks per MiB: %s\n", ticks_per_mib_str);
1900 
1901  uint64_t gib = pl->profile_data_size/(1024*1024*1024);
1902  if (gib)
1903  ticks_per_gib = total/gib;
1904  char ticks_per_gib_str[32] = "n/a";
1905  if (ticks_per_gib > 0)
1906  FormatNumber(ticks_per_gib, ticks_per_gib_str, sizeof(ticks_per_gib_str));
1907  fprintf(fp, " Ticks per GiB: %s\n", ticks_per_gib_str);
1908 
1909  if (fp != stdout)
1910  fclose(fp);
1911 }
1912 
1914 {
1915  ConfNode *conf = ConfGetNode("profiling.pcap-log");
1916  if (conf != NULL && ConfNodeChildValueIsTrue(conf, "enabled")) {
1917  profiling_pcaplog_enabled = 1;
1918  SCLogInfo("pcap-log profiling enabled");
1919 
1920  const char *filename = ConfNodeLookupChildValue(conf, "filename");
1921  if (filename != NULL) {
1922  const char *log_dir;
1923  log_dir = ConfigGetLogDirectory();
1924 
1925  profiling_pcaplog_file_name = SCMalloc(PATH_MAX);
1926  if (unlikely(profiling_pcaplog_file_name == NULL)) {
1927  FatalError("can't duplicate file name");
1928  }
1929 
1930  snprintf(profiling_pcaplog_file_name, PATH_MAX, "%s/%s", log_dir, filename);
1931 
1932  const char *v = ConfNodeLookupChildValue(conf, "append");
1933  if (v == NULL || ConfValIsTrue(v)) {
1934  profiling_pcaplog_file_mode = "a";
1935  } else {
1936  profiling_pcaplog_file_mode = "w";
1937  }
1938 
1939  profiling_pcaplog_output_to_file = 1;
1940  SCLogInfo("pcap-log profiling output goes to %s (mode %s)",
1941  profiling_pcaplog_file_name, profiling_pcaplog_file_mode);
1942  }
1943  }
1944 }
util-byte.h
PcapLogCompressionData_::pcap_buf_size
uint64_t pcap_buf_size
Definition: log-pcap.c:130
ConfGetChildValueInt
int ConfGetChildValueInt(const ConfNode *base, const char *name, intmax_t *val)
Definition: conf.c:434
PcapLogData
struct PcapLogData_ PcapLogData
PcapLogProfileData_
Definition: log-pcap.c:107
LOGMODE_COND_ALL
@ LOGMODE_COND_ALL
Definition: log-pcap.c:69
len
uint8_t len
Definition: app-layer-dnp3.h:2
ts
uint64_t ts
Definition: source-erf-file.c:55
PcapLogData_::conditional
LogModeConditionalType conditional
Definition: log-pcap.c:158
PcapLogThreadData
struct PcapLogThreadData_ PcapLogThreadData
ConfNodeChildValueIsTrue
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:859
MemBuffer_::buffer
uint8_t buffer[]
Definition: util-buffer.h:30
IsTcpSessionDumpingEnabled
bool IsTcpSessionDumpingEnabled(void)
Definition: stream-tcp-reassemble.c:89
util-fmemopen.h
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
USE_STREAM_DEPTH_DISABLED
#define USE_STREAM_DEPTH_DISABLED
Definition: log-pcap.c:80
PcapFileName_::secs
uint64_t secs
Definition: log-pcap.c:98
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
PcapLogData_::pcap_dumper
pcap_dumper_t * pcap_dumper
Definition: log-pcap.c:152
TS_FORMAT_SEC
#define TS_FORMAT_SEC
Definition: log-pcap.c:77
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:198
PcapFileName_::usecs
uint32_t usecs
Definition: log-pcap.c:99
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
PcapLogData_::filename
char * filename
Definition: log-pcap.c:146
PacketAlerts_::cnt
uint16_t cnt
Definition: decode.h:289
HONOR_PASS_RULES_ENABLED
#define HONOR_PASS_RULES_ENABLED
Definition: log-pcap.c:84
Packet_::flags
uint32_t flags
Definition: decode.h:473
ConfGetNode
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:181
DEFAULT_LOG_FILENAME
#define DEFAULT_LOG_FILENAME
Definition: log-pcap.c:59
threads.h
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:332
RING_BUFFER_MODE_ENABLED
#define RING_BUFFER_MODE_ENABLED
Definition: log-pcap.c:75
PcapLogData_::mode
int mode
Definition: log-pcap.c:147
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:248
PcapLogData_::pkt_cnt
uint64_t pkt_cnt
Definition: log-pcap.c:144
HONOR_PASS_RULES_DISABLED
#define HONOR_PASS_RULES_DISABLED
Definition: log-pcap.c:83
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
PcapLogData_::use_stream_depth
int use_stream_depth
Definition: log-pcap.c:141
PcapLogCompressionData_::format
enum PcapLogCompressionFormat format
Definition: log-pcap.c:121
PcapLogProfileData
struct PcapLogProfileData_ PcapLogProfileData
stream-tcp-reassemble.h
PcapLogData_::profile_handles
PcapLogProfileData profile_handles
Definition: log-pcap.c:163
LOGMODE_MULTI
#define LOGMODE_MULTI
Definition: log-pcap.c:66
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
PcapLogRegister
void PcapLogRegister(void)
Definition: log-pcap.c:210
SCFmemopen
#define SCFmemopen
Definition: util-fmemopen.h:52
PcapLogCompressionData_::buffer
uint8_t * buffer
Definition: log-pcap.c:122
DEFAULT_LIMIT
#define DEFAULT_LIMIT
Definition: log-pcap.c:62
PcapLogCallbackContext::connp
PcapLogCompressionData * connp
Definition: log-pcap.c:527
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:85
IsRunModeOffline
bool IsRunModeOffline(enum RunModes run_mode_to_check)
Definition: runmodes.c:574
PcapLogData_::pcap_dead_handle
pcap_t * pcap_dead_handle
Definition: log-pcap.c:151
Packet_::alerts
PacketAlerts alerts
Definition: decode.h:601
PacketTunnelChild
@ PacketTunnelChild
Definition: decode.h:413
TcpSegmentPcapHdrStorage_::pktlen
uint32_t pktlen
Definition: stream-tcp-private.h:67
PcapLogData_::size_current
uint64_t size_current
Definition: log-pcap.c:149
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:537
PcapLogData_::profile_rotate
PcapLogProfileData profile_rotate
Definition: log-pcap.c:166
OutputCtx_::data
void * data
Definition: tm-modules.h:88
PcapLogCompressionData_
Definition: log-pcap.c:120
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:84
PCAPLOG_PROFILE_END
#define PCAPLOG_PROFILE_END(prof)
Definition: log-pcap.c:224
PcapLogGetFilename
char * PcapLogGetFilename(void)
Definition: log-pcap.c:1789
OutputCtx_
Definition: tm-modules.h:85
LogModeConditionalType_
LogModeConditionalType_
Definition: log-pcap.c:68
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
PcapLogCompressionData_::bytes_in_block
uint64_t bytes_in_block
Definition: log-pcap.c:132
TAILQ_ENTRY
#define TAILQ_ENTRY(type)
Definition: queue.h:239
Packet_::datalink
int datalink
Definition: decode.h:620
PcapLogData_::is_private
bool is_private
Definition: log-pcap.c:156
PcapLogData_::size_limit
uint64_t size_limit
Definition: log-pcap.c:150
PKT_IS_TCP
#define PKT_IS_TCP(p)
Definition: decode.h:247
PcapLogProfileData_::cnt
uint64_t cnt
Definition: log-pcap.c:109
PcapLogData_::max_files
uint32_t max_files
Definition: log-pcap.c:155
PcapLogCompressionData_::file
FILE * file
Definition: log-pcap.c:128
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
decode.h
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
OutputInitResult_::ctx
OutputCtx * ctx
Definition: output.h:47
PCAP_LOG_COMPRESSION_FORMAT_NONE
@ PCAP_LOG_COMPRESSION_FORMAT_NONE
Definition: log-pcap.c:116
strlcat
size_t strlcat(char *, const char *src, size_t siz)
Definition: util-strlcatu.c:45
util-cpu.h
Packet_::ts
SCTime_t ts
Definition: decode.h:484
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
PKT_PSEUDO_STREAM_END
#define PKT_PSEUDO_STREAM_END
Definition: decode.h:1020
PCAP_LOG_COMPRESSION_FORMAT_LZ4
@ PCAP_LOG_COMPRESSION_FORMAT_LZ4
Definition: log-pcap.c:117
EnableTcpSessionDumping
void EnableTcpSessionDumping(void)
Definition: stream-tcp-reassemble.c:94
STREAM_DUMP_HEADERS
#define STREAM_DUMP_HEADERS
Definition: stream.h:34
GET_PKT_DATA
#define GET_PKT_DATA(p)
Definition: decode.h:220
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
PcapLogProfileData_::total
uint64_t total
Definition: log-pcap.c:108
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:313
util-time.h
OutputInitResult_::ok
bool ok
Definition: output.h:48
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
PcapFileName_::dirname
char * dirname
Definition: log-pcap.c:93
PcapLogData_::file_cnt
uint32_t file_cnt
Definition: log-pcap.c:154
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
util-profiling.h
PCAP_OUTPUT_BUFFER_SIZE
#define PCAP_OUTPUT_BUFFER_SIZE
Definition: log-pcap.h:31
stream.h
PcapLogCallbackContext::buf
MemBuffer * buf
Definition: log-pcap.c:528
TcpSegment
Definition: stream-tcp-private.h:72
Packet_
Definition: decode.h:436
SCLocalTime
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:267
GET_PKT_LEN
#define GET_PKT_LEN(p)
Definition: decode.h:219
TimeGet
SCTime_t TimeGet(void)
Definition: util-time.c:152
DEFAULT_FILE_LIMIT
#define DEFAULT_FILE_LIMIT
Definition: log-pcap.c:63
SCTime_t
Definition: util-time.h:40
TmEcode
TmEcode
Definition: tm-threads-common.h:83
RING_BUFFER_MODE_DISABLED
#define RING_BUFFER_MODE_DISABLED
Definition: log-pcap.c:74
PcapLogThreadData_::buf
MemBuffer * buf
Definition: log-pcap.c:189
PCAP_SNAPLEN
#define PCAP_SNAPLEN
Definition: log-pcap.c:86
PcapLogData_::plog_lock
SCMutex plog_lock
Definition: log-pcap.c:143
PcapLogData_::h
struct pcap_pkthdr * h
Definition: log-pcap.c:145
PcapLogCompressionData_::pcap_buf
uint8_t * pcap_buf
Definition: log-pcap.c:129
MemBuffer_
Definition: util-buffer.h:27
LOGMODE_COND_TAG
@ LOGMODE_COND_TAG
Definition: log-pcap.c:71
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:224
StreamSegmentForSession
int StreamSegmentForSession(const Packet *p, uint8_t flag, StreamSegmentCallback CallbackFunc, void *data)
Run callback for all segments on both directions of the session.
Definition: stream.c:64
PcapLogCallbackContext::pl
PcapLogData * pl
Definition: log-pcap.c:526
PcapLogData_::prev_day
int prev_day
Definition: log-pcap.c:148
PKT_FIRST_TAG
#define PKT_FIRST_TAG
Definition: decode.h:1068
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
PCAPLOG_PROFILE_START
#define PCAPLOG_PROFILE_START
Definition: log-pcap.c:221
log-pcap.h
SCGetThreadIdLong
#define SCGetThreadIdLong(...)
Definition: threads.h:255
PcapLogData_::honor_pass_rules
int honor_pass_rules
Definition: log-pcap.c:142
pcap_file_thread
thread_local char * pcap_file_thread
Definition: log-pcap.c:105
PcapLogData_::profile_open
PcapLogProfileData profile_open
Definition: log-pcap.c:165
PcapLogData_::profile_lock
PcapLogProfileData profile_lock
Definition: log-pcap.c:160
OutputInitResult_
Definition: output.h:46
util-conf.h
Packet_::flow
struct Flow_ * flow
Definition: decode.h:475
StringParseUint64
int StringParseUint64(uint64_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:308
TimeDifferenceMicros
uint64_t TimeDifferenceMicros(struct timeval t0, struct timeval t1)
Definition: util-time.c:644
LOGGER_PCAP
@ LOGGER_PCAP
Definition: suricata-common.h:489
suricata-common.h
OutputCtx_::DeInit
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:91
util-path.h
TcpSegmentPcapHdrStorage_::ts
struct timeval ts
Definition: stream-tcp-private.h:66
LOGMODE_NORMAL
#define LOGMODE_NORMAL
Definition: log-pcap.c:65
MemBufferFree
void MemBufferFree(MemBuffer *buffer)
Definition: util-buffer.c:81
PKT_STREAM_NOPCAPLOG
#define PKT_STREAM_NOPCAPLOG
Definition: decode.h:1029
PcapLogData_::profile_data_size
uint64_t profile_data_size
Definition: log-pcap.c:153
SCTIME_SECS
#define SCTIME_SECS(t)
Definition: util-time.h:57
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:44
Packet_::ttype
enum PacketTunnelType ttype
Definition: decode.h:482
PcapLogProfileSetup
void PcapLogProfileSetup(void)
Definition: log-pcap.c:1913
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
FatalError
#define FatalError(...)
Definition: util-debug.h:502
PcapLogCallbackContext
Definition: log-pcap.c:525
PcapLogCompressionData_::buffer_size
uint64_t buffer_size
Definition: log-pcap.c:123
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
OutputRegisterPacketModule
void OutputRegisterPacketModule(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Register a packet output module.
Definition: output.c:178
PcapLogThreadData_
Definition: log-pcap.c:187
RunmodeGetCurrent
int RunmodeGetCurrent(void)
Definition: suricata.c:261
threadvars.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
ConfigGetLogDirectory
const char * ConfigGetLogDirectory(void)
Definition: util-conf.c:38
LOGMODE_COND_ALERTS
@ LOGMODE_COND_ALERTS
Definition: log-pcap.c:70
PcapFileName_::filename
char * filename
Definition: log-pcap.c:92
Packet_::root
struct Packet_ * root
Definition: decode.h:634
TcpSegmentPcapHdrStorage_::pkt_hdr
uint8_t * pkt_hdr
Definition: stream-tcp-private.h:69
str
#define str(s)
Definition: suricata-common.h:291
MAX_TOKS
#define MAX_TOKS
Definition: log-pcap.c:112
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
PcapLogCompressionData
struct PcapLogCompressionData_ PcapLogCompressionData
MODULE_NAME
#define MODULE_NAME
Definition: log-pcap.c:60
TcpSegment::pcap_hdr_storage
TcpSegmentPcapHdrStorage * pcap_hdr_storage
Definition: stream-tcp-private.h:78
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
SC_ATOMIC_DECLARE
SC_ATOMIC_DECLARE(uint32_t, thread_cnt)
PcapLogCompressionData_::pcap_buf_wrapper
FILE * pcap_buf_wrapper
Definition: log-pcap.c:131
src
uint16_t src
Definition: app-layer-dnp3.h:5
util-buffer.h
PcapFileName_
Definition: log-pcap.c:91
ConfValIsFalse
int ConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:562
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
PcapLogData_::profile_unlock
PcapLogProfileData profile_unlock
Definition: log-pcap.c:162
USE_STREAM_DEPTH_ENABLED
#define USE_STREAM_DEPTH_ENABLED
Definition: log-pcap.c:81
MemBufferWriteRaw
uint32_t MemBufferWriteRaw(MemBuffer *dst, const uint8_t *raw, const uint32_t raw_len)
Write a raw buffer to the MemBuffer dst.
Definition: util-buffer.c:112
PcapLogData_::profile_close
PcapLogProfileData profile_close
Definition: log-pcap.c:164
PKT_NOPACKET_INSPECTION
#define PKT_NOPACKET_INSPECTION
Definition: decode.h:1003
FlowHasAlerts
int FlowHasAlerts(const Flow *f)
Check if flow has alerts.
Definition: flow.c:184
PcapFileName
struct PcapFileName_ PcapFileName
PCAP_BUFFER_TIMEOUT
#define PCAP_BUFFER_TIMEOUT
Definition: log-pcap.c:87
MIN_LIMIT
#define MIN_LIMIT
Definition: log-pcap.c:61
dst
uint16_t dst
Definition: app-layer-dnp3.h:4
util-misc.h
PKT_HAS_TAG
#define PKT_HAS_TAG
Definition: decode.h:1011
PcapLogData_
Definition: log-pcap.c:140
PcapLogThreadData_::pcap_log
PcapLogData * pcap_log
Definition: log-pcap.c:188
PKT_FIRST_ALERTS
#define PKT_FIRST_ALERTS
Definition: decode.h:1067
SCLogNotice
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition: util-debug.h:237
TAILQ_INSERT_BEFORE
#define TAILQ_INSERT_BEFORE(listelm, elm, field)
Definition: queue.h:277
PcapLogData_::profile_write
PcapLogProfileData profile_write
Definition: log-pcap.c:161
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
TS_FORMAT_USEC
#define TS_FORMAT_USEC
Definition: log-pcap.c:78
SCMutex
#define SCMutex
Definition: threads-debug.h:114
MAX_FILENAMELEN
#define MAX_FILENAMELEN
Definition: log-pcap.c:113
MemBufferCreateNew
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition: util-buffer.c:32
output.h
LogModeConditionalType
enum LogModeConditionalType_ LogModeConditionalType
PcapLogCompressionFormat
PcapLogCompressionFormat
Definition: log-pcap.c:115
SCTIME_USECS
#define SCTIME_USECS(t)
Definition: util-time.h:56
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:814