ANNOUNCEMENT: Live Wireshark University & Allegro Packets online APAC Wireshark Training Session
April 17th, 2024 | 14:30-16:00 SGT (UTC+8) | Online

Wireshark-dev: Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap

From: James Ko <jim.list@xxxxxxxxxxx>
Date: Wed, 22 Nov 2017 18:13:36 +0000
Attached is my patch to get this working (with caveats).

To support this involved reading the the pcapng block header and parsing the just the section header block for endianess.  Then just rewrites the all blocks using the original/input endianess format to the pipe without looking much further at the block contents.

It only supports one interface.  I don't fully understand how multiple interfaces are handled  but would probably require parsing the interface description blocks as well to get the linktype and snaplen to put in the interface table.

I also did not implement the use_threads option for packet processing.

There is #ifdef WIN32 bits but I don't know if any of that is correct as I didn't really touch it and did not even try to compile it.

I hope this gets this feature moving along.

Regards,
James

diff --git a/dumpcap.c b/dumpcap.c
index cc420ba..6fff51f 100644
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -108,6 +108,7 @@
  */
 #include "wiretap/libpcap.h"
 #include "wiretap/pcapng_module.h"
+#include "wiretap/pcapng.h"
 
 /**#define DEBUG_DUMPCAP**/
 /**#define DEBUG_CHILD_DUMPCAP**/
@@ -270,8 +271,15 @@ typedef struct _capture_src {
                                                          /**< capture pipe (unix only "input file") */
     gboolean                     from_cap_pipe;          /**< TRUE if we are capturing data from a capture pipe */
     gboolean                     from_cap_socket;        /**< TRUE if we're capturing from socket */
-    struct pcap_hdr              cap_pipe_hdr;           /**< Pcap header when capturing from a pipe */
-    struct pcaprec_modified_hdr  cap_pipe_rechdr;        /**< Pcap record header when capturing from a pipe */
+    gboolean                     from_pcapng;            /**< TRUE if we're capturing from pcapng format */
+    union {
+        struct pcap_hdr              cap_pipe_hdr;       /**< Pcap header when capturing from a pipe */
+        struct pcapng_section_header_block_s pcapng_pipe_shb; /**< Pcapng header when capturing from a pipe */
+    };
+    union {
+        struct pcaprec_modified_hdr  cap_pipe_rechdr;    /**< Pcap record header when capturing from a pipe */
+        struct pcapng_block_header_s pcapng_pipe_bh;     /**< Pcapng block header when capturing from a pipe */
+    };
 #ifdef _WIN32
     HANDLE                       cap_pipe_h;             /**< The handle of the capture pipe */
 #endif
@@ -388,6 +396,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_shb(u_char *pcap_src_p, const u_char *pd);
+static void capture_loop_write_pcapng_block(u_char *pcap_src_p, const u_char *pd);
 static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname,
                                     int err, gboolean is_close);
 
@@ -1452,6 +1462,10 @@ cap_pipe_close(int pipe_fd, gboolean from_socket _U_)
 #endif
 }
 
+/* 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, struct pcapng_section_header_block_s *hdr, char *errmsg, int errmsgl);
+
 /* Mimic pcap_open_live() for pipe captures
 
  * We check if "pipename" is "-" (stdin), a AF_UNIX socket, or a FIFO,
@@ -1462,7 +1476,7 @@ cap_pipe_close(int pipe_fd, gboolean from_socket _U_)
 static void
 cap_pipe_open_live(char *pipename,
                    capture_src *pcap_src,
-                   struct pcap_hdr *hdr,
+                   void *hdr,
                    char *errmsg, int errmsgl)
 {
 #ifndef _WIN32
@@ -1774,16 +1788,38 @@ cap_pipe_open_live(char *pipename,
         pcap_src->cap_pipe_modified = TRUE;
         break;
     case BLOCK_TYPE_SHB:
-        /* This isn't pcap, it's pcapng.  We don't yet support
-           reading it. */
-        g_snprintf(errmsg, errmsgl, "Capturing from a pipe doesn't support pcapng format.");
-        goto error;
+        pcap_src->pcapng_pipe_bh.block_type = BLOCK_TYPE_SHB;
+        pcap_src->from_pcapng = TRUE;
+        global_capture_opts.use_pcapng = TRUE;
+        pcapng_pipe_open_live(fd, pcap_src, (struct pcapng_section_header_block_s *) hdr, 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.");
         goto error;
     }
