From hannes:
Enable the plugin to detect and reassemble chunked UA messages for displaying them correctly. From me: - Partly applied by hand. - move hf and ett asignments inseide the register routine as per convention. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8068 svn path=/trunk/; revision=46436
This commit is contained in:
parent
e5d5ea7db7
commit
e7c9ade670
|
@ -27,6 +27,7 @@
|
||||||
#include <epan/prefs.h>
|
#include <epan/prefs.h>
|
||||||
#include <epan/packet.h>
|
#include <epan/packet.h>
|
||||||
#include <epan/dissectors/packet-tcp.h>
|
#include <epan/dissectors/packet-tcp.h>
|
||||||
|
#include <epan/reassemble.h>
|
||||||
#include "opcua_transport_layer.h"
|
#include "opcua_transport_layer.h"
|
||||||
#include "opcua_security_layer.h"
|
#include "opcua_security_layer.h"
|
||||||
#include "opcua_application_layer.h"
|
#include "opcua_application_layer.h"
|
||||||
|
@ -58,6 +59,48 @@ gint ett_opcua_transport = -1;
|
||||||
gint ett_opcua_extensionobject = -1;
|
gint ett_opcua_extensionobject = -1;
|
||||||
gint ett_opcua_nodeid = -1;
|
gint ett_opcua_nodeid = -1;
|
||||||
|
|
||||||
|
static gint ett_opcua_fragment = -1;
|
||||||
|
static gint ett_opcua_fragments = -1;
|
||||||
|
|
||||||
|
static int hf_opcua_fragments = -1;
|
||||||
|
static int hf_opcua_fragment = -1;
|
||||||
|
static int hf_opcua_fragment_overlap = -1;
|
||||||
|
static int hf_opcua_fragment_overlap_conflicts = -1;
|
||||||
|
static int hf_opcua_fragment_multiple_tails = -1;
|
||||||
|
static int hf_opcua_fragment_too_long_fragment = -1;
|
||||||
|
static int hf_opcua_fragment_error = -1;
|
||||||
|
static int hf_opcua_fragment_count = -1;
|
||||||
|
static int hf_opcua_reassembled_in = -1;
|
||||||
|
static int hf_opcua_reassembled_length = -1;
|
||||||
|
static int hf_opcua_reassembled_data = -1;
|
||||||
|
|
||||||
|
static const fragment_items opcua_frag_items = {
|
||||||
|
/* Fragment subtrees */
|
||||||
|
&ett_opcua_fragment,
|
||||||
|
&ett_opcua_fragments,
|
||||||
|
/* Fragment fields */
|
||||||
|
&hf_opcua_fragments,
|
||||||
|
&hf_opcua_fragment,
|
||||||
|
&hf_opcua_fragment_overlap,
|
||||||
|
&hf_opcua_fragment_overlap_conflicts,
|
||||||
|
&hf_opcua_fragment_multiple_tails,
|
||||||
|
&hf_opcua_fragment_too_long_fragment,
|
||||||
|
&hf_opcua_fragment_error,
|
||||||
|
&hf_opcua_fragment_count,
|
||||||
|
/* Reassembled in field */
|
||||||
|
&hf_opcua_reassembled_in,
|
||||||
|
/* Reassembled length field */
|
||||||
|
&hf_opcua_reassembled_length,
|
||||||
|
/* Reassembled data field */
|
||||||
|
&hf_opcua_reassembled_data,
|
||||||
|
/* Tag */
|
||||||
|
"Message fragments"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static GHashTable *opcua_fragment_table = NULL;
|
||||||
|
static GHashTable *opcua_reassembled_table = NULL;
|
||||||
|
|
||||||
/** OpcUa Transport Message Types */
|
/** OpcUa Transport Message Types */
|
||||||
enum MessageType
|
enum MessageType
|
||||||
{
|
{
|
||||||
|
@ -83,19 +126,59 @@ static char* g_szMessageTypes[] =
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** Setup protocol subtree array */
|
|
||||||
static gint *ett[] =
|
|
||||||
{
|
|
||||||
&ett_opcua_transport,
|
|
||||||
&ett_opcua_extensionobject,
|
|
||||||
&ett_opcua_nodeid,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** plugin entry functions.
|
/** plugin entry functions.
|
||||||
* This registers the OpcUa protocol.
|
* This registers the OpcUa protocol.
|
||||||
*/
|
*/
|
||||||
void proto_register_opcua(void)
|
void proto_register_opcua(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static hf_register_info hf[] =
|
||||||
|
{
|
||||||
|
{&hf_opcua_fragments,
|
||||||
|
{"Message fragments", "opcua.fragments",
|
||||||
|
FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment,
|
||||||
|
{"Message fragment", "opcua.fragment",
|
||||||
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_overlap,
|
||||||
|
{"Message fragment overlap", "opcua.fragment.overlap",
|
||||||
|
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_overlap_conflicts,
|
||||||
|
{"Message fragment overlapping with conflicting data",
|
||||||
|
"opcua.fragment.overlap.conflicts",
|
||||||
|
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_multiple_tails,
|
||||||
|
{"Message has multiple tail fragments",
|
||||||
|
"opcua.fragment.multiple_tails",
|
||||||
|
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_too_long_fragment,
|
||||||
|
{"Message fragment too long", "opcua.fragment.too_long_fragment",
|
||||||
|
FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_error,
|
||||||
|
{"Message defragmentation error", "opcua.fragment.error",
|
||||||
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_fragment_count,
|
||||||
|
{"Message fragment count", "opcua.fragment.count",
|
||||||
|
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_reassembled_in,
|
||||||
|
{"Reassembled in", "opcua.reassembled.in",
|
||||||
|
FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },
|
||||||
|
{&hf_opcua_reassembled_length,
|
||||||
|
{"Reassembled length", "opcua.reassembled.length",
|
||||||
|
FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Setup protocol subtree array */
|
||||||
|
static gint *ett[] =
|
||||||
|
{
|
||||||
|
&ett_opcua_transport,
|
||||||
|
&ett_opcua_extensionobject,
|
||||||
|
&ett_opcua_nodeid,
|
||||||
|
&ett_opcua_fragment,
|
||||||
|
&ett_opcua_fragments
|
||||||
|
};
|
||||||
|
|
||||||
module_t *opcua_module;
|
module_t *opcua_module;
|
||||||
|
|
||||||
proto_opcua = proto_register_protocol(
|
proto_opcua = proto_register_protocol(
|
||||||
|
@ -117,6 +200,10 @@ void proto_register_opcua(void)
|
||||||
|
|
||||||
range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT), 65535);
|
range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT), 65535);
|
||||||
|
|
||||||
|
fragment_table_init(&opcua_fragment_table);
|
||||||
|
reassembled_table_init(&opcua_reassembled_table);
|
||||||
|
proto_register_field_array(proto_opcua, hf, array_length(hf));
|
||||||
|
|
||||||
/* register user preferences */
|
/* register user preferences */
|
||||||
opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
|
opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
|
||||||
prefs_register_range_preference(opcua_module, "tcp_ports",
|
prefs_register_range_preference(opcua_module, "tcp_ports",
|
||||||
|
@ -206,10 +293,13 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
|
||||||
/* Clear out stuff in the info column */
|
/* Clear out stuff in the info column */
|
||||||
col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
|
col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
|
||||||
|
|
||||||
if (tree && pfctParse)
|
if (pfctParse)
|
||||||
{
|
{
|
||||||
gint offset = 0;
|
gint offset = 0;
|
||||||
int iServiceId = -1;
|
int iServiceId = -1;
|
||||||
|
tvbuff_t *next_tvb = tvb;
|
||||||
|
gboolean bParseService = TRUE;
|
||||||
|
gboolean bIsLastFragment = FALSE;
|
||||||
|
|
||||||
/* we are being asked for details */
|
/* we are being asked for details */
|
||||||
proto_item *ti = NULL;
|
proto_item *ti = NULL;
|
||||||
|
@ -218,9 +308,121 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
|
||||||
ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
|
ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
|
||||||
transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
|
transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
|
||||||
|
|
||||||
|
/* MSG_MESSAGE might be fragmented, check for that */
|
||||||
|
if (msgtype == MSG_MESSAGE)
|
||||||
|
{
|
||||||
|
guint8 chunkType = 0;
|
||||||
|
guint32 opcua_seqid = 0;
|
||||||
|
guint32 opcua_num = 0;
|
||||||
|
guint32 opcua_seqnum = 0;
|
||||||
|
fragment_data *frag_msg = NULL;
|
||||||
|
|
||||||
|
offset = 3;
|
||||||
|
|
||||||
|
chunkType = tvb_get_guint8(tvb, offset); offset += 1;
|
||||||
|
|
||||||
|
offset += 4; /* Message Size */
|
||||||
|
offset += 4; /* SecureChannelId */
|
||||||
|
offset += 4; /* Security Token Id */
|
||||||
|
|
||||||
|
opcua_num = tvb_get_letohl(tvb, offset); offset += 4; /* Security Sequence Number */
|
||||||
|
opcua_seqid = tvb_get_letohl(tvb, offset); offset += 4; /* Security RequestId */
|
||||||
|
|
||||||
|
/* check if tvb is part of a chunked message:
|
||||||
|
the UA protocol does not tell us that, so we look into opcua_fragment_table and
|
||||||
|
opcua_reassembled_table if the opcua_seqid belongs to a chunked message */
|
||||||
|
frag_msg = fragment_get(pinfo, opcua_seqid, opcua_fragment_table);
|
||||||
|
if (frag_msg == NULL)
|
||||||
|
{
|
||||||
|
frag_msg = fragment_get_reassembled_id(pinfo, opcua_seqid, opcua_reassembled_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frag_msg != NULL || chunkType != 'F')
|
||||||
|
{
|
||||||
|
gboolean bSaveFragmented = pinfo->fragmented;
|
||||||
|
gboolean bMoreFragments = TRUE;
|
||||||
|
tvbuff_t *new_tvb = NULL;
|
||||||
|
|
||||||
|
pinfo->fragmented = TRUE;
|
||||||
|
|
||||||
|
if (frag_msg == NULL)
|
||||||
|
{
|
||||||
|
/* first fragment */
|
||||||
|
offset = 0;
|
||||||
|
opcua_seqnum = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* the UA protocol does not number the chunks beginning from 0 but from a
|
||||||
|
arbitrary value, so we have to fake the numbers in the stored fragments.
|
||||||
|
this way Wireshark reassembles the message, as it expects the fragment
|
||||||
|
sequence numbers to start at 0 */
|
||||||
|
while (frag_msg->next) {frag_msg = frag_msg->next;}
|
||||||
|
opcua_seqnum = frag_msg->offset + 1;
|
||||||
|
|
||||||
|
if (chunkType == 'F')
|
||||||
|
{
|
||||||
|
bMoreFragments = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frag_msg = fragment_add_seq_check(tvb,
|
||||||
|
offset,
|
||||||
|
pinfo,
|
||||||
|
opcua_seqid, /* ID for fragments belonging together */
|
||||||
|
opcua_fragment_table, /* list of message fragments */
|
||||||
|
opcua_reassembled_table, /* list of reassembled messages */
|
||||||
|
opcua_seqnum, /* fragment sequence number */
|
||||||
|
tvb_length_remaining(tvb, offset), /* fragment length - to the end */
|
||||||
|
bMoreFragments); /* More fragments? */
|
||||||
|
|
||||||
|
new_tvb = process_reassembled_data(tvb,
|
||||||
|
offset,
|
||||||
|
pinfo,
|
||||||
|
"Reassembled Message",
|
||||||
|
frag_msg,
|
||||||
|
&opcua_frag_items,
|
||||||
|
NULL,
|
||||||
|
transport_tree);
|
||||||
|
|
||||||
|
if (new_tvb)
|
||||||
|
{
|
||||||
|
/* Reassembled */
|
||||||
|
bIsLastFragment = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Not last packet of reassembled UA message */
|
||||||
|
col_append_fstr(pinfo->cinfo, COL_INFO, " (Message fragment %u)", opcua_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_tvb)
|
||||||
|
{
|
||||||
|
/* take it all */
|
||||||
|
next_tvb = new_tvb;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* only show transport header */
|
||||||
|
bParseService = FALSE;
|
||||||
|
next_tvb = tvb_new_subset(tvb, 0, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pinfo->fragmented = bSaveFragmented;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
/* call the transport message dissector */
|
/* call the transport message dissector */
|
||||||
iServiceId = (*pfctParse)(transport_tree, tvb, &offset);
|
iServiceId = (*pfctParse)(transport_tree, tvb, &offset);
|
||||||
|
|
||||||
|
/* parse the service if not chunked or last chunk */
|
||||||
|
if (msgtype == MSG_MESSAGE && bParseService)
|
||||||
|
{
|
||||||
|
iServiceId = parseService(transport_tree, next_tvb, &offset);
|
||||||
|
}
|
||||||
|
|
||||||
/* display the service type in addition to the message type */
|
/* display the service type in addition to the message type */
|
||||||
if (iServiceId != -1)
|
if (iServiceId != -1)
|
||||||
{
|
{
|
||||||
|
@ -229,7 +431,14 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
|
||||||
{
|
{
|
||||||
if (g_requesttypes[indx].value == (guint32)iServiceId)
|
if (g_requesttypes[indx].value == (guint32)iServiceId)
|
||||||
{
|
{
|
||||||
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr);
|
if (bIsLastFragment == FALSE)
|
||||||
|
{
|
||||||
|
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s (Message Reassembled)", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
indx++;
|
indx++;
|
||||||
|
|
|
@ -162,11 +162,6 @@ int parseError(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
|
||||||
|
|
||||||
int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
|
int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
|
||||||
{
|
{
|
||||||
proto_item *ti;
|
|
||||||
proto_tree *encobj_tree;
|
|
||||||
proto_tree *nodeid_tree;
|
|
||||||
int ServiceId = 0;
|
|
||||||
|
|
||||||
proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
|
proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
|
||||||
proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
|
proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
|
||||||
proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
|
proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
|
||||||
|
@ -175,12 +170,22 @@ int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
|
||||||
/* message data contains the security layer */
|
/* message data contains the security layer */
|
||||||
parseSecurityLayer(tree, tvb, pOffset);
|
parseSecurityLayer(tree, tvb, pOffset);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parseService(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
|
||||||
|
{
|
||||||
|
proto_item *ti;
|
||||||
|
proto_tree *encobj_tree;
|
||||||
|
proto_tree *nodeid_tree;
|
||||||
|
int ServiceId = 0;
|
||||||
|
|
||||||
/* AT THE MOMENT NO SECURITY IS IMPLEMENTED IN UA.
|
/* AT THE MOMENT NO SECURITY IS IMPLEMENTED IN UA.
|
||||||
* WE CAN JUST JUMP INTO THE APPLICATION LAYER DATA.
|
* WE CAN JUST JUMP INTO THE APPLICATION LAYER DATA.
|
||||||
* THIS WILL CHAHNGE IN THE FUTURE. */
|
* THIS WILL CHAHNGE IN THE FUTURE. */
|
||||||
|
|
||||||
/* add encodeable object subtree */
|
/* add encodeable object subtree */
|
||||||
ti = proto_tree_add_text(tree, tvb, 0, -1, "Message : Encodeable Object");
|
ti = proto_tree_add_text(tree, tvb, 0, -1, "OpcUa Service : Encodeable Object");
|
||||||
encobj_tree = proto_item_add_subtree(ti, ett_opcua_extensionobject);
|
encobj_tree = proto_item_add_subtree(ti, ett_opcua_extensionobject);
|
||||||
|
|
||||||
/* add nodeid subtree */
|
/* add nodeid subtree */
|
||||||
|
|
|
@ -26,6 +26,7 @@ int parseHello(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int parseError(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
int parseError(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
|
int parseService(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int parseOpenSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
int parseOpenSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
int parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
|
||||||
int registerTransportLayerTypes(int proto);
|
int registerTransportLayerTypes(int proto);
|
||||||
|
|
Loading…
Reference in New Issue