wmem: allow wmem_destroy_list to ignore a NULL list.
[metze/wireshark/wip.git] / dumpcap.c
index 10832d5fc6bf0096477a25a995759098e0856fb1..26f9c08d90d80dfc968b15d31a1145f23acf439b 100644 (file)
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -40,9 +40,9 @@
 #include <signal.h>
 #include <errno.h>
 
-#include <wsutil/cmdarg_err.h>
-#include <wsutil/crash_info.h>
+#include <ui/cmdarg_err.h>
 #include <wsutil/strtoi.h>
+#include <cli_main.h>
 #include <version_info.h>
 
 #ifndef HAVE_GETOPT_LONG
 
 #include "writecap/pcapio.h"
 
-#ifdef _WIN32
-#include <wsutil/unicode-utils.h>
-#endif
-
 #ifndef _WIN32
 #include <sys/un.h>
 #endif
 
-#include <wsutil/clopts_common.h>
+#include <ui/clopts_common.h>
 #include <wsutil/privileges.h>
 
 #include "sync_pipe.h"
@@ -82,9 +78,6 @@
 #include <capchild/capture_session.h>
 #include <capchild/capture_sync.h>
 
-#include "conditions.h"
-#include "capture_stop_conditions.h"
-
 #include "wsutil/tempfile.h"
 #include "log.h"
 #include "wsutil/file_util.h"
@@ -143,7 +136,7 @@ static gboolean infoprint;      /* if TRUE, print capture info after clearing in
 /** Stop a low-level capture (stops the capture child). */
 static void capture_loop_stop(void);
 /** Close a pipe, or socket if \a from_socket is TRUE */
-static void cap_pipe_close(int pipe_fd, gboolean from_socket _U_);
+static void cap_pipe_close(int pipe_fd, gboolean from_socket);
 
 #if !defined (__linux__)
 #ifndef HAVE_PCAP_BREAKLOOP
@@ -223,14 +216,14 @@ typedef enum {
 } cap_pipe_err_t;
 
 typedef struct _pcap_pipe_info {
-    struct pcap_hdr              hdr;       /**< Pcap header when capturing from a pipe */
-    struct pcaprec_modified_hdr  rechdr;    /**< Pcap record header when capturing from a pipe */
+    gboolean                     byte_swapped; /**< TRUE if data in the pipe is byte swapped. */
+    struct pcap_hdr              hdr;          /**< Pcap header when capturing from a pipe */
+    struct pcaprec_modified_hdr  rechdr;       /**< Pcap record header when capturing from a pipe */
 } pcap_pipe_info_t;
 
 typedef struct _pcapng_pipe_info {
     struct pcapng_block_header_s         bh;  /**< Pcapng general block header when capturing from a pipe */
-    struct pcapng_section_header_block_s shb; /**< Pcapng section header when capturing from a pipe */
-    GList  *saved_blocks;                     /**< Pcapng block list of SHB and IDBs for multi_file_on */
+    GArray *src_iface_to_global;               /**< Int array mapping local IDB numbers to global_ld.interface_data */
 } pcapng_pipe_info_t;
 
 struct _loop_data; /* forward declaration so we can use it in the cap_pipe_dispatch function pointer */