+    pcap_pipe_open_live(fd, pcap_src, (struct pcap_hdr *) hdr, errmsg, errmsgl);
+    return;
 
+error:
+    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg);
+    pcap_src->cap_pipe_err = PIPERR;
+    cap_pipe_close(fd, pcap_src->from_cap_socket);
+    pcap_src->cap_pipe_fd = -1;
+#ifdef _WIN32
+    pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
+}
+
+static void
+pcap_pipe_open_live(int fd,
+                    capture_src *pcap_src,
+                    struct pcap_hdr *hdr,
+                    char *errmsg, int errmsgl)
+{
+    size_t   bytes_read;
+    ssize_t  b;
+    int      sel_ret;
 #ifdef _WIN32
     if (pcap_src->from_cap_socket)
 #endif
@@ -1829,7 +1865,6 @@ cap_pipe_open_live(char *pipename,
         }
     }
 #endif
-
     if (pcap_src->cap_pipe_byte_swapped) {
         /* Byte-swap the header fields about which we care. */
         hdr->version_major = GUINT16_SWAP_LE_BE(hdr->version_major);
@@ -1850,7 +1885,8 @@ cap_pipe_open_live(char *pipename,
         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");
+        g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format version %d.%d",
+                   hdr->version_major, hdr->version_minor);
         goto error;
     }
 
@@ -1869,11 +1905,190 @@ error:
 #endif
 }
 
