From hannes:
authorAnders Broman <anders.broman@ericsson.com>
Fri, 7 Dec 2012 09:33:40 +0000 (09:33 -0000)
committerAnders Broman <anders.broman@ericsson.com>
Fri, 7 Dec 2012 09:33:40 +0000 (09:33 -0000)
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

plugins/opcua/opcua.c
plugins/opcua/opcua_transport_layer.c
plugins/opcua/opcua_transport_layer.h

index 11168346098f115d2b84bb286d318472dc582ac5..91ca3bb8fc0cf36281914ab70a63be2c984dacd0 100644 (file)
@@ -27,6 +27,7 @@
 #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"
@@ -58,6 +59,48 @@ gint ett_opcua_transport = -1;
 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
 {
@@ -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.
  * 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(
@@ -117,6 +200,10 @@ void proto_register_opcua(void)
 
     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",
@@ -206,10 +293,13 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
     /* 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;
@@ -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);
         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)
         {
@@ -229,7 +431,14 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree
             {
                 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++;
index 1009d8b5c5a236f94a0e1ccb6d5bcf20b5649982..5d22fa78b46fbbf5d82f20116543de2d32059280 100644 (file)
@@ -162,11 +162,6 @@ int parseError(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_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;
@@ -175,12 +170,22 @@ int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset)
     /* message data contains the security layer */
     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.
      * WE CAN JUST JUMP INTO THE APPLICATION LAYER DATA.
      * THIS WILL CHAHNGE IN THE FUTURE. */
 
     /* 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);
 
     /* add nodeid subtree */
index 71779fb52509b93d81f66aea893c176d4e4f36d9..4ceb14b5ffd8abad0438b8add3ec9d3e7906292c 100644 (file)
@@ -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 parseError(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 parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset);
 int registerTransportLayerTypes(int proto);