@@ -265,7 +258,6 @@ typedef struct _capture_src {
 #endif
     int                          cap_pipe_fd;            /**< the file descriptor of the capture pipe */
     gboolean                     cap_pipe_modified;      /**< TRUE if data in the pipe uses modified pcap headers */
-    gboolean                     cap_pipe_byte_swapped;  /**< TRUE if data in the pipe is byte swapped */
     char *                       cap_pipe_databuf;       /**< Pointer to the data buffer we've allocated */
     size_t                       cap_pipe_databuf_size;  /**< Current size of the data buffer */
     guint                        cap_pipe_max_pkt_size;  /**< Maximum packet size allowed */
@@ -277,7 +269,7 @@ typedef struct _capture_src {
     size_t                       cap_pipe_bytes_to_read; /**< Used by cap_pipe_dispatch */
     size_t                       cap_pipe_bytes_read;    /**< Used by cap_pipe_dispatch */
 #endif
-    int (*cap_pipe_dispatch)(struct _loop_data *, struct _capture_src *, char *, int);
+    int (*cap_pipe_dispatch)(struct _loop_data *, struct _capture_src *, char *, size_t);
     cap_pipe_state_t cap_pipe_state;
     cap_pipe_err_t cap_pipe_err;
 
@@ -287,6 +279,13 @@ typedef struct _capture_src {
 #endif
 } capture_src;
 
+typedef struct _saved_idb {
+    gboolean deleted;
+    guint interface_id; /* capture_src->interface_id for the associated SHB */
+    guint8 *idb;        /* If non-NULL, IDB read from capture_src. This is an interface specified on the command line otherwise. */
+    guint idb_len;
+} saved_idb_t;
+
 /*
  * Global capture loop state.
  */
@@ -294,18 +293,27 @@ typedef struct _loop_data {
     /* common */
     gboolean  go;                  /**< TRUE as long as we're supposed to keep capturing */
     int       err;                 /**< if non-zero, error seen while capturing */
-    gint      packet_count;        /**< Number of packets we have already captured */
-    gint      packet_max;          /**< Number of packets we're supposed to capture - 0 means infinite */
+    gint      packets_captured;    /**< Number of packets we have already captured */
     guint     inpkts_to_sync_pipe; /**< Packets not already send out to the sync_pipe */
 #ifdef SIGINFO
     gboolean  report_packet_count; /**< Set by SIGINFO handler; print packet count */
 #endif
     GArray   *pcaps;               /**< Array of capture_src's on which we're capturing */
+    gboolean  pcapng_passthrough;  /**< We have one source and it's pcapng. Pass its SHB and IDBs through. */
+    guint8   *saved_shb;           /**< SHB to write when we have one pcapng input */
+    GArray   *saved_idbs;          /**< Array of saved_idb_t, written when we have a new section or output file. */
+    GRWLock   saved_shb_idb_lock;  /**< Saved IDB RW mutex */
     /* output file(s) */
     FILE     *pdh;
     int       save_file_fd;
-    guint64   bytes_written;
-    guint32   autostop_files;
+    guint64   bytes_written;       /**< Bytes written for the current file. */
+    /* autostop conditions */
+    int       packets_written;     /**< Packets written for the current file. */
+    int       file_count;
+    /* ring buffer conditions */
+    GTimer  *file_duration_timer;
+    time_t   next_interval_time;
+    int      interval_s;
 } loop_data;
 
 typedef struct _pcap_queue_element {
@@ -327,7 +335,7 @@ static const char please_report[] =
 
 /*
  * This needs to be static, so that the SIGINT handler can clear the "go"
- * flag.
+ * flag and for saved_shb_idb_lock.
  */
 static loop_data   global_ld;
 
@@ -380,8 +388,8 @@ static void capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_p
                                          const u_char *pd);
 static void capture_loop_queue_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
                                          const u_char *pd);
-static void capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, const u_char *pd);
-static void capture_loop_queue_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, const u_char *pd);
+static void capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd);
+static void capture_loop_queue_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd);
 static void capture_loop_get_errmsg(char *errmsg, size_t errmsglen,
                                     char *secondary_errmsg,
                                     size_t secondary_errmsglen,
@@ -448,16 +456,18 @@ print_usage(FILE *output)
     fprintf(output, "Stop conditions:\n");
     fprintf(output, "  -c <packet count>        stop after n packets (def: infinite)\n");
     fprintf(output, "  -a <autostop cond.> ...  duration:NUM - stop after NUM seconds\n");
-    fprintf(output, "                           filesize:NUM - stop this file after NUM KB\n");
+    fprintf(output, "                           filesize:NUM - stop this file after NUM kB\n");
     fprintf(output, "                              files:NUM - stop after NUM files\n");
+    fprintf(output, "                            packets:NUM - stop after NUM packets\n");
     /*fprintf(output, "\n");*/
     fprintf(output, "Output (files):\n");
     fprintf(output, "  -w <filename>            name of file to save (def: tempfile)\n");
     fprintf(output, "  -g                       enable group read access on the output file(s)\n");
     fprintf(output, "  -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
     fprintf(output, "                           interval:NUM - create time intervals of NUM secs\n");
-    fprintf(output, "                           filesize:NUM - switch to next file after NUM KB\n");
+    fprintf(output, "                           filesize:NUM - switch to next file after NUM kB\n");
     fprintf(output, "                              files:NUM - ringbuffer: replace after NUM files\n");
+    fprintf(output, "                            packets:NUM - ringbuffer: replace after NUM packets\n");
     fprintf(output, "  -n                       use pcapng format instead of pcap (default)\n");
     fprintf(output, "  -P                       use libpcap format instead of pcapng\n");
     fprintf(output, "  --capture-comment <comment>\n");
@@ -1092,7 +1102,7 @@ report_capture_count(gboolean reportit)
 {
     /* Don't print this if we're a capture child. */
     if (!capture_child && reportit) {
-        fprintf(stderr, "\rPackets captured: %d\n", global_ld.packet_count);
+        fprintf(stderr, "\rPackets captured: %d\n", global_ld.packets_captured);
         /* stderr could be line buffered */
         fflush(stderr);
     }
@@ -1198,7 +1208,7 @@ relinquish_privs_except_capture(void)
 /* Take care of byte order in the libpcap headers read from pipes.
  * (function taken from wiretap/libpcap.c) */
 static void
-cap_pipe_adjust_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
+cap_pipe_adjust_pcap_header(gboolean byte_swapped, struct pcap_hdr *hdr, struct pcaprec_hdr *rechdr)
 {
     if (byte_swapped) {
         /* Byte-swap the record header fields. */
@@ -1364,7 +1374,7 @@ cap_pipe_select(int pipe_fd)
 #define DEF_TCP_PORT 19000
 
 static int
-cap_open_socket(char *pipename, capture_src *pcap_src, char *errmsg, int errmsgl)
+cap_open_socket(char *pipename, capture_src *pcap_src, char *errmsg, size_t errmsgl)
 {
     char *sockname = pipename + 4;
     struct sockaddr_in sa;
@@ -1415,7 +1425,7 @@ cap_open_socket(char *pipename, capture_src *pcap_src, char *errmsg, int errmsgl
                       NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       (LPTSTR)&errorText, 0, NULL);
 #endif
-        g_snprintf(errmsg, errmsgl,
+        g_snprintf(errmsg, (gulong)errmsgl,
             "The capture session could not be initiated due to the socket error: \n"
 #ifdef _WIN32
             "         %d: %s", lastError, errorText ? (char *)errorText : "Unknown");
@@ -1435,7 +1445,7 @@ cap_open_socket(char *pipename, capture_src *pcap_src, char *errmsg, int errmsgl
     return fd;
 
 fail_invalid:
-    g_snprintf(errmsg, errmsgl,
+    g_snprintf(errmsg, (gulong)errmsgl,
         "The capture session could not be initiated because\n"
         "\"%s\" is not a valid socket specification", pipename);
     pcap_src->cap_pipe_err = PIPERR;
@@ -1446,26 +1456,32 @@ fail_invalid:
  * otherwise.
  */
 static void
-cap_pipe_close(int pipe_fd, gboolean from_socket _U_)
+cap_pipe_close(int pipe_fd, gboolean from_socket)
 {
 #ifdef _WIN32
     if (from_socket) {
         closesocket(pipe_fd);
     }
 #else
+    (void) from_socket; /* Mark unused, similar to Q_UNUSED */
     ws_close(pipe_fd);
 #endif
 }
 
-static int
-cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, int errmsgl)
+/** Read bytes from a capture source, which is assumed to be a pipe.
+ *
+ * Returns -1, or the number of bytes read similar to read(2).
+ * Sets pcap_src->cap_pipe_err on error or EOF.
+ */
+static ssize_t
+cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, size_t errmsgl)
 {
     int sel_ret;
     int fd = pcap_src->cap_pipe_fd;
 #ifdef _WIN32
     DWORD sz, bytes_read = 0;
 #else /* _WIN32 */
-    size_t sz, bytes_read = 0;
+    ssize_t sz, bytes_read = 0;
 #endif /* _WIN32 */
     ssize_t b;
 
@@ -1476,22 +1492,26 @@ cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, int errmsgl)
     sz = pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read;
     while (bytes_read < sz) {
         if (fd == -1) {
-            g_snprintf(errmsg, errmsgl, "Invalid file descriptor.");
+            g_snprintf(errmsg, (gulong)errmsgl, "Invalid file descriptor.");
+            pcap_src->cap_pipe_err = PIPNEXIST;
             return -1;
         }
 
         sel_ret = cap_pipe_select(fd);
         if (sel_ret < 0) {
-            g_snprintf(errmsg, errmsgl,
-                        "Unexpected error from select: %s.", g_strerror(errno));
+            g_snprintf(errmsg, (gulong)errmsgl,
+                       "Unexpected error from select: %s.", g_strerror(errno));
+            pcap_src->cap_pipe_err = PIPERR;
             return -1;
         } else if (sel_ret > 0) {
             b = cap_pipe_read(fd, pcap_src->cap_pipe_databuf+pcap_src->cap_pipe_bytes_read+bytes_read,
                               sz-bytes_read, pcap_src->from_cap_socket);
             if (b <= 0) {
-                if (b == 0)
-                    g_snprintf(errmsg, errmsgl, "End of file on pipe during cap_pipe_read.");
-                else {
+                if (b == 0) {
+                    g_snprintf(errmsg, (gulong)errmsgl,
+                               "End of file on pipe during cap_pipe_read.");
+                    pcap_src->cap_pipe_err = PIPEOF;
+                } else {
 #ifdef _WIN32
                     LPTSTR errorText = NULL;
                     int lastError = WSAGetLastError();
@@ -1501,14 +1521,18 @@ cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, int errmsgl)
                                 FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                 (LPTSTR)&errorText, 0, NULL);
-                    g_snprintf(errmsg, errmsgl, "Error on pipe data during cap_pipe_read: "
-                        "         %d: %s", lastError, errorText ? (char *)errorText : "Unknown");
+                    g_snprintf(errmsg, (gulong)errmsgl,
+                               "Error on pipe data during cap_pipe_read: "
+                               "         %d: %s", lastError,
+                               errorText ? (char *)errorText : "Unknown");
                     if (errorText)
                         LocalFree(errorText);
 #else
-                    g_snprintf(errmsg, errmsgl, "Error on pipe data during cap_pipe_read: %s.",
-                                g_strerror(errno));
+                    g_snprintf(errmsg, (gulong)errmsgl,
+                               "Error on pipe data during cap_pipe_read: %s.",
+                               g_strerror(errno));
 #endif
+                    pcap_src->cap_pipe_err = PIPERR;
                 }
                 return -1;
             }
@@ -1520,13 +1544,22 @@ cap_pipe_read_data_bytes(capture_src *pcap_src, char *errmsg, int errmsgl)
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_read_data_bytes read %lu of %lu",
           pcap_src->cap_pipe_bytes_read, pcap_src->cap_pipe_bytes_to_read);
 #endif
-    return 0;
+    return bytes_read;
 }
 
 /* Some forward declarations for breaking up cap_pipe_open_live for pcap and pcapng formats */
-static void pcap_pipe_open_live(int fd, capture_src *pcap_src, struct pcap_hdr *hdr, char *errmsg, int errmsgl);
-static void pcapng_pipe_open_live(int fd, capture_src *pcap_src, char *errmsg, int errmsgl);
-static int pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl);
+static void pcap_pipe_open_live(int fd, capture_src *pcap_src,
+                                struct pcap_hdr *hdr,
+                                char *errmsg, size_t errmsgl,
+                                char *secondary_errmsg, size_t secondary_errmsgl);
+static void pcapng_pipe_open_live(int fd, capture_src *pcap_src,
+                                  char *errmsg, size_t errmsgl);
+static int pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src,
+                                char *errmsg, size_t errmsgl);
+
+/* For problems that are probably Not Our Fault. */
+static char not_our_bug[] =
+    "Please report this to the developers of the program writing to the pipe.";
 
 /* Mimic pcap_open_live() for pipe captures
 
@@ -1539,7 +1572,8 @@ static void
 cap_pipe_open_live(char *pipename,
                    capture_src *pcap_src,
                    void *hdr,
-                   char *errmsg, int errmsgl)
+                   char *errmsg, size_t errmsgl,
+                   char *secondary_errmsg, size_t secondary_errmsgl)
 {
 #ifndef _WIN32
     ws_statb64         pipe_stat;
@@ -1584,7 +1618,7 @@ cap_pipe_open_live(char *pipename,
             if (errno == ENOENT || errno == ENOTDIR)
                 pcap_src->cap_pipe_err = PIPNEXIST;
             else {
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session could not be initiated "
                            "due to error getting information on pipe/socket: %s.", g_strerror(errno));
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1594,7 +1628,7 @@ cap_pipe_open_live(char *pipename,
         if (S_ISFIFO(pipe_stat.st_mode)) {
             fd = ws_open(pipename, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */);
             if (fd == -1) {
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session could not be initiated "
                            "due to error on pipe open: %s.", g_strerror(errno));
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1603,7 +1637,7 @@ cap_pipe_open_live(char *pipename,
         } else if (S_ISSOCK(pipe_stat.st_mode)) {
             fd = socket(AF_UNIX, SOCK_STREAM, 0);
             if (fd == -1) {
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session could not be initiated "
                            "due to error on socket create: %s.", g_strerror(errno));
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1635,7 +1669,7 @@ cap_pipe_open_live(char *pipename,
              */
             if (g_strlcpy(sa.sun_path, pipename, sizeof sa.sun_path) > sizeof sa.sun_path) {
                 /* Path name too long */
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session coud not be initiated "
                            "due to error on socket connect: Path name too long.");
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1644,7 +1678,7 @@ cap_pipe_open_live(char *pipename,
             }
             b = connect(fd, (struct sockaddr *)&sa, sizeof sa);
             if (b == -1) {
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session coud not be initiated "
                            "due to error on socket connect: %s.", g_strerror(errno));
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1659,7 +1693,7 @@ cap_pipe_open_live(char *pipename,
                  */
                 pcap_src->cap_pipe_err = PIPNEXIST;
             } else {
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session could not be initiated because\n"
                            "\"%s\" is neither an interface nor a socket nor a pipe.", pipename);
                 pcap_src->cap_pipe_err = PIPERR;
@@ -1682,7 +1716,7 @@ cap_pipe_open_live(char *pipename,
         g_free(pncopy);
 
         if (!pos) {
-            g_snprintf(errmsg, errmsgl,
+            g_snprintf(errmsg, (gulong)errmsgl,
                        "The capture session could not be initiated because\n"
                        "\"%s\" is neither an interface nor a pipe.", pipename);
             pcap_src->cap_pipe_err = PIPNEXIST;
@@ -1706,7 +1740,7 @@ cap_pipe_open_live(char *pipename,
             if (GetLastError() != ERROR_PIPE_BUSY) {
                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                               NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session on \"%s\" could not be started "
                            "due to error on pipe open: %s (error %lu).",
                            pipename, utf_16to8(err_str), GetLastError());
@@ -1718,7 +1752,7 @@ cap_pipe_open_live(char *pipename,
             if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) {
                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                              NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
-                g_snprintf(errmsg, errmsgl,
+                g_snprintf(errmsg, (gulong)errmsgl,
                            "The capture session on \"%s\" timed out during "
                            "pipe open: %s (error %lu).",
                            pipename, utf_16to8(err_str), GetLastError());
@@ -1749,8 +1783,9 @@ cap_pipe_open_live(char *pipename,
         while (bytes_read < sizeof magic) {
             sel_ret = cap_pipe_select(fd);
             if (sel_ret < 0) {
-                g_snprintf(errmsg, errmsgl,
-                           "Unexpected error from select: %s.", g_strerror(errno));
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Unexpected error from select: %s.",
+                           g_strerror(errno));
                 goto error;
             } else if (sel_ret > 0) {
                 b = cap_pipe_read(fd, ((char *)&magic)+bytes_read,
@@ -1762,9 +1797,11 @@ cap_pipe_open_live(char *pipename,
 
                 if (b <= 0) {
                     if (b == 0)
-                        g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open.");
+                        g_snprintf(errmsg, (gulong)errmsgl,
+                                   "End of file on pipe magic during open.");
                     else
-                        g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s.",
+                        g_snprintf(errmsg, (gulong)errmsgl,
+                                   "Error on pipe magic during open: %s.",
                                    g_strerror(errno));
                     goto error;
                 }
@@ -1788,9 +1825,11 @@ cap_pipe_open_live(char *pipename,
 
         if (pcap_src->cap_pipe_bytes_read <= 0) {
             if (pcap_src->cap_pipe_bytes_read == 0)
-                g_snprintf(errmsg, errmsgl, "End of file on pipe magic during open.");
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "End of file on pipe magic during open.");
             else
-                g_snprintf(errmsg, errmsgl, "Error on pipe magic during open: %s.",
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Error on pipe magic during open: %s.",
                            g_strerror(errno));
             goto error;
         }
@@ -1802,14 +1841,14 @@ cap_pipe_open_live(char *pipename,
     case PCAP_NSEC_MAGIC:
         /* Host that wrote it has our byte order, and was running
            a program using either standard or ss990417 libpcap. */
-        pcap_src->cap_pipe_byte_swapped = FALSE;
+        pcap_src->cap_pipe_info.pcap.byte_swapped = FALSE;
         pcap_src->cap_pipe_modified = FALSE;
         pcap_src->ts_nsec = magic == PCAP_NSEC_MAGIC;
         break;
     case PCAP_MODIFIED_MAGIC:
         /* Host that wrote it has our byte order, but was running
            a program using either ss990915 or ss991029 libpcap. */
-        pcap_src->cap_pipe_byte_swapped = FALSE;
+        pcap_src->cap_pipe_info.pcap.byte_swapped = FALSE;
         pcap_src->cap_pipe_modified = TRUE;
         break;
     case PCAP_SWAPPED_MAGIC:
@@ -1817,7 +1856,7 @@ cap_pipe_open_live(char *pipename,
         /* Host that wrote it has a byte order opposite to ours,
            and was running a program using either standard or
            ss990417 libpcap. */
-        pcap_src->cap_pipe_byte_swapped = TRUE;
+        pcap_src->cap_pipe_info.pcap.byte_swapped = TRUE;
         pcap_src->cap_pipe_modified = FALSE;
         pcap_src->ts_nsec = magic == PCAP_SWAPPED_NSEC_MAGIC;
         break;
@@ -1825,23 +1864,28 @@ cap_pipe_open_live(char *pipename,
         /* Host that wrote it out has a byte order opposite to
            ours, and was running a program using either ss990915
            or ss991029 libpcap. */
-        pcap_src->cap_pipe_byte_swapped = TRUE;
+        pcap_src->cap_pipe_info.pcap.byte_swapped = TRUE;
         pcap_src->cap_pipe_modified = TRUE;
         break;
     case BLOCK_TYPE_SHB:
         /* This isn't pcap, it's pcapng. */
         pcap_src->from_pcapng = TRUE;
         pcap_src->cap_pipe_dispatch = pcapng_pipe_dispatch;
+        pcap_src->cap_pipe_info.pcapng.src_iface_to_global = g_array_new(FALSE, FALSE, sizeof(guint32));
         global_capture_opts.use_pcapng = TRUE;      /* we can only output in pcapng format */
         pcapng_pipe_open_live(fd, pcap_src, errmsg, errmsgl);
         return;
     default:
         /* Not a pcap type we know about, or not pcap at all. */
-        g_snprintf(errmsg, errmsgl, "Unrecognized libpcap format or not libpcap data.");
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "Data written to the pipe is neither in a supported pcap format nor in pcapng format.");
+        g_snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
+                   not_our_bug);
         goto error;
     }
 
-    pcap_pipe_open_live(fd, pcap_src, (struct pcap_hdr *) hdr, errmsg, errmsgl);
+    pcap_pipe_open_live(fd, pcap_src, (struct pcap_hdr *) hdr, errmsg, errmsgl,
+                        secondary_errmsg, secondary_errmsgl);
     return;
 
 error:
@@ -1858,7 +1902,8 @@ static void
 pcap_pipe_open_live(int fd,
                     capture_src *pcap_src,
                     struct pcap_hdr *hdr,
-                    char *errmsg, int errmsgl)
+                    char *errmsg, size_t errmsgl,
+                    char *secondary_errmsg, size_t secondary_errmsgl)
 {
     size_t   bytes_read;
     ssize_t  b;
@@ -1872,8 +1917,9 @@ pcap_pipe_open_live(int fd,
         while (bytes_read < sizeof(struct pcap_hdr)) {
             sel_ret = cap_pipe_select(fd);
             if (sel_ret < 0) {
-                g_snprintf(errmsg, errmsgl,
-                           "Unexpected error from select: %s.", g_strerror(errno));
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Unexpected error from select: %s.",
+                           g_strerror(errno));
                 goto error;
             } else if (sel_ret > 0) {
                 b = cap_pipe_read(fd, ((char *)hdr)+bytes_read,
@@ -1881,10 +1927,14 @@ pcap_pipe_open_live(int fd,
                                   pcap_src->from_cap_socket);
                 if (b <= 0) {
                     if (b == 0)
-                        g_snprintf(errmsg, errmsgl, "End of file on pipe header during open.");
+                        g_snprintf(errmsg, (gulong)errmsgl,
+                                   "End of file on pipe header during open.");
                     else
-                        g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s.",
+                        g_snprintf(errmsg, (gulong)errmsgl,
+                                   "Error on pipe header during open: %s.",
                                    g_strerror(errno));
+                    g_snprintf(secondary_errmsg, (gulong)secondary_errmsgl,
+                               "%s", not_our_bug);
                     goto error;
                 }
                 bytes_read += b;
@@ -1900,16 +1950,20 @@ pcap_pipe_open_live(int fd,
         g_async_queue_pop(pcap_src->cap_pipe_done_q);
         if (pcap_src->cap_pipe_bytes_read <= 0) {
             if (pcap_src->cap_pipe_bytes_read == 0)
-                g_snprintf(errmsg, errmsgl, "End of file on pipe header during open.");
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "End of file on pipe header during open.");
             else
-                g_snprintf(errmsg, errmsgl, "Error on pipe header header during open: %s.",
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Error on pipe header header during open: %s.",
                            g_strerror(errno));
+            g_snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
+                       not_our_bug);
             goto error;
         }
     }
 #endif
 
-    if (pcap_src->cap_pipe_byte_swapped) {
+    if (pcap_src->cap_pipe_info.pcap.byte_swapped) {
         /* Byte-swap the header fields about which we care. */
         hdr->version_major = GUINT16_SWAP_LE_BE(hdr->version_major);
         hdr->version_minor = GUINT16_SWAP_LE_BE(hdr->version_minor);
@@ -1926,16 +1980,26 @@ pcap_pipe_open_live(int fd,
         pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_DBUS;
     } else
 #endif
+    if (pcap_src->linktype == 279) {             /* DLT_EBHSCR */
+        /*
+         * The maximum EBHSCR message size is 8MB, so allow packets up
+         * to that size.
+         */
+        pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_EBHSCR;
+    }
+    else {
         pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD;
+    }
 
     if (hdr->version_major < 2) {
-        g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format version %d.%d",
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "The old pcap format version %d.%d is not supported.",
                    hdr->version_major, hdr->version_minor);
+        g_snprintf(secondary_errmsg, (gulong)secondary_errmsgl, "%s",
+                   not_our_bug);
         goto error;
     }
 
-    pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
-    pcap_src->cap_pipe_err = PIPOK;
     pcap_src->cap_pipe_fd = fd;
     return;
 
@@ -1953,17 +2017,16 @@ error:
 static int
 pcapng_read_shb(capture_src *pcap_src,
                 char *errmsg,
-                int errmsgl)
+                size_t errmsgl)
 {
-    struct pcapng_block_header_s *bh = &pcap_src->cap_pipe_info.pcapng.bh;
-    struct pcapng_section_header_block_s *shb = &pcap_src->cap_pipe_info.pcapng.shb;
+    struct pcapng_section_header_block_s shb;
 
 #ifdef _WIN32
     if (pcap_src->from_cap_socket)
 #endif
     {
         pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_block_header_s) + sizeof(struct pcapng_section_header_block_s);
-        if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl)) {
+        if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
             return -1;
         }
     }
@@ -1976,9 +2039,11 @@ pcapng_read_shb(capture_src *pcap_src,
         g_async_queue_pop(pcap_src->cap_pipe_done_q);
         if (pcap_src->cap_pipe_bytes_read <= 0) {
             if (pcap_src->cap_pipe_bytes_read == 0)
-                g_snprintf(errmsg, errmsgl, "End of file on pipe section header during open.");
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "End of file on pipe section header during open.");
             else
-                g_snprintf(errmsg, errmsgl, "Error on pipe section header during open: %s.",
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Error on pipe section header during open: %s.",
                            g_strerror(errno));
             return -1;
         }
@@ -1986,31 +2051,37 @@ pcapng_read_shb(capture_src *pcap_src,
         pcap_src->cap_pipe_bytes_read = sizeof(struct pcapng_block_header_s) + sizeof(struct pcapng_section_header_block_s);
     }
 #endif
-    memcpy(shb, pcap_src->cap_pipe_databuf + sizeof(struct pcapng_block_header_s), sizeof(struct pcapng_section_header_block_s));
-    switch (shb->magic)
+    memcpy(&shb, pcap_src->cap_pipe_databuf + sizeof(struct pcapng_block_header_s), sizeof(struct pcapng_section_header_block_s));
+    switch (shb.magic)
     {
     case PCAPNG_MAGIC:
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng SHB MAGIC");
-        pcap_src->cap_pipe_byte_swapped = FALSE;
         break;
     case PCAPNG_SWAPPED_MAGIC:
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng SHB SWAPPED MAGIC");
-        pcap_src->cap_pipe_byte_swapped = TRUE;
-        break;
+        /*
+         * pcapng sources can contain all sorts of block types. Rather than add a bunch of
+         * complexity to this code (which is often privileged), punt and tell the user to
+         * swap bytes elsewhere.
+         */
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define OUR_ENDIAN "big"
+#define IFACE_ENDIAN "little"
+#else
+#define OUR_ENDIAN "little"
+#define IFACE_ENDIAN "big"
+#endif
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "Interface %u is " IFACE_ENDIAN " endian but we're " OUR_ENDIAN " endian.",
+                   pcap_src->interface_id);
+        return -1;
     default:
         /* Not a pcapng type we know about, or not pcapng at all. */
-        g_snprintf(errmsg, errmsgl, "Unrecognized pcapng format or not pcapng data.");
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "Unrecognized pcapng format or not pcapng data.");
         return -1;
     }
 
-    if (pcap_src->cap_pipe_byte_swapped) {
-        /* Byte-swap the header fields about which we care. */
-        shb->version_major = GUINT16_SWAP_LE_BE(shb->version_major);
-        shb->version_minor = GUINT16_SWAP_LE_BE(shb->version_minor);
-        shb->section_length = GUINT64_SWAP_LE_BE(shb->section_length);
-        bh->block_total_length = GUINT32_SWAP_LE_BE(bh->block_total_length);
-    }
-
     pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD;
 
     /* Setup state to capture any options following the section header block */
@@ -2019,44 +2090,93 @@ pcapng_read_shb(capture_src *pcap_src,
     return 0;
 }
 
-static int
-pcapng_write_saved_block(capture_src *pcap_src, struct pcapng_block_header_s *bh)
+/*
+ * Save IDB blocks for playback whenever we change output files.
+ * Rewrite EPB and ISB interface IDs.
+ */
+static gboolean
+pcapng_adjust_block(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd)
 {
-    guint32 length = bh->block_total_length;
+    switch(bh->block_type) {
+    case BLOCK_TYPE_SHB:
+    {
+        if (global_ld.pcapng_passthrough) {
+            /*
+             * We have a single pcapng input. We pass the SHB through when
+             * writing a single output file and for the first ring buffer
+             * file. We need to save it for the second and subsequent ring
+             * buffer files.
+             */
+            g_free(global_ld.saved_shb);
+            global_ld.saved_shb = (guint8 *) g_memdup(pd, bh->block_total_length);
 
-    if (pcap_src->cap_pipe_byte_swapped) {
-        length = GUINT32_SWAP_LE_BE(length);
+            /*
+             * We're dealing with one section at a time, so we can (and must)
+             * get rid of our old IDBs.
+             */
+            for (unsigned i = 0; i < global_ld.saved_idbs->len; i++) {
+                saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, i);
+                g_free(idb_source->idb);
+            }
+            g_array_set_size(global_ld.saved_idbs, 0);
+        } else {
+            /*
+             * We have a new SHB from this capture source. We need to keep
+             * global_ld.saved_idbs intact, so we mark IDBs we previously
+             * collected from this source as deleted.
+             */
+            for (unsigned i = 0; i < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len; i++) {
+                guint32 iface_id = g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, i);
+                saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, iface_id);
+                g_assert(idb_source->interface_id == pcap_src->interface_id);
+                g_free(idb_source->idb);
+                memset(idb_source, 0, sizeof(saved_idb_t));
+                idb_source->deleted = TRUE;
+                g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: deleted pcapng IDB %u", G_STRFUNC, iface_id);
+            }
+        }
+        g_array_set_size(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, 0);
     }
-
-    return pcapng_write_block(global_ld.pdh,
-                       (const guint8 *) bh,
-                       length,
-                       &global_ld.bytes_written, &global_ld.err);
-}
-
-/* Save SHB and IDB blocks to playback whenever we change output files. */
-/* The list is saved in reverse order of blocks added */
-static gboolean
-pcapng_block_save(capture_src *pcap_src)
-{
-    pcapng_pipe_info_t *pcapng = &pcap_src->cap_pipe_info.pcapng;
-    struct pcapng_block_header_s *bh = &pcapng->bh;
-
-    /* Delete all the old blocks first whenever we get a SHB */
-    if (bh->block_type == BLOCK_TYPE_SHB) {
-        g_list_free_full(pcapng->saved_blocks, g_free);
-        pcapng->saved_blocks = NULL;
-    } else if (bh->block_type != BLOCK_TYPE_IDB) {
-        return TRUE;
+        break;
+    case BLOCK_TYPE_IDB:
+    {
+        /*
+         * Always gather IDBs. We can remove them or mark them as deleted
+         * when we get a new SHB.
+         */
+        saved_idb_t idb_source = { 0 };
+        idb_source.interface_id = pcap_src->interface_id;
+        idb_source.idb_len = bh->block_total_length;
+        idb_source.idb = (guint8 *) g_memdup(pd, idb_source.idb_len);
+        g_array_append_val(global_ld.saved_idbs, idb_source);
+        guint32 iface_id = global_ld.saved_idbs->len - 1;
+        g_array_append_val(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, iface_id);
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: mapped pcapng IDB %u -> %u from source %u",
+              G_STRFUNC, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len - 1, iface_id, pcap_src->interface_id);
     }
-
-    gpointer data = g_malloc(bh->block_total_length);
-    if (data == NULL) {
-        return FALSE;
+        break;
+    case BLOCK_TYPE_EPB:
+    case BLOCK_TYPE_ISB:
+    {
+        if (global_ld.pcapng_passthrough) {
+            /* Our input and output interface IDs are the same. */
+            break;
+        }
+        /* The interface ID is the first 32-bit field after the BH for both EPBs and ISBs. */
+        guint32 iface_id;
+        memcpy(&iface_id, pd + sizeof(struct pcapng_block_header_s), 4);
+        if (iface_id < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len) {
+            memcpy(pd + sizeof(struct pcapng_block_header_s),
+                   &g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, iface_id), 4);
+        } else {
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: pcapng EPB or ISB interface id %u > max %u", G_STRFUNC, iface_id, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len);
+            return FALSE;
+        }
+    }
+        break;
+    default:
+        break;
     }
-    memcpy(data, pcap_src->cap_pipe_databuf, bh->block_total_length);
-
-    pcapng->saved_blocks = g_list_prepend(pcapng->saved_blocks, data);
 
     return TRUE;
 }
@@ -2065,7 +2185,7 @@ static void
 pcapng_pipe_open_live(int fd,
                       capture_src *pcap_src,
                       char *errmsg,
-                      int errmsgl)
+                      size_t errmsgl)
 {
     guint32 type = BLOCK_TYPE_SHB;
     struct pcapng_block_header_s *bh = &pcap_src->cap_pipe_info.pcapng.bh;
@@ -2079,9 +2199,8 @@ pcapng_pipe_open_live(int fd,
         /* read the rest of the pcapng general block header */
         pcap_src->cap_pipe_bytes_read = sizeof(guint32);
         pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_block_header_s);
-        pcap_src->cap_pipe_err = PIPOK;
         pcap_src->cap_pipe_fd = fd;
-        if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl)) {
+        if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
             goto error;
         }
         memcpy(bh, pcap_src->cap_pipe_databuf, sizeof(struct pcapng_block_header_s));
@@ -2099,15 +2218,16 @@ pcapng_pipe_open_live(int fd,
         g_async_queue_pop(pcap_src->cap_pipe_done_q);
         if (pcap_src->cap_pipe_bytes_read <= 0) {
             if (pcap_src->cap_pipe_bytes_read == 0)
-                g_snprintf(errmsg, errmsgl, "End of file on pipe block_total_length during open.");
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "End of file on pipe block_total_length during open.");
             else
-                g_snprintf(errmsg, errmsgl, "Error on pipe block_total_length during open: %s.",
+                g_snprintf(errmsg, (gulong)errmsgl,
+                           "Error on pipe block_total_length during open: %s.",
                            g_strerror(errno));
             goto error;
         }
         pcap_src->cap_pipe_bytes_read = sizeof(struct pcapng_block_header_s);
         memcpy(pcap_src->cap_pipe_databuf, bh, sizeof(struct pcapng_block_header_s));
-        pcap_src->cap_pipe_err = PIPOK;
         pcap_src->cap_pipe_fd = fd;
     }
 #endif
@@ -2130,7 +2250,7 @@ error:
 /* We read one record from the pipe, take care of byte order in the record
  * header, write the record to the capture file, and update capture statistics. */
 static int
-pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl)
+pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, size_t errmsgl)
 {
     struct pcap_pkthdr  phdr;
     enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
@@ -2260,7 +2380,8 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
         break;
 
     default:
-        g_snprintf(errmsg, errmsgl, "pcap_pipe_dispatch: invalid state");
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "pcap_pipe_dispatch: invalid state");
         result = PD_ERR;
 
     } /* switch (pcap_src->cap_pipe_state) */
@@ -2272,7 +2393,7 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
 
     case PD_REC_HDR_READ:
         /* We've read the header. Take care of byte order. */
-        cap_pipe_adjust_header(pcap_src->cap_pipe_byte_swapped, &pcap_info->hdr,
+        cap_pipe_adjust_pcap_header(pcap_src->cap_pipe_info.pcap.byte_swapped, &pcap_info->hdr,
                                &pcap_info->rechdr.hdr);
         if (pcap_info->rechdr.hdr.incl_len > pcap_src->cap_pipe_max_pkt_size) {
             /*
@@ -2281,8 +2402,8 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
              * STATE_EXPECT_DATA) as that would not fit in the buffer and
              * instead stop with an error.
              */
-            g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
-                       ld->packet_count+1, pcap_info->rechdr.hdr.incl_len);
+            g_snprintf(errmsg, (gulong)errmsgl, "Frame %u too long (%d bytes)",
+                       ld->packets_captured+1, pcap_info->rechdr.hdr.incl_len);
             break;
         }
 
@@ -2343,12 +2464,12 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
 #ifdef _WIN32
         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
-        g_snprintf(errmsg, errmsgl,
+        g_snprintf(errmsg, (gulong)errmsgl,
                    "Error reading from pipe: %s (error %lu)",
                    utf_16to8(err_str), GetLastError());
         LocalFree(err_str);
 #else
-        g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
+        g_snprintf(errmsg, (gulong)errmsgl, "Error reading from pipe: %s",
                    g_strerror(errno));
 #endif
         /* Fall through */
@@ -2362,7 +2483,7 @@ pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errms
 }
 
 static int
-pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl)
+pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, size_t errmsgl)
 {
     enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
            PD_ERR } result;
@@ -2408,7 +2529,7 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
 #ifdef _WIN32
         if (pcap_src->from_cap_socket) {
 #endif
-            if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl)) {
+            if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
                 return -1;
             }
 #ifdef _WIN32
@@ -2426,8 +2547,9 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
             }
         }
 #endif