+static void
+pcapng_read_shb(capture_src *pcap_src,
+                struct pcapng_section_header_block_s *hdr,
+                char *errmsg,
+                int errmsgl)
+{
+    size_t   bytes_read;
+    ssize_t  b;
+
+#ifdef _WIN32
+    if (pcap_src->from_cap_socket)
+#endif
+    {
+        /* Read the section header */
+        bytes_read = 0;
+        while (bytes_read < sizeof(struct pcapng_section_header_block_s)) {
+            b = cap_pipe_read(pcap_src->cap_pipe_fd, ((char *)hdr)+bytes_read,
+                                sizeof(struct pcapng_section_header_block_s) - bytes_read,
+                                pcap_src->from_cap_socket);
+            if (b <= 0) {
+                if (b == 0)
+                    g_snprintf(errmsg, errmsgl, "End of file on pipe header during open.");
+                else
+                    g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s.",
+                                g_strerror(errno));
+                goto error;
+            }
+            bytes_read += b;
+        }
+    }
+#ifdef _WIN32
+    else {
+        pcap_src->cap_pipe_buf = (char *) hdr;
+        pcap_src->cap_pipe_bytes_read = 0;
+        pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_section_header_block_s);
+        g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
+        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.");
+            else
+                g_snprintf(errmsg, errmsgl, "Error on pipe section header during open: %s.",
+                           g_strerror(errno));
+            goto error;
+        }
+    }
+#endif
+
+    switch (hdr->magic)
+    {
+    case PCAPNG_MAGIC:
+        pcap_src->cap_pipe_byte_swapped = FALSE;
+        break;
+    case PCAPNG_SWAPPED_MAGIC:
+        pcap_src->cap_pipe_byte_swapped = TRUE;
+        break;
+    default:
+        /* Not a pcapng type we know about, or not pcapng at all. */
+        g_snprintf(errmsg, errmsgl, "Unrecognized pcapng format or not pcapng data.");
+        goto error;
+    }
+
+    if (pcap_src->cap_pipe_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);
+        hdr->section_length = GUINT64_SWAP_LE_BE(hdr->section_length);
+        pcap_src->pcapng_pipe_bh.block_total_length = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_total_length);
+    }
+
+    pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD;
+
+    /* Setup state to capture the rest of the section header block */
+    pcap_src->cap_pipe_state = STATE_READ_DATA;
+    pcap_src->cap_pipe_bytes_to_read = pcap_src->pcapng_pipe_bh.block_total_length
+            - sizeof(struct pcapng_block_header_s)
+            - sizeof(struct pcapng_section_header_block_s);
+    pcap_src->cap_pipe_bytes_read = 0;
+
+    return;
+
+error:
+    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_read_shb: error %s", errmsg);
+    pcap_src->cap_pipe_err = PIPERR;
+    cap_pipe_close(pcap_src->cap_pipe_fd, pcap_src->from_cap_socket);
+    pcap_src->cap_pipe_fd = -1;
+#ifdef _WIN32
+    pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
+}
+
+static void
+pcapng_pipe_open_live(int fd,
+                      capture_src *pcap_src,
+                      struct pcapng_section_header_block_s *hdr,
+                      char *errmsg,
+                      int errmsgl)
+{
+    size_t   bytes_read;
+    ssize_t  b;
+    int      sel_ret;
+
+    if (pcap_src->pcapng_pipe_bh.block_type != BLOCK_TYPE_SHB) {
+        g_snprintf(errmsg, errmsgl, "Expected Section Header Block instead of 0x%X.",
+            pcap_src->pcapng_pipe_bh.block_type);
+        goto error;
+    }
+#ifdef _WIN32
+    if (pcap_src->from_cap_socket)
+#endif
+    {
+        /* read the block header total length */
+        bytes_read = sizeof(guint32);
+        while (bytes_read < sizeof(struct pcapng_block_header_s)) {
+            if (fd == -1) {
+                g_snprintf(errmsg, errmsgl, "Invalid file descriptor.");
+                goto error;
+            }
+
+            sel_ret = cap_pipe_select(fd);
+            if (sel_ret < 0) {
+                g_snprintf(errmsg, errmsgl,
+                           "Unexpected error from select: %s.", g_strerror(errno));
+                goto error;
+            } else if (sel_ret > 0) {
+                b = cap_pipe_read(fd, ((char *)&pcap_src->pcapng_pipe_bh)+bytes_read,
+                                  sizeof(struct pcapng_block_header_s)-bytes_read,
+                                  pcap_src->from_cap_socket);
+                if (b <= 0) {
+                    if (b == 0)
+                        g_snprintf(errmsg, errmsgl, "End of file on pipe section during open.");
+                    else
+                        g_snprintf(errmsg, errmsgl, "Error on pipe section during open: %s.",
+                                   g_strerror(errno));
+                    goto error;
+                }
+                bytes_read += b;
+            }
+        }
+    }
+#ifdef _WIN32
+    else {
+#if GLIB_CHECK_VERSION(2,31,0)
+        g_thread_new("cap_pipe_open_live", &cap_thread_read, pcap_src);
+#else
+        g_thread_create(&cap_thread_read, pcap_src, FALSE, NULL);
+#endif
+
+        pcap_src->cap_pipe_buf = (char *) &pcap_src->pcapng_pipe_bh.block_total_length;
+        pcap_src->cap_pipe_bytes_read = 0;
+        pcap_src->cap_pipe_bytes_to_read = sizeof(pcap_src->pcapng_pipe_bh.block_total_length);
+        /* We don't have to worry about cap_pipe_read_mtx here */
+        g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
+        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.");
+            else
+                g_snprintf(errmsg, errmsgl, "Error on pipe block_total_length during open: %s.",
+                           g_strerror(errno));
+            goto error;
+        }
+    }
+#endif
+
+    pcap_src->cap_pipe_err = PIPOK;
+    pcap_src->cap_pipe_fd = fd;
+    pcapng_read_shb(pcap_src, hdr, errmsg, errmsgl);
+    return;
+
+error:
+    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg);
+    pcap_src->cap_pipe_err = PIPERR;
+    cap_pipe_close(fd, pcap_src->from_cap_socket);
+    pcap_src->cap_pipe_fd = -1;
+#ifdef _WIN32
+    pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
+#endif
+}
 
 /* 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
-cap_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, int errmsgl)
 {
     struct pcap_pkthdr  phdr;
     enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
@@ -2118,6 +2333,256 @@ cap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsg
     return -1;
 }
 
+static int
+pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl)
+{
+    enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
+           PD_ERR } result;
+#ifdef _WIN32
+#if !GLIB_CHECK_VERSION(2,31,18)
+    GTimeVal  wait_time;
+#endif
+    gpointer  q_status;
+    wchar_t  *err_str;
+#endif
+    ssize_t   b;
+    guint new_bufsize;
+
+#ifdef LOG_CAPTURE_VERBOSE
+    g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_pipe_dispatch");
+#endif
+
+    switch (pcap_src->cap_pipe_state) {
+
+    case STATE_EXPECT_REC_HDR:
+#ifdef _WIN32
+        if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
+#endif
+
+            pcap_src->cap_pipe_state = STATE_READ_REC_HDR;
+            pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_block_header_s);
+            pcap_src->cap_pipe_bytes_read = 0;
+
+#ifdef _WIN32
+            pcap_src->cap_pipe_buf = (char *) &pcap_src->pcapng_pipe_bh;
+            g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
+            g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
+        }
+#endif
+        /* Fall through */
+
+    case STATE_READ_REC_HDR:
+#ifdef _WIN32
+        if (pcap_src->from_cap_socket)
+#endif
+        {
+            b = cap_pipe_read(pcap_src->cap_pipe_fd, ((char *)&pcap_src->pcapng_pipe_bh)+pcap_src->cap_pipe_bytes_read,
+                 pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read, pcap_src->from_cap_socket);
+            if (b <= 0) {
+                if (b == 0)
+                    result = PD_PIPE_EOF;
+                else
+                    result = PD_PIPE_ERR;
+                break;
+            }
+            pcap_src->cap_pipe_bytes_read += b;
+        }
+#ifdef _WIN32
+        else {
+#if GLIB_CHECK_VERSION(2,31,18)
+            q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
+#else
+            g_get_current_time(&wait_time);
+            g_time_val_add(&wait_time, PIPE_READ_TIMEOUT);
+            q_status = g_async_queue_timed_pop(pcap_src->cap_pipe_done_q, &wait_time);
+#endif
+            if (pcap_src->cap_pipe_err == PIPEOF) {
+                result = PD_PIPE_EOF;
+                break;
+            } else if (pcap_src->cap_pipe_err == PIPERR) {
+                result = PD_PIPE_ERR;
+                break;
+            }
+            if (!q_status) {
+                return 0;
+            }
+        }
+#endif
+        if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read)
+            return 0;
+        result = PD_REC_HDR_READ;
+        break;
+
+    case STATE_EXPECT_DATA:
+#ifdef _WIN32
+        if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) {
+#endif
+
+            pcap_src->cap_pipe_state = STATE_READ_DATA;
+            pcap_src->cap_pipe_bytes_to_read = pcap_src->pcapng_pipe_bh.block_total_length
+                - sizeof(struct pcapng_block_header_s);
+            pcap_src->cap_pipe_bytes_read = 0;
+
+#ifdef _WIN32
+            pcap_src->cap_pipe_buf = pcap_src->cap_pipe_databuf;
+            g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
+            g_mutex_unlock(pcap_src->cap_pipe_read_mtx);
+        }
+#endif
+        /* Fall through */
+
+    case STATE_READ_DATA:
+#ifdef _WIN32
+        if (pcap_src->from_cap_socket)
+#endif
+        {
+            b = cap_pipe_read(pcap_src->cap_pipe_fd,
+                              pcap_src->cap_pipe_databuf+pcap_src->cap_pipe_bytes_read,
+                              pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read,
+                              pcap_src->from_cap_socket);
+            if (b <= 0) {
+                if (b == 0)
+                    result = PD_PIPE_EOF;
+                else
+                    result = PD_PIPE_ERR;
+                break;
+            }
+            pcap_src->cap_pipe_bytes_read += b;
+        }
+#ifdef _WIN32
+        else {
+
+#if GLIB_CHECK_VERSION(2,31,18)
+            q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT);
+#else
+            g_get_current_time(&wait_time);
+            g_time_val_add(&wait_time, PIPE_READ_TIMEOUT);
+            q_status = g_async_queue_timed_pop(pcap_src->cap_pipe_done_q, &wait_time);
+#endif /* GLIB_CHECK_VERSION(2,31,18) */
+            if (pcap_src->cap_pipe_err == PIPEOF) {
+                result = PD_PIPE_EOF;
+                break;
+            } else if (pcap_src->cap_pipe_err == PIPERR) {
+                result = PD_PIPE_ERR;
+                break;
+            }
+            if (!q_status) {
+                return 0;
+            }
+        }
+#endif /* _WIN32 */
+        if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read)
+            return 0;
+        result = PD_DATA_READ;
+        break;
+
+    default:
+        g_snprintf(errmsg, errmsgl, "pcapng_pipe_dispatch: invalid state");
+        result = PD_ERR;
+
+    } /* switch (pcap_src->cap_pipe_state) */
+
+    /*
+     * We've now read as much data as we were expecting, so process it.
+     */
+    switch (result) {
+
+    case PD_REC_HDR_READ:
+        if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_SHB) {
+            pcapng_read_shb(pcap_src, &pcap_src->pcapng_pipe_shb, errmsg, errmsgl);
+            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. */
+            pcap_src->pcapng_pipe_bh.block_type = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_type);
+            pcap_src->pcapng_pipe_bh.block_total_length = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_total_length);
+        }
+        if (pcap_src->pcapng_pipe_bh.block_total_length > pcap_src->cap_pipe_max_pkt_size) {
+            /*
+            * The record contains more data than the advertised/allowed in the
+            * pcapng header, do not try to read more data (do not change to
+            * 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_src->pcapng_pipe_bh.block_total_length);
+            break;
+        }
+
+        if (pcap_src->pcapng_pipe_bh.block_total_length > pcap_src->cap_pipe_databuf_size) {
+            /*
+            * Grow the buffer to the packet size, rounded up to a power of
+            * 2.
+            */
+            new_bufsize = pcap_src->pcapng_pipe_bh.block_total_length;
+            /*
+            * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+            */
+            new_bufsize--;
+            new_bufsize |= new_bufsize >> 1;
+            new_bufsize |= new_bufsize >> 2;
+            new_bufsize |= new_bufsize >> 4;
+            new_bufsize |= new_bufsize >> 8;
+            new_bufsize |= new_bufsize >> 16;
+            new_bufsize++;
+            pcap_src->cap_pipe_databuf = (guchar*)g_realloc(pcap_src->cap_pipe_databuf, new_bufsize);
+            pcap_src->cap_pipe_databuf_size = new_bufsize;
+        }
+
+        /* The record always has at least the block total length following the header */
+        if (pcap_src->pcapng_pipe_bh.block_total_length < sizeof(struct pcapng_block_header_s)+sizeof(guint32)) {
+            g_snprintf(errmsg, errmsgl, "malformed pcapng block_total_length < minimum");
+            pcap_src->cap_pipe_err = PIPEOF;
+            return -1;
+        }
+        pcap_src->cap_pipe_state = STATE_EXPECT_DATA;
+        return 0;
+
+    case PD_DATA_READ:
+        /* If this is a section header block then write out the parsed data with the received data */
+        if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_SHB) {
+            capture_loop_write_pcapng_shb((u_char *)pcap_src, pcap_src->cap_pipe_databuf);
+            pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
+            return 1;
+        }
+        if (use_threads) {
+            //capture_loop_queue_pcapng_block((u_char *)pcap_src, pcap_src->cap_pipe_databuf);
+            g_snprintf(errmsg, errmsgl,
+                    "capture_loop_queue_pcapng_block not implemented for threads.");
+            return -1;
+        } else {
+            capture_loop_write_pcapng_block((u_char *)pcap_src, pcap_src->cap_pipe_databuf);
+        }
+        pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
+        return 1;
+
+    case PD_PIPE_EOF:
+        pcap_src->cap_pipe_err = PIPEOF;
+        return -1;
+
+    case PD_PIPE_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,
+                   "Error reading from pipe: %s (error %d)",
+                   utf_16to8(err_str), GetLastError());
+        LocalFree(err_str);
+#else
+        g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s",
+                   g_strerror(errno));
+#endif
+        /* Fall through */
+    case PD_ERR:
+        break;
+    }
+
+    pcap_src->cap_pipe_err = PIPERR;
+    /* Return here rather than inside the switch to prevent GCC warning */
+    return -1;
+}
 
 /** Open the capture input file (pcap or capture pipe).
  *  Returns TRUE if it succeeds, FALSE otherwise. */
