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) * */
- Follow-Ups:
- Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap
- From: Roland Knall
- Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap
- From: Graham Bloice
- Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap
- Prev by Date: Re: [Wireshark-dev] reduce tshark memory usage
- Next by Date: Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap
- Previous by thread: Re: [Wireshark-dev] Top of trunk failing to build with make on SuSE 12.2(and other OSes)
- Next by thread: Re: [Wireshark-dev] Adding pcap-ng pipe support to dumpcap
- Index(es):