From ddcdbb03d7073958add33192db1d775cbdad3ded Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 19 Dec 2014 23:53:46 +0100 Subject: [RFC] Implement Export PDU for tshark This patch introduces the "-U tap_name[,filter]" tshark option and is similar to the "Export PDUs as file" option in Wireshark. Wireshark implements this feature by reopening a capture file, applying a tap and finally opening the temporary file. Since tshark knows in advance that a PDU export is needed, it can optimize by not creating the temporary file and perform the export at the first opportunity. This patch splits the opening/tapping functionality from error reporting since tshark does not need a temp file and has no dialogs. The capture file comment is now specified explicitly as there is no "current file" anymore if the tap is running without active file. TODO: - Do not '#ifdef 0' the GTK/Qt functionality, split the file to avoid a linking error with tshark. - Why is pkt_encap modifiable? Both Wireshark Qt and GTK set WTAP_ENCAP_WIRESHARK_UPPER_PDU, what is the idea behind this? - Review whether it is acceptable to overwrite save_file in tshark. - Add documentation (tshark manpage). Bug: 3444 Change-Id: Ie159495d42c32c2ba7400f2991b7b8185b3fda09 --- tshark.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++-- ui/tap_export_pdu.c | 82 ++++++++++++++++++++++++++++++++++++++++------------- ui/tap_export_pdu.h | 28 ++++++++++++++++++ 3 files changed, 164 insertions(+), 22 deletions(-) diff --git a/tshark.c b/tshark.c index f96f49b930..738a631a89 100644 --- a/tshark.c +++ b/tshark.c @@ -98,6 +98,7 @@ #include "ui/util.h" #include "ui/ui_util.h" #include "ui/cli/tshark-tap.h" +#include "ui/tap_export_pdu.h" #include "version_info.h" #include "register.h" #include @@ -380,6 +381,7 @@ print_usage(FILE *output) fprintf(output, " -W n Save extra information in the file, if supported.\n"); fprintf(output, " n = write network address resolution information\n"); fprintf(output, " -X : eXtension options, see the man page for details\n"); + fprintf(output, " -U tap_name[,filter] PDUs export mode, see the man page for details\n"); fprintf(output, " -z various statistics, see the man page for details\n"); fprintf(output, " --capture-comment \n"); fprintf(output, " add a capture comment to the newly created\n"); @@ -1009,11 +1011,13 @@ main(int argc, char *argv[]) int log_flags; int optind_initial; gchar *output_only = NULL; + gchar **pdu_export_args = NULL; + exp_pdu_t exp_pdu_tap_data; /* the leading - ensures that getopt() does not permute the argv[] entries we have to make sure that the first getopt() preserves the content of argv[] for the subsequent getopt_long() call */ -#define OPTSTRING "-2" OPTSTRING_CAPTURE_COMMON "C:d:e:E:F:gG:hH:" "K:lnN:o:O:PqQr:R:S:t:T:u:vVw:W:xX:Y:z:" +#define OPTSTRING "-2" OPTSTRING_CAPTURE_COMMON "C:d:e:E:F:gG:hH:" "K:lnN:o:O:PqQr:R:S:t:T:u:U:vVw:W:xX:Y:z:" static const char optstring[] = OPTSTRING; @@ -1630,6 +1634,13 @@ main(int argc, char *argv[]) epan_cleanup(); return 0; } + case 'U': /* Export PDUs to file */ + if (!*optarg) { + cmdarg_err("Tap name is required!"); + return 1; + } + pdu_export_args = g_strsplit(optarg, ",", 2); + break; case 'O': /* Only output these protocols */ /* already processed; just ignore it now */ break; @@ -2066,6 +2077,55 @@ main(int argc, char *argv[]) } } + /* PDU export requested. Take the ownership of the '-w' file, apply tap + * filters and start tapping. */ + if (pdu_export_args) { + const char *exp_pdu_filename; + const char *exp_pdu_tap_name = pdu_export_args[0]; + const char *exp_pdu_filter = pdu_export_args[1]; /* may be NULL to disable filter */ + char *exp_pdu_error; + int exp_fd; + + if (!cf_name) { + cmdarg_err("PDUs export requires a capture file (specify with -r)."); + return 1; + } + /* Take ownership of the '-w' output file. */ +#ifdef HAVE_LIBPCAP + exp_pdu_filename = global_capture_opts.save_file; + global_capture_opts.save_file = NULL; +#else + exp_pdu_filename = output_file_name; + output_file_name = NULL; +#endif + if (exp_pdu_filename == NULL) { + cmdarg_err("PDUs export requires an output file (-w)."); + return 1; + } + + exp_pdu_error = exp_pdu_pre_open(exp_pdu_tap_name, exp_pdu_filter, + &exp_pdu_tap_data); + if (exp_pdu_error) { + cmdarg_err("Cannot register tap: %s", exp_pdu_error); + g_free(exp_pdu_error); + return 2; + } + + exp_fd = ws_open(exp_pdu_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + if (exp_fd == -1) { + cmdarg_err("%s: %s", exp_pdu_filename, file_open_error_message(errno, TRUE)); + return 2; + } + + /* Activate the export PDU tap */ + err = exp_pdu_open(&exp_pdu_tap_data, exp_fd, + g_strdup_printf("Dump of PDUs from %s", cf_name)); + if (err != 0) { + cmdarg_err("Failed to start the PDU export: %s", g_strerror(err)); + return 2; + } + } + /* We have to dissect each packet if: we're printing information about each packet; @@ -2074,8 +2134,11 @@ main(int argc, char *argv[]) we're using a display filter on the packets; + we're exporting PDUs; + we're using any taps that need dissection. */ - do_dissection = print_packet_info || rfcode || dfcode || tap_listeners_require_dissection(); + do_dissection = print_packet_info || rfcode || dfcode || pdu_export_args || + tap_listeners_require_dissection(); if (cf_name) { /* @@ -2112,6 +2175,15 @@ main(int argc, char *argv[]) read some packets; however, we exit with an error status. */ exit_status = 2; } + + if (pdu_export_args) { + err = exp_pdu_close(&exp_pdu_tap_data); + if (err) { + cmdarg_err("%s", wtap_strerror(err)); + exit_status = 2; + } + g_strfreev(pdu_export_args); + } } else { /* No capture file specified, so we're supposed to do a live capture or get a list of link-layer types for a live capture device; diff --git a/ui/tap_export_pdu.c b/ui/tap_export_pdu.c index 2b9b64e113..8f0abf328d 100644 --- a/ui/tap_export_pdu.c +++ b/ui/tap_export_pdu.c @@ -91,13 +91,10 @@ export_pdu_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, co return FALSE; /* Do not redraw */ } -static void -exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) +int +exp_pdu_open(exp_pdu_t *exp_pdu_tap_data, int fd, char *comment) { - int import_file_fd; - char *tmpname, *capfile_name; int err; - /* pcapng defs */ wtapng_section_t *shb_hdr; wtapng_iface_descriptions_t *idb_inf; @@ -105,10 +102,6 @@ exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) GString *os_info_str; char appname[100]; - /* Choose a random name for the temporary import buffer */ - import_file_fd = create_tempfile(&tmpname, "Wireshark_PDU_"); - capfile_name = g_strdup(tmpname); - /* Create data for SHB */ os_info_str = g_string_new(""); get_os_version_info(os_info_str); @@ -118,7 +111,7 @@ exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) shb_hdr = g_new(wtapng_section_t,1); shb_hdr->section_length = -1; /* options */ - shb_hdr->opt_comment = g_strdup_printf("Dump of PDUs from %s", cfile.filename); + shb_hdr->opt_comment = comment; shb_hdr->shb_hardware = NULL; /* UTF-8 string containing the * description of the hardware used to create this section. */ @@ -155,8 +148,41 @@ exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) g_array_append_val(idb_inf->interface_data, int_data); - exp_pdu_tap_data->wdh = wtap_dump_fdopen_ng(import_file_fd, WTAP_FILE_TYPE_SUBTYPE_PCAPNG, WTAP_ENCAP_WIRESHARK_UPPER_PDU, WTAP_MAX_PACKET_SIZE, FALSE, shb_hdr, idb_inf, &err); + exp_pdu_tap_data->wdh = wtap_dump_fdopen_ng(fd, WTAP_FILE_TYPE_SUBTYPE_PCAPNG, WTAP_ENCAP_WIRESHARK_UPPER_PDU, WTAP_MAX_PACKET_SIZE, FALSE, shb_hdr, idb_inf, &err); if (exp_pdu_tap_data->wdh == NULL) { + g_assert(err != 0); + return err; + } + + return 0; +} + +int +exp_pdu_close(exp_pdu_t *exp_pdu_tap_data) +{ + int err = 0; + if (!wtap_dump_close(exp_pdu_tap_data->wdh, &err)) + g_assert(err != 0); + + remove_tap_listener(exp_pdu_tap_data); + return err; +} + +#if 0 +static void +exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) +{ + int import_file_fd; + char *tmpname, *capfile_name; + int err; + + /* Choose a random name for the temporary import buffer */ + import_file_fd = create_tempfile(&tmpname, "Wireshark_PDU_"); + capfile_name = g_strdup(tmpname); + + err = exp_pdu_open(exp_pdu_tap_data, import_file_fd, + g_strdup_printf("Dump of PDUs from %s", cfile.filename)); + if (err != 0) { open_failure_alert_box(capfile_name, err, TRUE); goto end; } @@ -166,12 +192,10 @@ exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) cf_retap_packets(&cfile); - if (!wtap_dump_close(exp_pdu_tap_data->wdh, &err)) { + if (!exp_pdu_close(exp_pdu_tap_data, &err)) { write_failure_alert_box(capfile_name, err); } - remove_tap_listener(exp_pdu_tap_data); - /* XXX: should this use the open_routine type in the cfile instead of WTAP_TYPE_AUTO? */ if (cf_open(&cfile, capfile_name, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) { open_failure_alert_box(capfile_name, err, FALSE); @@ -197,30 +221,48 @@ exp_pdu_file_open(exp_pdu_t *exp_pdu_tap_data) end: g_free(capfile_name); } +#endif -gboolean -do_export_pdu(const char *filter, gchar *tap_name, exp_pdu_t *exp_pdu_tap_data) +char * +exp_pdu_pre_open(const char *tap_name, const char *filter, exp_pdu_t *exp_pdu_tap_data) { GString *error_string; + /* XXX: can we always assume WTAP_ENCAP_WIRESHARK_UPPER_PDU? */ + exp_pdu_tap_data->pkt_encap = wtap_wtap_encap_to_pcap_encap(WTAP_ENCAP_WIRESHARK_UPPER_PDU); + /* Register this tap listener now */ error_string = register_tap_listener(tap_name, /* The name of the tap we want to listen to */ exp_pdu_tap_data, /* instance identifier/pointer to a struct holding * all state variables */ filter, /* pointer to a filter string */ - TL_REQUIRES_NOTHING, /* flags for the tap listener */ + TL_REQUIRES_PROTO_TREE, /* flags for the tap listener */ NULL, export_pdu_packet, NULL); - if (error_string){ + if (error_string != NULL) + return g_string_free(error_string, FALSE); + + return NULL; +} + +gboolean +do_export_pdu(const char *filter, gchar *tap_name, exp_pdu_t *exp_pdu_tap_data) +{ +#if 0 + char *error; + error = exp_pdu_register_tap(tap_name, filter, exp_pdu_tap_data); + if (error){ /* Error. We failed to attach to the tap. Clean up */ - simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str); - g_string_free(error_string, TRUE); + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error); + g_free(error); return FALSE; } exp_pdu_file_open(exp_pdu_tap_data); return TRUE; +#endif + return FALSE; } /* diff --git a/ui/tap_export_pdu.h b/ui/tap_export_pdu.h index df6b5bad45..5c2de947d2 100644 --- a/ui/tap_export_pdu.h +++ b/ui/tap_export_pdu.h @@ -32,8 +32,36 @@ typedef struct _exp_pdu_t { wtap_dumper* wdh; } exp_pdu_t; +/** + * Filters the current opened capture file into a temporary file. On success, + * TRUE is returned and the filtered file is opened into the UI. + */ gboolean do_export_pdu(const char *filter, gchar *tap_name, exp_pdu_t *data); +/** + * Registers the tap listener which will add matching packets to the exported + * file. Must be called before exp_pdu_open. + * + * @param tap_name One of the names registered with register_export_pdu_tap(). + * @param filter An tap filter, may be NULL to disable filtering which + * improves performance if you do not need a filter. + * @return NULL on success or an error string on failure which must be freed + * with g_free(). Failure could occur when the filter or tap_name are invalid. + */ +char *exp_pdu_pre_open(const char *tap_name, const char *filter, + exp_pdu_t *exp_pdu_tap_data); + +/** + * Use the given file descriptor for writing an output file. Can only be called + * once and exp_pdu_pre_open() must be called before. + * + * @return 0 on success or a wtap error code. + */ +int exp_pdu_open(exp_pdu_t *data, int fd, char *comment); + +/* Stops the PDUs export. */ +int exp_pdu_close(exp_pdu_t *exp_pdu_tap_data); + #ifdef __cplusplus } #endif /* __cplusplus */ -- cgit v1.2.1