@@ -2199,34 +2664,16 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
                    "Could not allocate memory.");
             return FALSE;
         }
-        pcap_src->received = 0;
-        pcap_src->dropped = 0;
-        pcap_src->flushed = 0;
-        pcap_src->pcap_h = NULL;
+        memset(pcap_src, 0, sizeof(capture_src));
 #ifdef MUST_DO_SELECT
         pcap_src->pcap_fd = -1;
 #endif
-        pcap_src->pcap_err = FALSE;
         pcap_src->interface_id = i;
-        pcap_src->tid = NULL;
-        pcap_src->snaplen = 0;
         pcap_src->linktype = -1;
-        pcap_src->ts_nsec = FALSE;
-        pcap_src->from_cap_pipe = FALSE;
-        pcap_src->from_cap_socket = FALSE;
-        memset(&pcap_src->cap_pipe_hdr, 0, sizeof(struct pcap_hdr));
-        memset(&pcap_src->cap_pipe_rechdr, 0, sizeof(struct pcaprec_modified_hdr));
 #ifdef _WIN32
         pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE;
 #endif
         pcap_src->cap_pipe_fd = -1;
-        pcap_src->cap_pipe_modified = FALSE;
-        pcap_src->cap_pipe_byte_swapped = FALSE;
-#ifdef _WIN32
-        pcap_src->cap_pipe_buf = NULL;
-#endif
-        pcap_src->cap_pipe_bytes_to_read = 0;
-        pcap_src->cap_pipe_bytes_read = 0;
         pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR;
         pcap_src->cap_pipe_err = PIPOK;
 #ifdef _WIN32