-        if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read)
+        if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) {
             return 0;
+        }
         memcpy(bh, pcap_src->cap_pipe_databuf, sizeof(struct pcapng_block_header_s));
         result = PD_REC_HDR_READ;
         break;
@@ -2461,7 +2583,7 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
 #ifdef _WIN32
         if (pcap_src->from_cap_socket) {
 #endif
-            if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl)) {
+            if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
                 return -1;
             }
 #ifdef _WIN32
@@ -2487,7 +2609,8 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
         break;
 
     default:
-        g_snprintf(errmsg, errmsgl, "pcapng_pipe_dispatch: invalid state");
+        g_snprintf(errmsg, (gulong)errmsgl,
+                   "pcapng_pipe_dispatch: invalid state");
         result = PD_ERR;
 
     } /* switch (pcap_src->cap_pipe_state) */
@@ -2504,12 +2627,6 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
             return 1;
         }
 
-        /* We've read the header. Take care of byte order. */
-        if (pcap_src->cap_pipe_byte_swapped) {
-            /* Byte-swap the record header fields. */
-            bh->block_type = GUINT32_SWAP_LE_BE(bh->block_type);
-            bh->block_total_length = GUINT32_SWAP_LE_BE(bh->block_total_length);
-        }
         if (bh->block_total_length > pcap_src->cap_pipe_max_pkt_size) {
             /*
             * The record contains more data than the advertised/allowed in the
@@ -2517,8 +2634,8 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
             * STATE_EXPECT_DATA) as that would not fit in the buffer and
             * instead stop with an error.
             */
-            g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)",
-                    ld->packet_count+1, bh->block_total_length);
+            g_snprintf(errmsg, (gulong)errmsgl, "Frame %u too long (%d bytes)",
+                    ld->packets_captured+1, bh->block_total_length);
             break;
         }
 
