Dumpcap: Fix writing SHBs and IDBs.

If we have a single capture source and that capture source is pcapng and
we're writing a pcapng file, do the following:

- Pass its SHB and IDBs through unmodified. Don't save or write command
  line interface IDBs.

- Save the most recent SHB and IDBs so that we can write them when we're
  writing multiple output files.

If we have multiple capture sources, do the following:

- Write Dumpcap's SHB.

- Keep a global list of IDBs, consisting of both command line interfaces
  and IDBs read from pcapng sources.

- When reading an EPB or ISB, remap its local interface number to its
  corresponding global number.

Add Dumpcap pcapng section tests. Make the application IDs in the
"many_interfaces" captures unique.

Change-Id: I2005934c1f83d839727421960005f106d6c682dd
Reviewed-on: https://code.wireshark.org/review/30085
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2018-10-08 13:25:36 -07:00
parent 377f5d0de7
commit f300676bec
6 changed files with 473 additions and 157 deletions

431
dumpcap.c
View File

@ -227,8 +227,7 @@ typedef struct _pcap_pipe_info {
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 */
@ -284,6 +283,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.
*/
@ -297,6 +303,10 @@ typedef struct _loop_data {
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;
@ -329,7 +339,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;
@ -1846,6 +1856,7 @@ cap_pipe_open_live(char *pipename,
/* 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;
@ -1967,7 +1978,7 @@ pcapng_read_shb(capture_src *pcap_src,
char *errmsg,
int errmsgl)
{
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)
@ -1997,8 +2008,8 @@ 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");
@ -2034,29 +2045,101 @@ pcapng_read_shb(capture_src *pcap_src,
return 0;
}
/* Save SHB and IDB blocks to playback whenever we change output files. */
/* The list is saved in reverse order of blocks added */
/*
* Save IDB blocks for playback whenever we change output files.
* Rewrite EPB and ISB interface IDs.
*/
static gboolean
pcapng_block_save(capture_src *pcap_src)
pcapng_adjust_block(loop_data *ld, 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;
}
switch(bh->block_type) {
case BLOCK_TYPE_SHB:
{
g_rw_lock_writer_lock (&ld->saved_shb_idb_lock);
if (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(ld->saved_shb);
ld->saved_shb = (guint8 *) g_memdup(pcap_src->cap_pipe_databuf, bh->block_total_length);
gpointer data = g_malloc(bh->block_total_length);
if (data == NULL) {
return FALSE;
}
memcpy(data, pcap_src->cap_pipe_databuf, bh->block_total_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 < ld->saved_idbs->len; i++) {
saved_idb_t *idb_source = &g_array_index(ld->saved_idbs, saved_idb_t, i);
g_free(idb_source->idb);
}
g_array_set_size(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(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_rw_lock_writer_unlock (&ld->saved_shb_idb_lock);
pcapng->saved_blocks = g_list_prepend(pcapng->saved_blocks, data);
g_array_set_size(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, 0);
}
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(pcap_src->cap_pipe_databuf, idb_source.idb_len);
g_rw_lock_writer_lock (&ld->saved_shb_idb_lock);
g_array_append_val(ld->saved_idbs, idb_source);
guint32 iface_id = ld->saved_idbs->len - 1;
g_rw_lock_writer_unlock (&ld->saved_shb_idb_lock);
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: saved 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);
}
break;
case BLOCK_TYPE_EPB:
case BLOCK_TYPE_ISB:
{
if (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, pcap_src->cap_pipe_databuf + sizeof(struct pcapng_block_header_s), 4);
if (iface_id < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len) {
memcpy(pcap_src->cap_pipe_databuf + 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;
}
return TRUE;
}
@ -2545,7 +2628,7 @@ 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)) {
if (!pcapng_adjust_block(ld, pcap_src)) {
g_snprintf(errmsg, errmsgl, "pcapng_pipe_dispatch block save failed");
return -1;
}
@ -2656,6 +2739,7 @@ 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_malloc0(sizeof (capture_src));
@ -2664,6 +2748,19 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld,
"Could not allocate memory.");
return FALSE;
}
/*
* 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
@ -2801,6 +2898,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 */
@ -2845,8 +2953,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. */
@ -2899,15 +3007,124 @@ 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);
char *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_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: wrote dumpcap SHB %d", G_STRFUNC, successful);
g_string_free(cpu_info_str, TRUE);
g_free(appname);
}
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");
@ -2929,58 +3146,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;
@ -3471,9 +3640,6 @@ static time_t get_next_time_interval(int interval_s) {
static gboolean
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) {
@ -3491,70 +3657,15 @@ do_file_switch_or_stop(capture_options *capture_opts)
/* File switch succeeded: reset the conditions */
global_ld.bytes_written = 0;
global_ld.packets_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) {
struct pcapng_block_header_s *bh = (struct pcapng_block_header_s *) list->data;
successful = pcapng_write_block(global_ld.pdh,
(const guint8 *) bh,
bh->block_total_length,
&global_ld.bytes_written, &global_ld.err);
list = g_list_next(list);
}
pcap_src->cap_pipe_info.pcapng.saved_blocks = g_list_reverse(rlist);
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;
@ -4222,6 +4333,15 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const struct pcapng_block_he
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;
@ -4242,15 +4362,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
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)
@ -4298,7 +4418,7 @@ 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
capture_loop_wrote_one_packet(pcap_src);
@ -4418,8 +4538,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 */
@ -4689,8 +4809,11 @@ real_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 */
@ -5382,13 +5505,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);