@@ -2475,6 +2922,11 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err
             GString *cpu_info_str;
             GString *os_info_str;
 
+            /* If our source is also pcapng then we will just write the source blocks */
+            pcap_src = g_array_index(ld->pcaps, capture_src *, 0);
+            if (pcap_src->from_pcapng) {
+                return TRUE;
+            }
             cpu_info_str = g_string_new("");
             os_info_str = g_string_new("");
             get_cpu_info(cpu_info_str);
@@ -2649,7 +3101,11 @@ capture_loop_dispatch(loop_data *ld,
              * "select()" says we can read from the pipe without blocking
              */
 #endif
-            inpkts = cap_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len);
+            if (pcap_src->from_pcapng) {
+                inpkts = pcapng_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len);
+            } else {
+                inpkts = pcap_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len);
+            }
             if (inpkts < 0) {
                 ld->go = FALSE;
             }
@@ -2978,6 +3434,12 @@ do_file_switch_or_stop(capture_options *capture_opts,
                 GString *cpu_info_str;
                 GString *os_info_str;
 
+                /* If our source is also pcapng then we will just write the source blocks */
+                pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0);
+                if (pcap_src->from_pcapng) {
+                    return TRUE;
+                }
+
                 cpu_info_str = g_string_new("");
                 os_info_str = g_string_new("");
                 get_cpu_info(cpu_info_str);