@@ -2544,7 +2661,8 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
 
         /* The record always has at least the block total length following the header */
         if (bh->block_total_length < sizeof(struct pcapng_block_header_s)+sizeof(guint32)) {
-            g_snprintf(errmsg, errmsgl, "malformed pcapng block_total_length < minimum");
+            g_snprintf(errmsg, (gulong)errmsgl,
+                       "malformed pcapng block_total_length < minimum");
             pcap_src->cap_pipe_err = PIPEOF;
             return -1;
         }
@@ -2552,10 +2670,6 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
         return 0;
 
     case PD_DATA_READ:
-        if (!pcapng_block_save(pcap_src)) {
-            g_snprintf(errmsg, errmsgl, "pcapng_pipe_dispatch block save failed");
-            return -1;
-        }
         if (use_threads) {
             capture_loop_queue_pcapng_cb(pcap_src, bh, pcap_src->cap_pipe_databuf);
         } else {
@@ -2572,12 +2686,12 @@ pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int err
 #ifdef _WIN32
         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
-        g_snprintf(errmsg, errmsgl,
+        g_snprintf(errmsg, (gulong)errmsgl,
                    "Error reading from pipe: %s (error %lu)",
                    utf_16to8(err_str), GetLastError());
         LocalFree(err_str);
 #else
-        g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
+        g_snprintf(errmsg, (gulong)errmsgl, "Error reading from pipe: %s",
                    g_strerror(errno));
 #endif
         /* Fall through */
@@ -2663,15 +2777,28 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
         return FALSE;
     }
 
+    int pcapng_src_count = 0;
     for (i = 0; i < capture_opts->ifaces->len; i++) {
         interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
-        pcap_src = (capture_src *)g_malloc(sizeof (capture_src));
+        pcap_src = (capture_src *)g_malloc0(sizeof (capture_src));
         if (pcap_src == NULL) {
             g_snprintf(errmsg, (gulong) errmsg_len,
                    "Could not allocate memory.");
             return FALSE;
         }
-        memset(pcap_src, 0, sizeof(capture_src));
+
+        /*
+         * Add our pcapng interface entry. This will be deleted further
+         * down if pcapng_passthrough == TRUE.
+         */
+        saved_idb_t idb_source = { 0 };
+        idb_source.interface_id = i;
+        g_rw_lock_writer_lock (&ld->saved_shb_idb_lock);
+        g_array_append_val(global_ld.saved_idbs, idb_source);
+        g_rw_lock_writer_unlock (&ld->saved_shb_idb_lock);
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: saved capture_opts IDB %u",
+              G_STRFUNC, i);
+
 #ifdef MUST_DO_SELECT
         pcap_src->pcap_fd = -1;
 #endif
@@ -2747,7 +2874,10 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
             /* We couldn't open "iface" as a network device. */
             /* Try to open it as a pipe */
             gboolean pipe_err = FALSE;
-            cap_pipe_open_live(interface_opts->name, pcap_src, &pcap_src->cap_pipe_info.pcap.hdr, errmsg, (int) errmsg_len);
+            cap_pipe_open_live(interface_opts->name, pcap_src,
+                               &pcap_src->cap_pipe_info.pcap.hdr,
+                               errmsg, errmsg_len,
+                               secondary_errmsg, secondary_errmsg_len);
 
 #ifdef _WIN32
             if (pcap_src->from_cap_socket) {
@@ -2809,6 +2939,17 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
             report_capture_error(sync_msg_str, "");
             g_free(sync_msg_str);
         }
+        if (pcap_src->from_pcapng) {
+            pcapng_src_count++;
+        }
+    }
+    if (capture_opts->ifaces->len == 1 && pcapng_src_count == 1) {
+        ld->pcapng_passthrough = TRUE;
+        g_rw_lock_writer_lock (&ld->saved_shb_idb_lock);
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: Clearing %u interfaces for passthrough",
+              G_STRFUNC, global_ld.saved_idbs->len);
+        g_array_set_size(global_ld.saved_idbs, 0);
+        g_rw_lock_writer_unlock (&ld->saved_shb_idb_lock);
     }
 
     /* If not using libcap: we now can now set euid/egid to ruid/rgid         */