View File

@ -9,7 +9,6 @@
#
'''Subprocess test case superclass'''
import config
import difflib
import io
import os
@ -35,6 +34,19 @@ def cat_dhcp_command(mode):
sd_cmd += os.path.join(this_dir, 'util_dump_dhcp_pcap.py ' + mode)
return sd_cmd
def cat_cap_file_command(cap_files):
'''Create a command string for dumping one or more capture files to stdout'''
# XXX Do this in Python in a thread?
if isinstance(cap_files, str):
cap_files = [ cap_files ]
quoted_paths = ' '.join('"{}"'.format(cap_file) for cap_file in cap_files)
if sys.platform.startswith('win32'):
# https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb491026(v=technet.10)
# says that the `type` command "displays the contents of a text
# file." Copy to the console instead.
return 'copy {} CON'.format(quoted_paths)
return 'cat {}'.format(quoted_paths)
class LoggingPopen(subprocess.Popen):
'''Run a process using subprocess.Popen. Capture and log its output.

View File

@ -9,18 +9,20 @@
#
'''Capture tests'''
import fixtures
import glob
import hashlib
import os
import subprocess
import subprocesstest
import sys
import time
import uuid
import fixtures
capture_duration = 5
testout_pcap = 'testout.pcap'
testout_pcapng = 'testout.pcapng'
snapshot_len = 96
@fixtures.fixture
@ -71,6 +73,7 @@ def traffic_generator():
def wireshark_k(wireshark_command):
return tuple(list(wireshark_command) + ['-k'])
def capture_command(*cmd_args, shell=False):
if type(cmd_args[0]) != str:
# Assume something like ['wireshark', '-k']
@ -326,6 +329,165 @@ def check_dumpcap_ringbuffer_stdin(cmd_dumpcap):
return check_dumpcap_ringbuffer_stdin_real
@fixtures.fixture
def check_dumpcap_pcapng_sections(cmd_dumpcap, cmd_tshark, capture_file):
if sys.platform == 'win32':
fixtures.skip('Test requires OS fifo support.')
def check_dumpcap_pcapng_sections_real(self, multi_input=False, multi_output=False):
# Make sure we always test multiple SHBs in an input.
in_files_l = [ [
capture_file('many_interfaces.pcapng.1'),
capture_file('many_interfaces.pcapng.2')
] ]
if multi_input:
in_files_l.append([ capture_file('many_interfaces.pcapng.3') ])
fifo_files = []
fifo_procs = []
# Default values for our validity tests
check_val_d = {
'filename': None,
'packet_count': 0,
'idb_count': 0,
'ua_pt1_count': 0,
'ua_pt2_count': 0,
'ua_pt3_count': 0,
'ua_dc_count': 0,
}
check_vals = [ check_val_d ]
for in_files in in_files_l:
fifo_file = self.filename_from_id('dumpcap_pcapng_sections_{}.fifo'.format(len(fifo_files) + 1))
fifo_files.append(fifo_file)
# If a previous test left its fifo laying around, e.g. from a failure, remove it.
try:
os.unlink(fifo_file)
except: pass
os.mkfifo(fifo_file)
cat_cmd = subprocesstest.cat_cap_file_command(in_files)
fifo_procs.append(self.startProcess(('{0} > {1}'.format(cat_cmd, fifo_file)), shell=True))
if multi_output:
rb_unique = 'sections_rb_' + uuid.uuid4().hex[:6] # Random ID
testout_glob = '{}.{}_*.pcapng'.format(self.id(), rb_unique)
testout_file = '{}.{}.pcapng'.format(self.id(), rb_unique)
check_vals.append(check_val_d)
# check_vals[]['filename'] will be filled in below
else:
testout_file = self.filename_from_id(testout_pcapng)
check_vals[0]['filename'] = testout_file
# Capture commands
if not multi_input and not multi_output:
# Passthrough SHBs, single output file
capture_cmd_args = (
'-i', fifo_files[0],
'-w', testout_file
)
check_vals[0]['packet_count'] = 79
check_vals[0]['idb_count'] = 22
check_vals[0]['ua_pt1_count'] = 1
check_vals[0]['ua_pt2_count'] = 1
elif not multi_input and multi_output:
# Passthrough SHBs, multiple output files
capture_cmd_args = (
'-i', fifo_files[0],
'-w', testout_file,
'-a', 'files:2',
'-b', 'packets:53'
)
check_vals[0]['packet_count'] = 53
check_vals[0]['idb_count'] = 22
check_vals[0]['ua_pt1_count'] = 1
check_vals[1]['packet_count'] = 26
check_vals[1]['idb_count'] = 22
check_vals[1]['ua_pt1_count'] = 1
check_vals[1]['ua_pt2_count'] = 1
elif multi_input and not multi_output:
# Dumpcap SHBs, single output file
capture_cmd_args = (
'-i', fifo_files[0],
'-i', fifo_files[1],
'-w', testout_file
)
check_vals[0]['packet_count'] = 88
check_vals[0]['idb_count'] = 35
check_vals[0]['ua_dc_count'] = 1
else:
# Dumpcap SHBs, multiple output files
capture_cmd_args = (
'-i', fifo_files[0],
'-i', fifo_files[1],
'-w', testout_file,
'-a', 'files:2',
'-b', 'packets:53'
)
check_vals[0]['packet_count'] = 53
check_vals[0]['idb_count'] = 35
check_vals[0]['ua_dc_count'] = 1
check_vals[1]['packet_count'] = 35
check_vals[1]['idb_count'] = 35
check_vals[1]['ua_dc_count'] = 1
capture_cmd = capture_command(cmd_dumpcap, *capture_cmd_args)
capture_proc = self.runProcess(capture_cmd)
for fifo_proc in fifo_procs: fifo_proc.kill()
rb_files = []
if multi_output:
rb_files = sorted(glob.glob(testout_glob))
self.assertEqual(len(rb_files), 2)
check_vals[0]['filename'] = rb_files[0]
check_vals[1]['filename'] = rb_files[1]
for rbf in rb_files:
self.cleanup_files.append(rbf)
self.assertTrue(os.path.isfile(rbf))
returncode = capture_proc.returncode
self.assertEqual(returncode, 0)
if (returncode != 0):
return
# Output tests
if not multi_input and not multi_output:
# Check strict bit-for-bit passthrough.
in_hash = hashlib.sha256()
out_hash = hashlib.sha256()
for in_file in in_files_l[0]:
in_cap_file = capture_file(in_file)
with open(in_cap_file, 'rb') as f:
in_hash.update(f.read())
with open(testout_file, 'rb') as f:
out_hash.update(f.read())
self.assertEqual(in_hash.hexdigest(), out_hash.hexdigest())
# many_interfaces.pcapng.1 : 64 packets written by "Passthrough test #1"
# many_interfaces.pcapng.2 : 15 packets written by "Passthrough test #2"
# many_interfaces.pcapng.3 : 9 packets written by "Passthrough test #3"
for check_val in check_vals:
self.checkPacketCount(check_val['packet_count'], cap_file=check_val['filename'])
tshark_proc = self.runProcess(capture_command(cmd_tshark,
'-r', check_val['filename'],
'-V',
'-X', 'read_format:MIME Files Format'
))
# XXX Are there any other sanity checks we should run?
self.assertEqual(self.countOutput('Block: Interface Description Block',
proc=tshark_proc), check_val['idb_count'])
self.assertEqual(self.countOutput('Option: User Application = Passthrough test #1',
proc=tshark_proc), check_val['ua_pt1_count'])
self.assertEqual(self.countOutput('Option: User Application = Passthrough test #2',
proc=tshark_proc), check_val['ua_pt2_count'])
self.assertEqual(self.countOutput('Option: User Application = Passthrough test #3',
proc=tshark_proc), check_val['ua_pt3_count'])
self.assertEqual(self.countOutput('Option: User Application = Dumpcap \(Wireshark\)',
proc=tshark_proc), check_val['ua_dc_count'])
return check_dumpcap_pcapng_sections_real
@fixtures.mark_usefixtures('test_env')
@fixtures.uses_fixtures
class case_wireshark_capture(subprocesstest.SubprocessTestCase):
@ -416,7 +578,6 @@ class case_dumpcap_autostop(subprocesstest.SubprocessTestCase):
@fixtures.uses_fixtures
class case_dumpcap_ringbuffer(subprocesstest.SubprocessTestCase):
# duration, interval, filesize, packets, files
# Need a function that finds ringbuffer file names.
def test_dumpcap_ringbuffer_filesize(self, check_dumpcap_ringbuffer_stdin):
'''Capture from stdin using Dumpcap and write multiple files until we reach a file size limit'''
check_dumpcap_ringbuffer_stdin(self, filesize=15)
@ -424,3 +585,23 @@ class case_dumpcap_ringbuffer(subprocesstest.SubprocessTestCase):
def test_dumpcap_ringbuffer_packets(self, check_dumpcap_ringbuffer_stdin):
'''Capture from stdin using Dumpcap and write multiple files until we reach a packet limit'''
check_dumpcap_ringbuffer_stdin(self, packets=47) # Last prime before 50. Arbitrary.
@fixtures.mark_usefixtures('base_env')
@fixtures.uses_fixtures
class case_dumpcap_pcapng_sections(subprocesstest.SubprocessTestCase):
def test_dumpcap_pcapng_single_in_single_out(self, check_dumpcap_pcapng_sections):
'''Capture from a single pcapng source using Dumpcap and write a single file'''
check_dumpcap_pcapng_sections(self)
def test_dumpcap_pcapng_single_in_multi_out(self, check_dumpcap_pcapng_sections):
'''Capture from a single pcapng source using Dumpcap and write two files'''
check_dumpcap_pcapng_sections(self, multi_output=True)
def test_dumpcap_pcapng_multi_in_single_out(self, check_dumpcap_pcapng_sections):
'''Capture from two pcapng sources using Dumpcap and write a single file'''
check_dumpcap_pcapng_sections(self, multi_input=True)
def test_dumpcap_pcapng_multi_in_multi_out(self, check_dumpcap_pcapng_sections):
'''Capture from two pcapng sources using Dumpcap and write two files'''
check_dumpcap_pcapng_sections(self, multi_input=True, multi_output=True)