From 52823805b29a44a83eacd0e5b415b11227ec313b Mon Sep 17 00:00:00 2001 From: Michael Mann Date: Fri, 25 Aug 2017 15:29:17 -0400 Subject: [PATCH] Add support for reading comments in Network Monitor files The NetMon wiretap reads the title and description comment fields from a NetMon file and saves it in the wiretap private structure. Then when it's time to make a frame, the comment fields are added to a NetMon pseudoheader with a new WTAP ENCAP type, with the potential for netmon pseudoheader to contain pseudoheader data from "base" wiretap. Then the netmon_header dissector displays the comment fields and passes any "base" wiretap pseudoheader data when calling the wtap_encap dissector table that the frame dissector normally calls. Bug: 4225 Change-Id: I8f772bc9494364c98434c78b61eb5a64012ff3b9 Reviewed-on: https://code.wireshark.org/review/23210 Petri-Dish: Michael Mann Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-netmon.c | 102 +++++++++++- wiretap/netmon.c | 277 +++++++++++++++++++++++++++++--- wiretap/wtap.c | 3 + wiretap/wtap.h | 17 ++ 4 files changed, 371 insertions(+), 28 deletions(-) diff --git a/epan/dissectors/packet-netmon.c b/epan/dissectors/packet-netmon.c index e3c361e3f9..7eb28d8b58 100644 --- a/epan/dissectors/packet-netmon.c +++ b/epan/dissectors/packet-netmon.c @@ -67,8 +67,12 @@ static const value_string event_level_vals[] = { }; /* Initialize the protocol and registered fields */ +static int proto_netmon_header = -1; static int proto_netmon_event = -1; +static int hf_netmon_header_title_comment = -1; +static int hf_netmon_header_description_comment = -1; + static int hf_netmon_event_size = -1; static int hf_netmon_event_header_type = -1; static int hf_netmon_event_flags = -1; @@ -114,13 +118,81 @@ static int hf_netmon_event_user_data = -1; /* Initialize the subtree pointers */ +static gint ett_netmon_header = -1; static gint ett_netmon_event = -1; static gint ett_netmon_event_desc = -1; static gint ett_netmon_event_flags = -1; static gint ett_netmon_event_property = -1; static gint ett_netmon_event_extended_data = -1; +static dissector_table_t wtap_encap_table; + /* Code to actually dissect the packets */ +static int +dissect_netmon_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_item *ti; + proto_tree *header_tree; + union wtap_pseudo_header temp_header; + gchar *comment; + GIConv cd; + + ti = proto_tree_add_item(tree, proto_netmon_header, tvb, 0, 0, ENC_NA); + header_tree = proto_item_add_subtree(ti, ett_netmon_header); + + if (pinfo->pseudo_header->netmon.title != NULL) { + /* Title comment is UTF-16 */ + + if ((cd = g_iconv_open("UTF-8", "UTF-16")) != (GIConv) -1) + { + comment = g_convert_with_iconv(pinfo->pseudo_header->netmon.title, pinfo->pseudo_header->netmon.titleLength, cd, NULL, NULL, NULL); + g_iconv_close(cd); + + ti = proto_tree_add_string(header_tree, hf_netmon_header_title_comment, tvb, 0, 0, comment); + PROTO_ITEM_SET_GENERATED(ti); + g_free(comment); + } + + } + + if (pinfo->pseudo_header->netmon.description != NULL) { + /* Description comment is only ASCII */ + + /* Ensure string termination */ + comment = wmem_strndup(wmem_packet_scope(), pinfo->pseudo_header->netmon.description, pinfo->pseudo_header->netmon.descLength); + + ti = proto_tree_add_string(header_tree, hf_netmon_header_description_comment, tvb, 0, 0, comment); + PROTO_ITEM_SET_GENERATED(ti); + } + + /* Save the pseudo header data to a temp variable before it's copied to + * real pseudo header + */ + switch (pinfo->pseudo_header->netmon.sub_encap) + { + case WTAP_ENCAP_ATM_PDUS: + memcpy(&temp_header.atm, &pinfo->pseudo_header->netmon.subheader.atm, sizeof(temp_header.atm)); + memcpy(&pinfo->pseudo_header->atm, &temp_header.atm, sizeof(temp_header.atm)); + break; + case WTAP_ENCAP_ETHERNET: + memcpy(&temp_header.eth, &pinfo->pseudo_header->netmon.subheader.eth, sizeof(temp_header.eth)); + memcpy(&pinfo->pseudo_header->eth, &temp_header.eth, sizeof(temp_header.eth)); + break; + case WTAP_ENCAP_IEEE_802_11_NETMON: + memcpy(&temp_header.ieee_802_11, &pinfo->pseudo_header->netmon.subheader.ieee_802_11, sizeof(temp_header.ieee_802_11)); + memcpy(&pinfo->pseudo_header->ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11)); + break; + } + + if (!dissector_try_uint_new(wtap_encap_table, + pinfo->pseudo_header->netmon.sub_encap, tvb, pinfo, tree, TRUE, + (void *)pinfo->pseudo_header)) { + call_data_dissector(tvb, pinfo, tree); + } + + return tvb_captured_length(tvb); +} + static int dissect_netmon_event(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { @@ -239,7 +311,19 @@ dissect_netmon_event(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* void proto_register_netmon(void) { - static hf_register_info hf[] = { + static hf_register_info hf_header[] = { + { &hf_netmon_header_title_comment, + { "Comment title", "netmon_header.title_comment", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } + }, + { &hf_netmon_header_description_comment, + { "Comment description", "netmon_header.description_comment", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } + }, + }; + + + static hf_register_info hf_event[] = { { &hf_netmon_event_size, { "Size", "netmon_event.size", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } @@ -411,6 +495,7 @@ void proto_register_netmon(void) }; static gint *ett[] = { + &ett_netmon_header, &ett_netmon_event, &ett_netmon_event_desc, &ett_netmon_event_flags, @@ -418,18 +503,25 @@ void proto_register_netmon(void) &ett_netmon_event_extended_data }; + proto_netmon_header = proto_register_protocol ("Network Monitor Header", "NetMon Header", "netmon_header" ); proto_netmon_event = proto_register_protocol ("Network Monitor Event", "NetMon Event", "netmon_event" ); - proto_register_field_array(proto_netmon_event, hf, array_length(hf)); + + proto_register_field_array(proto_netmon_header, hf_header, array_length(hf_header)); + proto_register_field_array(proto_netmon_event, hf_event, array_length(hf_event)); proto_register_subtree_array(ett, array_length(ett)); } void proto_reg_handoff_netmon(void) { - dissector_handle_t netmon_handle; + dissector_handle_t netmon_event_handle, netmon_header_handle; - netmon_handle = create_dissector_handle(dissect_netmon_event, proto_netmon_event); + netmon_event_handle = create_dissector_handle(dissect_netmon_event, proto_netmon_event); + netmon_header_handle = create_dissector_handle(dissect_netmon_header, proto_netmon_header); - dissector_add_uint("wtap_encap", WTAP_ENCAP_NETMON_NET_NETEVENT, netmon_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_NETMON_NET_NETEVENT, netmon_event_handle); + dissector_add_uint("wtap_encap", WTAP_ENCAP_NETMON_HEADER, netmon_header_handle); + + wtap_encap_table = find_dissector_table("wtap_encap"); } /* diff --git a/wiretap/netmon.c b/wiretap/netmon.c index 3f46b49ffb..8238e955f5 100644 --- a/wiretap/netmon.c +++ b/wiretap/netmon.c @@ -120,6 +120,22 @@ struct netmonrec_2_3_trlr { guint8 timezone_index; /* index of time zone information */ }; +struct netmonrec_comment { + guint32 numFramePerComment; /* Currently, this is always set to 1. Each comment is attached to only one frame. */ + guint32 frameOffset; /* Offset in the capture file table that indicates the beginning of the frame. Key used to match comment with frame */ + guint32 titleLength; /* Number of bytes in the comment title. Must be greater than zero. */ + guint8* title; /* Comment title */ + guint32 descLength; /* Number of bytes in the comment description. Must be at least zero. */ + guint8* description; /* Comment description */ +}; + +/* Just the first few fields of netmonrec_comment so it can be read sequentially from file */ +struct netmonrec_comment_header { + guint32 numFramePerComment; + guint32 frameOffset; + guint32 titleLength; +}; + /* * The link-layer header on ATM packets. */ @@ -131,13 +147,14 @@ struct netmon_atm_hdr { }; typedef struct { - time_t start_secs; - guint32 start_nsecs; - guint8 version_major; - guint8 version_minor; + time_t start_secs; + guint32 start_nsecs; + guint8 version_major; + guint8 version_minor; guint32 *frame_table; - guint32 frame_table_size; - guint current_frame; + guint32 frame_table_size; + GHashTable* comment_table; + guint current_frame; } netmon_t; /* @@ -180,11 +197,19 @@ static gboolean netmon_seek_read(wtap *wth, gint64 seek_off, struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info); static gboolean netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info); -static void netmon_sequential_close(wtap *wth); +static void netmon_close(wtap *wth); static gboolean netmon_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr, const guint8 *pd, int *err, gchar **err_info); static gboolean netmon_dump_finish(wtap_dumper *wdh, int *err); +static void netmonrec_comment_destroy(gpointer key) { + struct netmonrec_comment *comment = (struct netmonrec_comment*) key; + + g_free(comment->title); + g_free(comment->description); + g_free(comment); +} + wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) { char magic[MAGIC_SIZE]; @@ -195,6 +220,10 @@ wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) guint32 frame_table_length; guint32 frame_table_size; guint32 *frame_table; + guint32 comment_table_offset; + guint32 comment_table_size; + GHashTable* comment_table; + struct netmonrec_comment* comment_rec; #ifdef WORDS_BIGENDIAN unsigned int i; #endif @@ -244,11 +273,11 @@ wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) /* This is a netmon file */ wth->file_type_subtype = file_type; - netmon = (netmon_t *)g_malloc(sizeof(netmon_t)); + netmon = (netmon_t *)g_malloc0(sizeof(netmon_t)); wth->priv = (void *)netmon; wth->subtype_read = netmon_read; wth->subtype_seek_read = netmon_seek_read; - wth->subtype_sequential_close = netmon_sequential_close; + wth->subtype_close = netmon_close; /* NetMon capture file formats v2.1+ use per-packet encapsulation types. NetMon 3 sets the value in * the header to 1 (Ethernet) for backwards compability. */ @@ -289,20 +318,17 @@ wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) netmon->version_major = hdr.ver_major; netmon->version_minor = hdr.ver_minor; - /* - * No frame table allocated yet; initialize these in case we - * get an error before allocating it or when trying to allocate - * it, so that the attempt to release the private data on failure - * doesn't crash. - */ - netmon->frame_table_size = 0; - netmon->frame_table = NULL; - /* * Get the offset of the frame index table. */ frame_table_offset = pletoh32(&hdr.frametableoffset); + /* + * Get the offset and length of the comment index table. + */ + comment_table_offset = pletoh32(&hdr.commentdataoffset); + comment_table_size = pletoh32(&hdr.commentdatalength); + /* * It appears that some NetMon 2.x files don't have the * first packet starting exactly 128 bytes into the file. @@ -350,6 +376,44 @@ wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) { return WTAP_OPEN_ERROR; } + + /* + * Sanity check the comment table information before we bother to allocate + * large chunks of memory for the frame table + */ + if (comment_table_size > 0) { + /* + * XXX - clamp the size of the comment table, so that we don't + * attempt to allocate a huge comment table and fail. + * + * Just use same size requires as frame table + */ + if (comment_table_size > 512*1024*1024) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("netmon: comment table size is %u, which is larger than we support", + comment_table_size); + return WTAP_OPEN_ERROR; + } + + if (comment_table_size < 17) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("netmon: comment table size is %u, which is too small to use", + comment_table_size); + return WTAP_OPEN_ERROR; + } + + if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) { + return WTAP_OPEN_ERROR; + } + + /* + * Return back to the frame table offset + */ + if (file_seek(wth->fh, frame_table_offset, SEEK_SET, err) == -1) { + return WTAP_OPEN_ERROR; + } + } + frame_table = (guint32 *)g_try_malloc(frame_table_length); if (frame_table_length != 0 && frame_table == NULL) { *err = ENOMEM; /* we assume we're out of memory */ @@ -363,6 +427,115 @@ wtap_open_return_val netmon_open(wtap *wth, int *err, gchar **err_info) netmon->frame_table_size = frame_table_size; netmon->frame_table = frame_table; + if (comment_table_size > 0) { + comment_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, netmonrec_comment_destroy); + if (comment_table == NULL) { + *err = ENOMEM; /* we assume we're out of memory */ + g_free(frame_table); + return WTAP_OPEN_ERROR; + } + + /* Make sure the file contains the full comment section */ + if (file_seek(wth->fh, comment_table_offset+comment_table_size, SEEK_SET, err) == -1) { + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + if (file_seek(wth->fh, comment_table_offset, SEEK_SET, err) == -1) { + /* Shouldn't fail... */ + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + while (comment_table_size > 16) { + struct netmonrec_comment_header comment_header; + guint32 desc_length; + + /* Read the first 12 bytes of the structure */ + if (!wtap_read_bytes(wth->fh, &comment_header, 12, err, err_info)) { + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + comment_table_size -= 12; + + /* Make sure comment size is sane */ + if (pletoh32(&comment_header.titleLength) == 0) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("netmon: comment title size can't be 0"); + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + if (pletoh32(&comment_header.titleLength) > comment_table_size) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("netmon: comment title size is %u, which is larger than the entire comment section (%d)", + pletoh32(&comment_header.titleLength), comment_table_size); + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + comment_rec = g_new(struct netmonrec_comment, 1); + comment_rec->numFramePerComment = pletoh32(&comment_header.numFramePerComment); + comment_rec->frameOffset = pletoh32(&comment_header.frameOffset); + comment_rec->titleLength = pletoh32(&comment_header.titleLength); + comment_rec->title = (guint8*)g_malloc(comment_rec->titleLength); + + g_hash_table_insert(comment_table, GUINT_TO_POINTER(comment_rec->frameOffset), comment_rec); + + /* Read the comment title */ + if (!wtap_read_bytes(wth->fh, comment_rec->title, comment_rec->titleLength, err, err_info)) { + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + comment_table_size -= comment_rec->titleLength; + + if (comment_table_size < 4) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup("netmon: corrupt comment section"); + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + if (!wtap_read_bytes(wth->fh, &desc_length, 4, err, err_info)) { + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + comment_table_size -= 4; + + comment_rec->descLength = pletoh32(&desc_length); + if (comment_rec->descLength > 0) { + /* Make sure comment size is sane */ + if (comment_rec->descLength > comment_table_size) { + *err = WTAP_ERR_BAD_FILE; + *err_info = g_strdup_printf("netmon: comment description size is %u, which is larger than the entire comment section (%d)", + comment_rec->descLength, comment_table_size); + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + comment_rec->description = (guint8*)g_malloc(comment_rec->descLength); + + /* Read the comment description */ + if (!wtap_read_bytes(wth->fh, comment_rec->description, comment_rec->descLength, err, err_info)) { + g_free(frame_table); + g_hash_table_destroy(comment_table); + return WTAP_OPEN_ERROR; + } + + comment_table_size -= comment_rec->descLength; + } + } + netmon->comment_table = comment_table; + } + #ifdef WORDS_BIGENDIAN /* * OK, now byte-swap the frame table. @@ -467,6 +640,7 @@ netmon_process_record(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr, } trlr; guint16 network; int pkt_encap; + struct netmonrec_comment* comment_rec = NULL; /* Read record header. */ switch (netmon->version_major) { @@ -754,6 +928,62 @@ netmon_process_record(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr, } netmon_set_pseudo_header_info(phdr, buf); + + /* If any header specific information is present, set it as pseudo header data + * and set the encapsulation type, so it can be handled to the netmon_header + * dissector for further processing + */ + if (netmon->comment_table != NULL) { + comment_rec = (struct netmonrec_comment*)g_hash_table_lookup(netmon->comment_table, GUINT_TO_POINTER(netmon->frame_table[netmon->current_frame-1])); + } + + if (comment_rec != NULL) { + union wtap_pseudo_header temp_header; + + /* These are the current encapsulation types that NetMon uses. + * Save them off so they can be copied to the NetMon pseudoheader + */ + switch (phdr->pkt_encap) + { + case WTAP_ENCAP_ATM_PDUS: + memcpy(&temp_header.atm, &phdr->pseudo_header.atm, sizeof(temp_header.atm)); + break; + case WTAP_ENCAP_ETHERNET: + memcpy(&temp_header.eth, &phdr->pseudo_header.eth, sizeof(temp_header.eth)); + break; + case WTAP_ENCAP_IEEE_802_11_NETMON: + memcpy(&temp_header.ieee_802_11, &phdr->pseudo_header.ieee_802_11, sizeof(temp_header.ieee_802_11)); + break; + } + memset(&phdr->pseudo_header.netmon, 0, sizeof(phdr->pseudo_header.netmon)); + + /* Save the current encapsulation type to the NetMon pseudoheader */ + phdr->pseudo_header.netmon.sub_encap = phdr->pkt_encap; + + /* Copy the comment data */ + phdr->pseudo_header.netmon.titleLength = comment_rec->titleLength; + phdr->pseudo_header.netmon.title = comment_rec->title; + phdr->pseudo_header.netmon.descLength = comment_rec->descLength; + phdr->pseudo_header.netmon.description = comment_rec->description; + + /* Copy the saved pseudoheaders to the netmon pseudoheader structure */ + switch (phdr->pkt_encap) + { + case WTAP_ENCAP_ATM_PDUS: + memcpy(&phdr->pseudo_header.netmon.subheader.atm, &temp_header.atm, sizeof(temp_header.atm)); + break; + case WTAP_ENCAP_ETHERNET: + memcpy(&phdr->pseudo_header.netmon.subheader.eth, &temp_header.eth, sizeof(temp_header.eth)); + break; + case WTAP_ENCAP_IEEE_802_11_NETMON: + memcpy(&phdr->pseudo_header.netmon.subheader.ieee_802_11, &temp_header.ieee_802_11, sizeof(temp_header.ieee_802_11)); + break; + } + + /* Encapsulation type is now something that can be passed to netmon_header dissector */ + phdr->pkt_encap = WTAP_ENCAP_NETMON_HEADER; + } + return SUCCESS; } @@ -767,10 +997,6 @@ static gboolean netmon_read(wtap *wth, int *err, gchar **err_info, for (;;) { /* Have we reached the end of the packet data? */ if (netmon->current_frame >= netmon->frame_table_size) { - /* Yes. We won't need the frame table any more; - free it. */ - g_free(netmon->frame_table); - netmon->frame_table = NULL; *err = 0; /* it's just an EOF, not an error */ return FALSE; } @@ -865,7 +1091,7 @@ netmon_read_atm_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header, /* Throw away the frame table used by the sequential I/O stream. */ static void -netmon_sequential_close(wtap *wth) +netmon_close(wtap *wth) { netmon_t *netmon = (netmon_t *)wth->priv; @@ -873,6 +1099,11 @@ netmon_sequential_close(wtap *wth) g_free(netmon->frame_table); netmon->frame_table = NULL; } + + if (netmon->comment_table != NULL) { + g_hash_table_destroy(netmon->comment_table); + netmon->comment_table = NULL; + } } typedef struct { diff --git a/wiretap/wtap.c b/wiretap/wtap.c index d606cb57d5..4e57ece6db 100644 --- a/wiretap/wtap.c +++ b/wiretap/wtap.c @@ -935,6 +935,9 @@ static struct encap_type_info encap_table_base[] = { /* WTAP_ENCAP_NETMON_NET_NETEVENT */ { "Network Monitor Network Event", "netmon_event" }, + + /* WTAP_ENCAP_NETMON_HEADER */ + { "Network Monitor Header", "netmon_header" }, }; WS_DLL_LOCAL diff --git a/wiretap/wtap.h b/wiretap/wtap.h index 7959b0bfc6..00bafcde19 100644 --- a/wiretap/wtap.h +++ b/wiretap/wtap.h @@ -275,6 +275,7 @@ extern "C" { #define WTAP_ENCAP_VSOCK 185 #define WTAP_ENCAP_NORDIC_BLE 186 #define WTAP_ENCAP_NETMON_NET_NETEVENT 187 +#define WTAP_ENCAP_NETMON_HEADER 188 /* After adding new item here, please also add new item to encap_table_base array */ #define WTAP_NUM_ENCAP_TYPES wtap_get_num_encap_types() @@ -1147,6 +1148,21 @@ struct sysdig_event_phdr { /* ... Event ... */ }; +/* Packet "pseudo-header" information for header data from NetMon files. */ + +struct netmon_phdr { + guint32 titleLength; /* Number of bytes in the comment title */ + guint8* title; /* Comment title */ + guint32 descLength; /* Number of bytes in the comment description */ + guint8* description; /* Comment description */ + guint sub_encap; /* "Real" encap value for the record that will be used once pseudo header data is display */ + union sub_wtap_pseudo_header { + struct eth_phdr eth; + struct atm_phdr atm; + struct ieee_802_11_phdr ieee_802_11; + } subheader; +}; + /* Pseudo-header for file-type-specific records */ struct ft_specific_record_phdr { guint record_type; /* the type of record this is */ @@ -1179,6 +1195,7 @@ union wtap_pseudo_header { struct llcp_phdr llcp; struct logcat_phdr logcat; struct sysdig_event_phdr sysdig_event; + struct netmon_phdr netmon; struct ft_specific_record_phdr ftsrec; };