@@ -2853,8 +2994,8 @@ static void capture_loop_close_input(loop_data *ld)
                 pcap_src->cap_pipe_databuf = NULL;
             }
             if (pcap_src->from_pcapng) {
-                g_list_free_full(pcap_src->cap_pipe_info.pcapng.saved_blocks, g_free);
-                pcap_src->cap_pipe_info.pcapng.saved_blocks = NULL;
+                g_array_free(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, TRUE);
+                pcap_src->cap_pipe_info.pcapng.src_iface_to_global = NULL;
             }
         } else {
             /* Capture device.  If open, close the pcap_t. */
@@ -2907,15 +3048,122 @@ capture_loop_init_filter(pcap_t *pcap_h, gboolean from_cap_pipe,
     return INITFILTER_NO_ERROR;
 }
 
+/*
+ * Write the dumpcap pcapng SHB and IDBs if needed.
+ * Called from capture_loop_init_output and do_file_switch_or_stop.
+ */
+static gboolean
+capture_loop_init_pcapng_output(capture_options *capture_opts, loop_data *ld)
+{
+    g_rw_lock_reader_lock (&ld->saved_shb_idb_lock);
+
+    if (ld->pcapng_passthrough && !ld->saved_shb) {
+        /* We have a single pcapng capture interface and this is the first or only output file. */
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: skipping dumpcap SHB and IDBs in favor of source", G_STRFUNC);
+        g_rw_lock_reader_unlock (&ld->saved_shb_idb_lock);
+        return TRUE;
+    }
+
+    gboolean successful = TRUE;
+    int      err;
+    GString *os_info_str = g_string_new("");
+
+    get_os_version_info(os_info_str);
+
+    if (ld->saved_shb) {
+        /* We have a single pcapng capture interface and multiple output files. */
+
+        struct pcapng_block_header_s bh;
+
+        memcpy(&bh, ld->saved_shb, sizeof(struct pcapng_block_header_s));
+
+        successful = pcapng_write_block(ld->pdh, ld->saved_shb, bh.block_total_length, &ld->bytes_written, &err);
+
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: wrote saved passthrough SHB %d", G_STRFUNC, successful);
+    } else {
+        GString *cpu_info_str = g_string_new("");
+        get_cpu_info(cpu_info_str);
+
+        successful = pcapng_write_section_header_block(ld->pdh,
+                                                       (const char *)capture_opts->capture_comment,   /* Comment */
+                                                       cpu_info_str->str,           /* HW */
+                                                       os_info_str->str,            /* OS */
+                                                       get_appname_and_version(),
+                                                       -1,                          /* section_length */
+                                                       &ld->bytes_written,
+                                                       &err);
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: wrote dumpcap SHB %d", G_STRFUNC, successful);
+        g_string_free(cpu_info_str, TRUE);
+    }
+
+    for (unsigned i = 0; successful && (i < ld->saved_idbs->len); i++) {
+        saved_idb_t idb_source = g_array_index(ld->saved_idbs, saved_idb_t, i);
+        if (idb_source.deleted) {
+            /*
+             * Our interface is out of scope. Suppose we're writing multiple
+             * files and a source switches sections. We currently write dummy
+             * IDBs like so:
+             *
+             * File 1: IDB0, IDB1, IDB2
+             * [ The source of IDBs 1 and 2 writes an SHB with two new IDBs ]
+             * [ We switch output files ]
+             * File 2: IDB0, dummy IDB, dummy IDB, IDB3, IDB4
+             *
+             * It might make more sense to write the original data so that
+             * so that our IDB lists are more consistent across files.
+             */
+            successful = pcapng_write_interface_description_block(global_ld.pdh,
+                                                                  "Interface went out of scope",    /* OPT_COMMENT       1 */
+                                                                  "dummy",                          /* IDB_NAME          2 */
+                                                                  "Dumpcap dummy interface",        /* IDB_DESCRIPTION   3 */
+                                                                  NULL,                             /* IDB_FILTER       11 */
+                                                                  os_info_str->str,                 /* IDB_OS           12 */
+                                                                  -1,
+                                                                  0,
+                                                                  &(global_ld.bytes_written),
+                                                                  0,                                /* IDB_IF_SPEED      8 */
+                                                                  6,                                /* IDB_TSRESOL       9 */
+                                                                  &global_ld.err);
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: skipping deleted pcapng IDB %u", G_STRFUNC, i);
+        } else if (idb_source.idb && idb_source.idb_len) {
+            successful = pcapng_write_block(global_ld.pdh, idb_source.idb, idb_source.idb_len, &ld->bytes_written, &err);
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: wrote pcapng IDB %d", G_STRFUNC, successful);
+        } else if (idb_source.interface_id < capture_opts->ifaces->len) {
+            unsigned if_id = idb_source.interface_id;
+            interface_options *interface_opts = &g_array_index(capture_opts->ifaces, interface_options, if_id);
+            capture_src *pcap_src = g_array_index(ld->pcaps, capture_src *, if_id);
+            if (pcap_src->from_cap_pipe) {
+                pcap_src->snaplen = pcap_src->cap_pipe_info.pcap.hdr.snaplen;
+            } else {
+                pcap_src->snaplen = pcap_snapshot(pcap_src->pcap_h);
+            }
+            successful = pcapng_write_interface_description_block(global_ld.pdh,
+                                                                  NULL,                       /* OPT_COMMENT       1 */
+                                                                  interface_opts->name,       /* IDB_NAME          2 */
+                                                                  interface_opts->descr,      /* IDB_DESCRIPTION   3 */
+                                                                  interface_opts->cfilter,    /* IDB_FILTER       11 */
+                                                                  os_info_str->str,           /* IDB_OS           12 */
+                                                                  pcap_src->linktype,
+                                                                  pcap_src->snaplen,
+                                                                  &(global_ld.bytes_written),
+                                                                  0,                          /* IDB_IF_SPEED      8 */
+                                                                  pcap_src->ts_nsec ? 9 : 6,  /* IDB_TSRESOL       9 */
+                                                                  &global_ld.err);
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: wrote capture_opts IDB %d: %d", G_STRFUNC, if_id, successful);
+        }
+    }
+    g_rw_lock_reader_unlock (&ld->saved_shb_idb_lock);
+
+    g_string_free(os_info_str, TRUE);
+
+    return successful;
+}
 
 /* set up to write to the already-opened capture output file/files */
 static gboolean
 capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *errmsg, int errmsg_len)
 {
     int               err;
-    guint             i;
-    capture_src      *pcap_src;
-    interface_options *interface_opts;
     gboolean          successful;
 
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_init_output");
@@ -2937,58 +3185,10 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err
         }
     }
     if (ld->pdh) {
-        pcap_src = g_array_index(ld->pcaps, capture_src *, 0);
-        if (pcap_src->from_pcapng) {
-            /* We are just going to rewrite the source SHB and IDB blocks */
-            return TRUE;
-        }
         if (capture_opts->use_pcapng) {
-            char    *appname;
-            GString *cpu_info_str;
-            GString *os_info_str;
-
-            cpu_info_str = g_string_new("");
-            os_info_str = g_string_new("");
-            get_cpu_info(cpu_info_str);
-            get_os_version_info(os_info_str);
-
-            appname = g_strdup_printf("Dumpcap (Wireshark) %s", get_ws_vcs_version_info());
-            successful = pcapng_write_session_header_block(ld->pdh,
-                                (const char *)capture_opts->capture_comment,   /* Comment */
-                                cpu_info_str->str,           /* HW */
-                                os_info_str->str,            /* OS */
-                                appname,
-                                -1,                          /* section_length */
-                                &ld->bytes_written,
-                                &err);
-            g_string_free(cpu_info_str, TRUE);
-            g_free(appname);
-
-            for (i = 0; successful && (i < capture_opts->ifaces->len); i++) {
-                interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
-                pcap_src = g_array_index(ld->pcaps, capture_src *, i);
-                if (pcap_src->from_cap_pipe) {
-                    pcap_src->snaplen = pcap_src->cap_pipe_info.pcap.hdr.snaplen;
-                } else {
-                    pcap_src->snaplen = pcap_snapshot(pcap_src->pcap_h);
-                }
-                successful = pcapng_write_interface_description_block(global_ld.pdh,
-                                                                    NULL,                       /* OPT_COMMENT       1 */
-                                                                    interface_opts->name,       /* IDB_NAME          2 */
-                                                                    interface_opts->descr,      /* IDB_DESCRIPTION   3 */
-                                                                    interface_opts->cfilter,    /* IDB_FILTER       11 */
-                                                                    os_info_str->str,           /* IDB_OS           12 */
-                                                                    pcap_src->linktype,
-                                                                    pcap_src->snaplen,
-                                                                    &(global_ld.bytes_written),
-                                                                    0,                          /* IDB_IF_SPEED      8 */
-                                                                    pcap_src->ts_nsec ? 9 : 6,  /* IDB_TSRESOL       9 */
-                                                                    &global_ld.err);
-            }
-
-            g_string_free(os_info_str, TRUE);
-
+            successful = capture_loop_init_pcapng_output(capture_opts, ld);
         } else {
+            capture_src *pcap_src;
             pcap_src = g_array_index(ld->pcaps, capture_src *, 0);
             if (pcap_src->from_cap_pipe) {
                 pcap_src->snaplen = pcap_src->cap_pipe_info.pcap.hdr.snaplen;
@@ -3099,7 +3299,7 @@ capture_loop_dispatch(loop_data *ld,
     gint   packet_count_before;
     int    sel_ret;
 
-    packet_count_before = ld->packet_count;
+    packet_count_before = ld->packets_captured;
     if (pcap_src->from_cap_pipe) {
         /* dispatch from capture pipe */
 #ifdef LOG_CAPTURE_VERBOSE
@@ -3132,7 +3332,9 @@ capture_loop_dispatch(loop_data *ld,
              */
             inpkts = pcap_src->cap_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len);
             if (inpkts < 0) {
-                ld->go = FALSE;
+                g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: src %u pipe reached EOF or err, rcv: %u drop: %u flush: %u",
+                      G_STRFUNC, pcap_src->interface_id, pcap_src->received, pcap_src->dropped, pcap_src->flushed);
+                g_assert(pcap_src->cap_pipe_err != PIPOK);
             }
         }
     }
@@ -3267,7 +3469,7 @@ capture_loop_dispatch(loop_data *ld,
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "capture_loop_dispatch: %d new packet%s", inpkts, plurality(inpkts, "", "s"));
 #endif
 
-    return ld->packet_count - packet_count_before;
+    return ld->packets_captured - packet_count_before;
 }
 
 #ifdef _WIN32
@@ -3465,24 +3667,23 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
     return TRUE;
 }
 
