#include <epan/prefs.h>
#include <epan/packet.h>
#include <epan/dissectors/packet-tcp.h>
+#include <epan/reassemble.h>
#include "opcua_transport_layer.h"
#include "opcua_security_layer.h"
#include "opcua_application_layer.h"
gint ett_opcua_extensionobject = -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 */
enum MessageType
{
};
-/** Setup protocol subtree array */
-static gint *ett[] =
-{
- &ett_opcua_transport,
- &ett_opcua_extensionobject,
- &ett_opcua_nodeid,
-};
/** plugin entry functions.
* This registers the OpcUa protocol.
*/
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;
proto_opcua = proto_register_protocol(
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 */
opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
prefs_register_range_preference(opcua_module, "tcp_ports",
/* Clear out stuff in the info column */
col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
- if (tree && pfctParse)
+ if (pfctParse)
{
gint offset = 0;
int iServiceId = -1;
+ tvbuff_t *next_tvb = tvb;
+ gboolean bParseService = TRUE;
+ gboolean bIsLastFragment = FALSE;
/* we are being asked for details */
proto_item *ti = NULL;
ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
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 */
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 */
if (iServiceId != -1)
{
{
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;
}
indx++;