@@ -3648,6 +4110,113 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname,
     }
 }
 
+/* one pcapng block was captured, process it */
+static void
+capture_loop_write_pcapng_shb(u_char *pcap_src_p, const u_char *pd)
+{
+    capture_src *pcap_src = (capture_src *) (void *) pcap_src_p;
+    int          err;
+
+    if (!global_capture_opts.use_pcapng) {
+        return;
+    }
+    /* 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. */
+    if (!global_ld.go) {
+        pcap_src->flushed++;
+        return;
+    }
+
+    if (global_ld.pdh) {
+        gboolean successful;
+
+        /* We're supposed to write the packet to a file; do so.
+           If this fails, set "ld->go" to FALSE, to stop the capture, and set
+           "ld->err" to the error. */
+        successful = pcapng_rewrite_session_header_block(global_ld.pdh,
+                                                pcap_src->pcapng_pipe_shb.magic,
+                                                pcap_src->pcapng_pipe_shb.version_major,
+                                                pcap_src->pcapng_pipe_shb.version_minor,
+                                                pcap_src->pcapng_pipe_bh.block_total_length,
+                                                pd,
+                                                &global_ld.bytes_written, &err);
+
+        fflush(global_ld.pdh);
+        if (!successful) {
+            global_ld.go = FALSE;
+            global_ld.err = err;
+#if 0
+            pcap_src->dropped++;
+        } 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.",
+                   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;
+            }
+#endif /* 0 */
+        }
+    }
+}
+
+
+/* one pcapng block was captured, process it */
+static void
+capture_loop_write_pcapng_block(u_char *pcap_src_p, const u_char *pd)
+{
+    capture_src *pcap_src = (capture_src *) (void *) pcap_src_p;
+    int          err;
+
+    if (!global_capture_opts.use_pcapng) {
+        return;
+    }
+    /* 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. */
+    if (!global_ld.go) {
+        pcap_src->flushed++;
+        return;
+    }
+
+    if (global_ld.pdh) {
+        gboolean successful;
+
+        /* We're supposed to write the packet to a file; do so.
+           If this fails, set "ld->go" to FALSE, to stop the capture, and set
+           "ld->err" to the error. */
+        successful = pcapng_rewrite_general_block(global_ld.pdh,
+                                                pcap_src->pcapng_pipe_shb.magic,
+                                                pcap_src->pcapng_pipe_bh.block_type,
+                                                pcap_src->pcapng_pipe_bh.block_total_length,
+                                                pd,
+                                                &global_ld.bytes_written, &err);
+
+        fflush(global_ld.pdh);
+        if (!successful) {
+            global_ld.go = FALSE;
+            global_ld.err = err;
+            pcap_src->dropped++;
+        } else if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_EPB) {
+#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.",
+                   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;
+            }
+        }
+    }
+}
 
 /* one packet was captured, process it */
 static void