+static time_t get_next_time_interval(int interval_s) {
+    time_t next_time = time(NULL);
+    next_time -= next_time % interval_s;
+    next_time += interval_s;
+    return next_time;
+}
 
 /* Do the work of handling either the file size or file duration capture
    conditions being reached, and switching files or stopping. */
 static gboolean
-do_file_switch_or_stop(capture_options *capture_opts,
-                       condition *cnd_autostop_files,
-                       condition *cnd_autostop_size,
-                       condition *cnd_file_duration,
-                       condition *cnd_file_interval)
+do_file_switch_or_stop(capture_options *capture_opts)
 {
-    guint             i;
-    capture_src      *pcap_src;
-    interface_options *interface_opts;
     gboolean          successful;
 
     if (capture_opts->multi_files_on) {
-        if (cnd_autostop_files != NULL &&
-            cnd_eval(cnd_autostop_files, (guint64)++global_ld.autostop_files)) {
+        if (capture_opts->has_autostop_files &&
+            ++global_ld.file_count >= capture_opts->autostop_files) {
             /* no files left: stop here */
             global_ld.go = FALSE;
             return FALSE;
@@ -3494,78 +3695,28 @@ do_file_switch_or_stop(capture_options *capture_opts,
 
             /* File switch succeeded: reset the conditions */
             global_ld.bytes_written = 0;
-            pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0);
-            if (pcap_src->from_pcapng) {
-                /* Write the saved SHB and all IDBs to start of next file */
-                /* The blocks were saved in reverse so reverse it before iterating */
-                GList *rlist = g_list_reverse(pcap_src->cap_pipe_info.pcapng.saved_blocks);
-                GList *list = rlist;
-                successful = TRUE;
-                while (list && successful) {
-                    successful = pcapng_write_saved_block(pcap_src, (struct pcapng_block_header_s *) list->data);
-                    list = g_list_next(list);
-                }
-                pcap_src->cap_pipe_info.pcapng.saved_blocks = g_list_reverse(rlist);
+            global_ld.packets_written = 0;
+            if (capture_opts->use_pcapng) {
+                successful = capture_loop_init_pcapng_output(capture_opts, &global_ld);
             } else {
-                if (capture_opts->use_pcapng) {
-                    char    *appname;
-                    GString *cpu_info_str;
-                    GString *os_info_str;
-
-                    cpu_info_str = g_string_new("");
-                    os_info_str = g_string_new("");
-                    get_cpu_info(cpu_info_str);
-                    get_os_version_info(os_info_str);
-
-                    appname = g_strdup_printf("Dumpcap (Wireshark) %s", get_ws_vcs_version_info());
-                    successful = pcapng_write_session_header_block(global_ld.pdh,
-                                    (const char *)capture_opts->capture_comment,   /* Comment */
-                                    cpu_info_str->str,           /* HW */
-                                    os_info_str->str,            /* OS */
-                                    appname,
-                                    -1,                          /* section_length */
-                                    &(global_ld.bytes_written),
-                                    &global_ld.err);
-                    g_string_free(cpu_info_str, TRUE);
-                    g_free(appname);
-
-                    for (i = 0; successful && (i < capture_opts->ifaces->len); i++) {
-                        interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
-                        pcap_src = g_array_index(global_ld.pcaps, capture_src *, i);
-                        successful = pcapng_write_interface_description_block(global_ld.pdh,
-                                                                            NULL,                        /* OPT_COMMENT       1 */
-                                                                            interface_opts->name,        /* IDB_NAME          2 */
-                                                                            interface_opts->descr,       /* IDB_DESCRIPTION   3 */
-                                                                            interface_opts->cfilter,     /* IDB_FILTER       11 */
-                                                                            os_info_str->str,            /* IDB_OS           12 */
-                                                                            pcap_src->linktype,
-                                                                            pcap_src->snaplen,
-                                                                            &(global_ld.bytes_written),
-                                                                            0,                          /* IDB_IF_SPEED      8 */
-                                                                            pcap_src->ts_nsec ? 9 : 6,  /* IDB_TSRESOL       9 */
-                                                                            &global_ld.err);
-                    }
-
-                    g_string_free(os_info_str, TRUE);
-
-                } else {
-                    pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0);
-                    successful = libpcap_write_file_header(global_ld.pdh, pcap_src->linktype, pcap_src->snaplen,
-                                                        pcap_src->ts_nsec, &global_ld.bytes_written, &global_ld.err);
-                }
+                capture_src *pcap_src;
+                pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0);
+                successful = libpcap_write_file_header(global_ld.pdh, pcap_src->linktype, pcap_src->snaplen,
+                                                       pcap_src->ts_nsec, &global_ld.bytes_written, &global_ld.err);
             }
+
             if (!successful) {
                 fclose(global_ld.pdh);
                 global_ld.pdh = NULL;
                 global_ld.go = FALSE;
                 return FALSE;
             }
-            if (cnd_autostop_size)
-                cnd_reset(cnd_autostop_size);
-            if (cnd_file_duration)
-                cnd_reset(cnd_file_duration);
-            if (cnd_file_interval)
-                cnd_reset(cnd_file_interval);
+            if (global_ld.file_duration_timer) {
+                g_timer_reset(global_ld.file_duration_timer);
+            }
+            if (global_ld.next_interval_time) {
+                global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s);
+            }
             fflush(global_ld.pdh);
             if (!quiet)
                 report_packet_count(global_ld.inpkts_to_sync_pipe);
@@ -3587,24 +3738,66 @@ do_file_switch_or_stop(capture_options *capture_opts,
 static void *
 pcap_read_handler(void* arg)
 {
-    capture_src *pcap_src;
+    capture_src *pcap_src = (capture_src *)arg;
     char         errmsg[MSG_MAX_LENGTH+1];
 
-    pcap_src = (capture_src *)arg;
-
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Started thread for interface %d.",
           pcap_src->interface_id);
 
-    while (global_ld.go) {
+    /* If this is a pipe input it might finish early. */
+    while (global_ld.go && pcap_src->cap_pipe_err == PIPOK) {
         /* dispatch incoming packets */
         capture_loop_dispatch(&global_ld, errmsg, sizeof(errmsg), pcap_src);
     }
+
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Stopped thread for interface %d.",
           pcap_src->interface_id);
     g_thread_exit(NULL);
     return (NULL);
 }
 
+/* Try to pop an item off the packet queue and if it exists, write it */
+static gboolean
+capture_loop_dequeue_packet(void) {
+    pcap_queue_element *queue_element;
+
+    g_async_queue_lock(pcap_queue);
+    queue_element = (pcap_queue_element *)g_async_queue_timeout_pop_unlocked(pcap_queue, WRITER_THREAD_TIMEOUT);
+    if (queue_element) {
+        if (queue_element->pcap_src->from_pcapng) {
+            pcap_queue_bytes -= queue_element->u.bh.block_total_length;
+        } else {
+            pcap_queue_bytes -= queue_element->u.phdr.caplen;
+        }
+        pcap_queue_packets -= 1;
+    }
+    g_async_queue_unlock(pcap_queue);
+    if (queue_element) {
+        if (queue_element->pcap_src->from_pcapng) {
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
+                  "Dequeued a block of type 0x%08x of length %d captured on interface %d.",
+                  queue_element->u.bh.block_type, queue_element->u.bh.block_total_length,
+                  queue_element->pcap_src->interface_id);
+
+            capture_loop_write_pcapng_cb(queue_element->pcap_src,
+                                        &queue_element->u.bh,
+                                        queue_element->pd);
+        } else {
+            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
+                "Dequeued a packet of length %d captured on interface %d.",
+                queue_element->u.phdr.caplen, queue_element->pcap_src->interface_id);
+
+            capture_loop_write_packet_cb((u_char *) queue_element->pcap_src,
+                                        &queue_element->u.phdr,
+                                        queue_element->pd);
+        }
+        g_free(queue_element->pd);
+        g_free(queue_element);
+        return TRUE;
+    }
+    return FALSE;
+}
+
 /* Do the low-level work of a capture.
    Returns TRUE if it succeeds, FALSE otherwise. */
 static gboolean
