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