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 <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Michael Mann 2017-08-25 15:29:17 -04:00 committed by Anders Broman
parent 32b27bf19d
commit 52823805b2
4 changed files with 371 additions and 28 deletions

View File

@ -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");
}
/*

View File

@ -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 {

View File

@ -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

View File

@ -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;
};