@@ -3617,11 +3810,7 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 #endif
     int               err_close;
     int               inpkts;
-    condition        *cnd_file_duration     = NULL;
-    condition        *cnd_file_interval     = NULL;
-    condition        *cnd_autostop_files    = NULL;
-    condition        *cnd_autostop_size     = NULL;
-    condition        *cnd_autostop_duration = NULL;
+    GTimer           *autostop_duration_timer = NULL;
     gboolean          write_ok;
     gboolean          close_ok;
     gboolean          cfilter_error         = FALSE;
@@ -3636,19 +3825,18 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
     /* init the loop data */
     global_ld.go                  = TRUE;
-    global_ld.packet_count        = 0;
+    global_ld.packets_captured    = 0;
 #ifdef SIGINFO
     global_ld.report_packet_count = FALSE;
 #endif
-    if (capture_opts->has_autostop_packets)
-        global_ld.packet_max      = capture_opts->autostop_packets;
-    else
-        global_ld.packet_max      = 0;        /* no limit */
     global_ld.inpkts_to_sync_pipe = 0;
     global_ld.err                 = 0;  /* no error seen yet */
     global_ld.pdh                 = NULL;
-    global_ld.autostop_files      = 0;
     global_ld.save_file_fd        = -1;
+    global_ld.file_count          = 0;
+    global_ld.file_duration_timer = NULL;
+    global_ld.next_interval_time  = 0;
+    global_ld.interval_s          = 0;
 
     /* We haven't yet gotten the capture statistics. */
     *stats_known      = FALSE;
@@ -3722,32 +3910,24 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
         report_new_capture_file(capture_opts->save_file);
     }
 
-    /* initialize capture stop (and alike) conditions */
-    init_capture_stop_conditions();
+    if (capture_opts->has_file_interval) {
+        global_ld.interval_s = capture_opts->file_interval;
+        global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s);
+    }
     /* create stop conditions */
     if (capture_opts->has_autostop_filesize) {
         if (capture_opts->autostop_filesize > (((guint32)INT_MAX + 1) / 1000)) {
             capture_opts->autostop_filesize = ((guint32)INT_MAX + 1) / 1000;
         }
-        cnd_autostop_size =
-            cnd_new(CND_CLASS_CAPTURESIZE, (guint64)capture_opts->autostop_filesize * 1000);
     }
-    if (capture_opts->has_autostop_duration)
-        cnd_autostop_duration =
-            cnd_new(CND_CLASS_TIMEOUT,(gint32)capture_opts->autostop_duration);
+    if (capture_opts->has_autostop_duration) {
+        autostop_duration_timer = g_timer_new();
+    }
 
     if (capture_opts->multi_files_on) {
-        if (capture_opts->has_file_duration)
-            cnd_file_duration =
-                cnd_new(CND_CLASS_TIMEOUT, capture_opts->file_duration);
-
-        if (capture_opts->has_autostop_files)
-            cnd_autostop_files =
-                cnd_new(CND_CLASS_CAPTURESIZE, (guint64)capture_opts->autostop_files);
-
-        if (capture_opts->has_file_interval)
-            cnd_file_interval =
-                cnd_new(CND_CLASS_INTERVAL, capture_opts->file_interval);
+        if (capture_opts->has_file_duration) {
+            global_ld.file_duration_timer = g_timer_new();
+        }
     }
 
     /* init the time values */
@@ -3775,40 +3955,9 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
     while (global_ld.go) {
         /* dispatch incoming packets */
         if (use_threads) {
-            pcap_queue_element *queue_element;
-
-            g_async_queue_lock(pcap_queue);
-            queue_element = (pcap_queue_element *)g_async_queue_timeout_pop_unlocked(pcap_queue, WRITER_THREAD_TIMEOUT);
-            if (queue_element) {
-                if (queue_element->pcap_src->from_pcapng) {
-                    pcap_queue_bytes -= queue_element->u.bh.block_total_length;
-                    pcap_queue_packets -= 1;
-                } else {
-                    pcap_queue_bytes -= queue_element->u.phdr.caplen;
-                    pcap_queue_packets -= 1;
-                }
-            }
-            g_async_queue_unlock(pcap_queue);
-            if (queue_element) {
-                if (queue_element->pcap_src->from_pcapng) {
-                    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-                        "Dequeued a block of length %d captured on interface %d.",
-                        queue_element->u.bh.block_total_length, queue_element->pcap_src->interface_id);
+            gboolean dequeued = capture_loop_dequeue_packet();
 
-                    capture_loop_write_pcapng_cb(queue_element->pcap_src,
-                                                &queue_element->u.bh,
-                                                queue_element->pd);
-                } else {
-                    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-                        "Dequeued a packet of length %d captured on interface %d.",
-                        queue_element->u.phdr.caplen, queue_element->pcap_src->interface_id);
-
-                    capture_loop_write_packet_cb((u_char *) queue_element->pcap_src,
-                                                &queue_element->u.phdr,
-                                                queue_element->pd);
-                }
-                g_free(queue_element->pd);
-                g_free(queue_element);
+            if (dequeued) {
                 inpkts = 1;
             } else {
                 inpkts = 0;
@@ -3818,11 +3967,25 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
             inpkts = capture_loop_dispatch(&global_ld, errmsg,
                                            sizeof(errmsg), pcap_src);
         }
+        if (inpkts == 0) {
+            /* Stop capturing if all of our sources are pipes and none of them are open. */
+            gboolean open_interfaces = FALSE;
+            for (i = 0; i < global_ld.pcaps->len; i++) {
+                pcap_src = g_array_index(global_ld.pcaps, capture_src *, i);
+                if (pcap_src->cap_pipe_err == PIPOK) {
+                    /* True for both non-pipes and open pipes. */
+                    open_interfaces = TRUE;
+                }
+            }
+            if (!open_interfaces) {
+                global_ld.go = FALSE;
+            }
+        }
 #ifdef SIGINFO
         /* Were we asked to print packet counts by the SIGINFO handler? */
         if (global_ld.report_packet_count) {
-            fprintf(stderr, "%u packet%s captured\n", global_ld.packet_count,
-                    plurality(global_ld.packet_count, "", "s"));
+            fprintf(stderr, "%u packet%s captured\n", global_ld.packets_captured,
+                    plurality(global_ld.packets_captured, "", "s"));
             global_ld.report_packet_count = FALSE;
         }
 #endif
@@ -3837,17 +4000,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
         if (inpkts > 0) {
             global_ld.inpkts_to_sync_pipe += inpkts;
 
-            /* check capture size condition */
-            if (cnd_autostop_size != NULL &&
-                cnd_eval(cnd_autostop_size, global_ld.bytes_written)) {
-                /* Capture size limit reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts,
-                                            cnd_autostop_files,
-                                            cnd_autostop_size,
-                                            cnd_file_duration,
-                                            cnd_file_interval))
-                    continue;
-            } /* cnd_autostop_size */
             if (capture_opts->output_to_pipe) {
                 fflush(global_ld.pdh);
             }
@@ -3890,31 +4042,23 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
             }
 
             /* check capture duration condition */
-            if (cnd_autostop_duration != NULL && cnd_eval(cnd_autostop_duration)) {
+            if (autostop_duration_timer != NULL && g_timer_elapsed(autostop_duration_timer, NULL) >= capture_opts->autostop_duration) {
                 /* The maximum capture time has elapsed; stop the capture. */
                 global_ld.go = FALSE;
                 continue;
             }
 
             /* check capture file duration condition */
-            if (cnd_file_duration != NULL && cnd_eval(cnd_file_duration)) {
+            if (global_ld.file_duration_timer != NULL && g_timer_elapsed(global_ld.file_duration_timer, NULL) >= capture_opts->file_duration) {
                 /* duration limit reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts,
-                                            cnd_autostop_files,
-                                            cnd_autostop_size,
-                                            cnd_file_duration,
-                                            cnd_file_interval))
+                if (!do_file_switch_or_stop(capture_opts))
                     continue;
             } /* cnd_file_duration */
 
             /* check capture file interval condition */
-            if (cnd_file_interval != NULL && cnd_eval(cnd_file_interval)) {
+            if (global_ld.interval_s && time(NULL) >= global_ld.next_interval_time) {
                 /* end of interval reached, do we have another file? */
-                if (!do_file_switch_or_stop(capture_opts,
-                                            cnd_autostop_files,
-                                            cnd_autostop_size,
-                                            cnd_file_duration,
-                                            cnd_file_interval))
+                if (!do_file_switch_or_stop(capture_opts))
                     continue;
             } /* cnd_file_interval */
         }
@@ -3922,7 +4066,6 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "Capture loop stopping ...");
     if (use_threads) {
-        pcap_queue_element *queue_element;
 
         for (i = 0; i < global_ld.pcaps->len; i++) {
             pcap_src = g_array_index(global_ld.pcaps, capture_src *, i);
@@ -3933,24 +4076,10 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
                   pcap_src->interface_id);
         }
         while (1) {
-            g_async_queue_lock(pcap_queue);
-            queue_element = (pcap_queue_element *)g_async_queue_try_pop_unlocked(pcap_queue);
-            if (queue_element) {
-                pcap_queue_bytes -= queue_element->u.phdr.caplen;
-                pcap_queue_packets -= 1;
-            }
-            g_async_queue_unlock(pcap_queue);
-            if (queue_element == NULL) {
+            gboolean dequeued = capture_loop_dequeue_packet();
+            if (!dequeued) {
                 break;
             }
-            g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-                  "Dequeued a packet of length %d captured on interface %d.",
-                  queue_element->u.phdr.caplen, queue_element->pcap_src->interface_id);
-            capture_loop_write_packet_cb((u_char *)queue_element->pcap_src,
-                                         &queue_element->u.phdr,
-                                         queue_element->pd);
-            g_free(queue_element->pd);
-            g_free(queue_element);
             global_ld.inpkts_to_sync_pipe += 1;
             if (capture_opts->output_to_pipe) {
                 fflush(global_ld.pdh);
@@ -3960,16 +4089,10 @@ capture_loop_start(capture_options *capture_opts, gboolean *stats_known, struct
 
 
     /* delete stop conditions */
-    if (cnd_file_duration != NULL)
-        cnd_delete(cnd_file_duration);
-    if (cnd_file_interval != NULL)
-        cnd_delete(cnd_file_interval);
-    if (cnd_autostop_files != NULL)
-        cnd_delete(cnd_autostop_files);
-    if (cnd_autostop_size != NULL)
-        cnd_delete(cnd_autostop_size);
-    if (cnd_autostop_duration != NULL)
-        cnd_delete(cnd_autostop_duration);
+    if (global_ld.file_duration_timer != NULL)
+        g_timer_destroy(global_ld.file_duration_timer);
+    if (autostop_duration_timer != NULL)
+        g_timer_destroy(autostop_duration_timer);
 
     /* did we have a pcap (input) error? */
     for (i = 0; i < capture_opts->ifaces->len; i++) {
@@ -4199,15 +4322,48 @@ capture_loop_get_errmsg(char *errmsg, size_t errmsglen, char *secondary_errmsg,
     }
 }
 
+/*
+ * We wrote one packet. Update some statistics and check if we've met any
+ * autostop or ring buffer conditions.
+ */
+static void
+capture_loop_wrote_one_packet(capture_src *pcap_src) {
+    global_ld.packets_captured++;
+    global_ld.packets_written++;
+    pcap_src->received++;
+
+    /* check -c NUM / -a packets:NUM */
+    if (global_capture_opts.has_autostop_packets && global_ld.packets_captured >= global_capture_opts.autostop_packets) {
+        fflush(global_ld.pdh);
+        global_ld.go = FALSE;
+        return;
+    }
+    /* check -b packets:NUM */
+    if (global_capture_opts.has_file_packets && global_ld.packets_written >= global_capture_opts.file_packets) {
+        do_file_switch_or_stop(&global_capture_opts);
+        return;
+    }
+    /* check -a filesize:NUM */
+    if (global_capture_opts.has_autostop_filesize &&
+        global_capture_opts.autostop_filesize > 0 &&
+        global_ld.bytes_written / 1000 >= global_capture_opts.autostop_filesize) {
+        /* Capture size limit reached, do we have another file? */
+        do_file_switch_or_stop(&global_capture_opts);
+        return;
+    }
+}
+
 /* one pcapng block was captured, process it */
 static void
-capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, const u_char *pd)
+capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd)
 {
     int          err;
 
-    if (!global_capture_opts.use_pcapng) {
-        return;
-    }
+    /*
+     * This should never be called if we're not writing pcapng.
+     */
+    g_assert(global_capture_opts.use_pcapng);
+
     /* We may be called multiple times from pcap_dispatch(); if we've set
        the "stop capturing" flag, ignore this packet, as we're not
        supposed to be saving any more packets. */
@@ -4216,6 +4372,21 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_he
         return;
     }
 
+    if (!pcapng_adjust_block(pcap_src, bh, pd)) {
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "%s failed to adjust pcapng block.", G_STRFUNC);
+        g_assert_not_reached();
+        return;
+    }
+
+    if (bh->block_type == BLOCK_TYPE_SHB && !global_ld.pcapng_passthrough) {
+        /*
+         * capture_loop_init_pcapng_output should've handled this. We need
+         * to write ISBs when they're initially read so we shouldn't skip
+         * them here.
+         */
+        return;
+    }
+
     if (global_ld.pdh) {
         gboolean successful;
 
@@ -4236,20 +4407,15 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_he
             /* count packet only if we actually have an EPB or SPB */
 #if defined(DEBUG_DUMPCAP) || defined(DEBUG_CHILD_DUMPCAP)
             g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-                  "Wrote a packet of length %d captured on interface %u.",
-                   bh->block_total_length, pcap_src->interface_id);
+                  "Wrote a pcapng block type %u of length %d captured on interface %u.",
+                   bh->block_type, bh->block_total_length, pcap_src->interface_id);
 #endif
-            global_ld.packet_count++;
-            pcap_src->received++;
-            /* if the user told us to stop after x packets, do we already have enough? */
-            if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) {
-                global_ld.go = FALSE;
-            }
+            capture_loop_wrote_one_packet(pcap_src);
         }
     }
 }
 