diff --git a/wiretap/pcapng.h b/wiretap/pcapng.h
index 36e14a4..1c2cf87 100644
--- a/wiretap/pcapng.h
+++ b/wiretap/pcapng.h
@@ -25,6 +25,12 @@
 #include "wtap.h"
 #include "ws_symbol_export.h"
 
+#define PCAPNG_MAGIC         0x1A2B3C4D
+#define PCAPNG_SWAPPED_MAGIC 0xD4C3B2A1
+
+#define PCAPNG_MAJOR_VERSION 1
+#define PCAPNG_MINOR_VERSION 0
+
 /* pcapng: common block header file encoding for every block type */
 typedef struct pcapng_block_header_s {
     guint32 block_type;
diff --git a/writecap/pcapio.c b/writecap/pcapio.c
index abc0af9..2fc253a 100644
--- a/writecap/pcapio.c
+++ b/writecap/pcapio.c
@@ -284,6 +284,85 @@ pcapng_write_string_option(FILE* pfile,
         return TRUE;
 }
 
+extern gboolean
+pcapng_rewrite_general_block(FILE* pfile,
+                           guint32 magic,
+                           guint32 block_type,
+                           guint32 block_total_length,
+                           const char *data,
+                           guint64 *bytes_written,
+                           int *err)
+{
+        guint32 bytes_to_write = block_total_length;
+
+        /* write the general block header */
+        if (magic != PCAPNG_MAGIC) {
+            block_type = GUINT32_SWAP_LE_BE(block_type);
+            block_total_length = GUINT32_SWAP_LE_BE(block_total_length);
+        }
+
+        if (!write_to_file(pfile, (const guint8*)&block_type, sizeof(guint32), bytes_written, err)) {
+                return FALSE;
+        }
+        if (!write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err)) {
+                return FALSE;
+        }
+
+        bytes_to_write -= 2*sizeof(guint32);
+
+        /* write the rest of the data including trailing block total length */
+        return write_to_file(pfile, (const guint8*)data, bytes_to_write, bytes_written, err);
+}
+
+gboolean
+pcapng_rewrite_session_header_block(FILE* pfile,  /**< Write information */
+                                    guint32 magic,
+                                    guint16 version_major,
+                                    guint16 version_minor,
+                                    guint32 block_total_length,
+                                    const char *data,
+                                    guint64 *bytes_written, /**< Number of written bytes */
+                                    int *err /**< Error type */
+                                    )
+{
+        guint32 write_total_length;
+        guint32 bytes_to_write = block_total_length;
+        struct shb shb;
+
+        /* Size of base header */
+        if (magic == PCAPNG_MAGIC) {
+            write_total_length = block_total_length;
+
+            /* write shb header */
+            shb.block_type = SECTION_HEADER_BLOCK_TYPE;
+            shb.block_total_length = write_total_length;
+            shb.byte_order_magic = magic;
+            shb.major_version = version_major;
+            shb.minor_version = version_minor;
+            shb.section_length = ~0ULL;
+        }
+        else {
+            write_total_length = GUINT32_SWAP_LE_BE(block_total_length);
+
+            /* write shb header */
+            shb.block_type = SECTION_HEADER_BLOCK_TYPE;
+            shb.block_total_length = write_total_length;
+            shb.byte_order_magic = PCAPNG_SWAPPED_MAGIC;
+            shb.major_version = GUINT16_SWAP_LE_BE(version_major);
+            shb.minor_version = GUINT16_SWAP_LE_BE(version_minor);
+            shb.section_length = ~0ULL;
+        }
+
+        if (!write_to_file(pfile, (const guint8*)&shb, sizeof(struct shb), bytes_written, err)) {
+                return FALSE;
+        }
+
+        bytes_to_write -= sizeof(struct shb);
+
+        /* write the rest of the data including trailing block total length */
+        return write_to_file(pfile, (const guint8*)data, bytes_to_write, bytes_written, err);
+}
+
 gboolean
 pcapng_write_session_header_block(FILE* pfile,
                                   const char *comment,
diff --git a/writecap/pcapio.h b/writecap/pcapio.h
index 9263024..f51c62e 100644
--- a/writecap/pcapio.h
+++ b/writecap/pcapio.h
@@ -43,6 +43,26 @@ libpcap_write_packet(FILE* pfile,
 
 /* Writing pcap-ng files */
 
+extern gboolean
+pcapng_rewrite_general_block(FILE* pfile,
+                           guint32 magic,
+                           guint32 block_type,
+                           guint32 block_total_length,
+                           const char *data,
+                           guint64 *bytes_written,
+                           int *err);
+
+extern gboolean
+pcapng_rewrite_session_header_block(FILE* pfile,  /**< Write information */
+                                    guint32 magic,
+                                    guint16 version_major,
+                                    guint16 version_minor,
+                                    guint32 block_total_length,
+                                    const char *data,
+                                    guint64 *bytes_written, /**< Number of written bytes */
+                                    int *err /**< Error type */
+                                    );
+
 /** Write a section header block (SHB)
  *
  */