-/* one packet was captured, process it */
+/* one pcap packet was captured, process it */
 static void
 capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
                              const u_char *pd)
@@ -4297,15 +4463,10 @@ capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
         } else {
 #if defined(DEBUG_DUMPCAP) || defined(DEBUG_CHILD_DUMPCAP)
             g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-                  "Wrote a packet of length %d captured on interface %u.",
+                  "Wrote a pcap packet of length %d captured on interface %u.",
                    phdr->caplen, pcap_src->interface_id);
 #endif
-            global_ld.packet_count++;
-            pcap_src->received++;
-            /* if the user told us to stop after x packets, do we already have enough? */
-            if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) {
-                global_ld.go = FALSE;
-            }
+            capture_loop_wrote_one_packet(pcap_src);
         }
     }
 }
@@ -4374,7 +4535,7 @@ capture_loop_queue_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr,
 
 /* one pcapng block was captured, queue it */
 static void
-capture_loop_queue_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, const u_char *pd)
+capture_loop_queue_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd)
 {
     pcap_queue_element *queue_element;
     gboolean            limit_reached;
@@ -4422,8 +4583,8 @@ capture_loop_queue_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_he
     } else {
         pcap_src->received++;
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO,
-              "Queued a packet of length %d captured on interface %u.",
-              bh->block_total_length, pcap_src->interface_id);
+              "Queued a block of type 0x%08x of length %d captured on interface %u.",
+              bh->block_type, bh->block_total_length, pcap_src->interface_id);
     }
     /* I don't want to hold the mutex over the debug output. So the
        output may be wrong */
@@ -4506,8 +4667,6 @@ get_dumpcap_runtime_info(GString *str)
 int
 main(int argc, char *argv[])
 {
-    GString          *comp_info_str;
-    GString          *runtime_info_str;
     int               opt;
     static const struct option long_options[] = {
         {"help", no_argument, NULL, 'h'},
@@ -4548,24 +4707,11 @@ main(int argc, char *argv[])
 
     cmdarg_err_init(dumpcap_cmdarg_err, dumpcap_cmdarg_err_cont);
 
-    /* Get the compile-time version information string */
-    comp_info_str = get_compiled_version_info(NULL, get_dumpcap_compiled_info);
-
-    /* Get the run-time version information string */
-    runtime_info_str = get_runtime_version_info(get_dumpcap_runtime_info);
-
-    /* Add it to the information to be reported on a crash. */
-    ws_add_crash_info("Dumpcap (Wireshark) %s\n"
-           "\n"
-           "%s"
-           "\n"
-           "%s",
-        get_ws_vcs_version_info(), comp_info_str->str, runtime_info_str->str);
-    g_string_free(comp_info_str, TRUE);
-    g_string_free(runtime_info_str, TRUE);
+    /* Initialize the version information. */
+    ws_init_version_info("Dumpcap (Wireshark)", NULL, get_dumpcap_compiled_info,
+                         get_dumpcap_runtime_info);
 
 #ifdef _WIN32
-    arg_list_utf_16to8(argc, argv);
     create_app_running_mutex();
 
     /*
@@ -4694,8 +4840,11 @@ main(int argc, char *argv[])
                       log_flags,
                       console_log_handler, NULL /* user_data */);
 
-    /* Initialize the pcaps list */
+    /* Initialize the pcaps list and IDBs */
     global_ld.pcaps = g_array_new(FALSE, FALSE, sizeof(capture_src *));
+    global_ld.pcapng_passthrough = FALSE;
+    global_ld.saved_shb = NULL;
+    global_ld.saved_idbs = g_array_new(FALSE, TRUE, sizeof(saved_idb_t));
 
 #ifdef _WIN32
     /* Load wpcap if possible. Do this before collecting the run-time version information */
@@ -4852,19 +5001,12 @@ main(int argc, char *argv[])
     while ((opt = getopt_long(argc, argv, OPTSTRING, long_options, NULL)) != -1) {
         switch (opt) {
         case 'h':        /* Print help and exit */
-            printf("Dumpcap (Wireshark) %s\n"
-                   "Capture network packets and dump them into a pcapng or pcap file.\n"
-                   "See https://www.wireshark.org for more information.\n",
-                   get_ws_vcs_version_info());
+            show_help_header("Capture network packets and dump them into a pcapng or pcap file.");
             print_usage(stdout);
             exit_main(0);
             break;
         case 'v':        /* Show version and exit */
-            comp_info_str = get_compiled_version_info(NULL, get_dumpcap_compiled_info);
-            runtime_info_str = get_runtime_version_info(get_dumpcap_runtime_info);
-            show_version("Dumpcap (Wireshark)", comp_info_str, runtime_info_str);
-            g_string_free(comp_info_str, TRUE);
-            g_string_free(runtime_info_str, TRUE);
+            show_version();
             exit_main(0);
             break;
         /*** capture option specific ***/
@@ -5064,9 +5206,10 @@ main(int argc, char *argv[])
             }
             if (!global_capture_opts.has_autostop_filesize &&
                 !global_capture_opts.has_file_duration &&
-                !global_capture_opts.has_file_interval) {
-                cmdarg_err("Ring buffer requested, but no maximum capture file size, duration"
-                           "or interval were specified.");
+                !global_capture_opts.has_file_interval &&
+                !global_capture_opts.has_file_packets) {
+                cmdarg_err("Ring buffer requested, but no maximum capture file size, duration "
+                           "interval, or packets were specified.");
 #if 0
                 /* XXX - this must be redesigned as the conditions changed */
                 global_capture_opts.multi_files_on = FALSE;
@@ -5272,7 +5415,6 @@ main(int argc, char *argv[])
     return 0; /* never here, make compiler happy */
 }
 
-
 static void
 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
                     const char *message, gpointer user_data _U_)
@@ -5370,13 +5512,13 @@ console_log_handler(const char *log_domain, GLogLevelFlags log_level,
 static void
 report_packet_count(unsigned int packet_count)
 {
-    char tmp[SP_DECISIZE+1+1];
+    char count_str[SP_DECISIZE+1+1];
     static unsigned int count = 0;
 
     if (capture_child) {
-        g_snprintf(tmp, sizeof(tmp), "%u", packet_count);
-        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets: %s", tmp);
-        pipe_write_block(2, SP_PACKET_COUNT, tmp);
+        g_snprintf(count_str, sizeof(count_str), "%u", packet_count);
+        g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "Packets: %s", count_str);
+        pipe_write_block(2, SP_PACKET_COUNT, count_str);
     } else {
         count += packet_count;
         fprintf(stderr, "\rPackets: %u ", count);
@@ -5465,17 +5607,16 @@ report_capture_error(const char *error_msg, const char *secondary_error_msg)
 static void
 report_packet_drops(guint32 received, guint32 pcap_drops, guint32 drops, guint32 flushed, guint32 ps_ifdrop, gchar *name)
 {
-    char tmp[SP_DECISIZE+1+1];
     guint32 total_drops = pcap_drops + drops + flushed;
 
-    g_snprintf(tmp, sizeof(tmp), "%u", total_drops);
-
     if (capture_child) {
+        char* tmp = g_strdup_printf("%u:%s", total_drops, name);
+
         g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
             "Packets received/dropped on interface '%s': %u/%u (pcap:%u/dumpcap:%u/flushed:%u/ps_ifdrop:%u)",
             name, received, total_drops, pcap_drops, drops, flushed, ps_ifdrop);
-        /* XXX: Need to provide interface id, changes to consumers required. */
         pipe_write_block(2, SP_DROPS, tmp);
+        g_free(tmp);
     } else {
         fprintf(stderr,
             "Packets received/dropped on interface '%s': %u/%u (pcap:%u/dumpcap:%u/flushed:%u/ps_ifdrop:%u) (%.1f%%)\n",