1 /******************************************************************************
2 ** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved.
3 ** Web: http://www.ascolab.com
5 ** This program is free software; you can redistribute it and/or
6 ** modify it under the terms of the GNU General Public License
7 ** as published by the Free Software Foundation; either version 2
8 ** of the License, or (at your option) any later version.
10 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 ** Project: OpcUa Wireshark Plugin
15 ** Description: OpcUa Protocol Decoder.
17 ** Author: Gerhard Gappmeier <gerhard.gappmeier@ascolab.com>
18 ******************************************************************************/
22 #include <epan/packet.h>
23 #include <epan/reassemble.h>
24 #include <epan/dissectors/packet-tcp.h>
25 #include "opcua_transport_layer.h"
26 #include "opcua_security_layer.h"
27 #include "opcua_application_layer.h"
28 #include "opcua_complextypeparser.h"
29 #include "opcua_serviceparser.h"
30 #include "opcua_enumparser.h"
31 #include "opcua_simpletypes.h"
32 #include "opcua_hfindeces.h"
34 void proto_register_opcua(void);
36 extern const value_string g_requesttypes[];
37 extern const int g_NumServices;
39 /* forward reference */
40 void proto_reg_handoff_opcua(void);
41 /* declare parse function pointer */
42 typedef int (*FctParse)(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset);
44 static int proto_opcua = -1;
45 static dissector_handle_t opcua_handle;
46 /** Official IANA registered port for OPC UA Binary Protocol. */
47 #define OPCUA_PORT_RANGE "4840"
49 /** subtree types used in opcua_transport_layer.c */
50 gint ett_opcua_extensionobject = -1;
51 gint ett_opcua_nodeid = -1;
53 /** subtree types used locally */
54 static gint ett_opcua_transport = -1;
55 static gint ett_opcua_fragment = -1;
56 static gint ett_opcua_fragments = -1;
58 static int hf_opcua_fragments = -1;
59 static int hf_opcua_fragment = -1;
60 static int hf_opcua_fragment_overlap = -1;
61 static int hf_opcua_fragment_overlap_conflicts = -1;
62 static int hf_opcua_fragment_multiple_tails = -1;
63 static int hf_opcua_fragment_too_long_fragment = -1;
64 static int hf_opcua_fragment_error = -1;
65 static int hf_opcua_fragment_count = -1;
66 static int hf_opcua_reassembled_in = -1;
67 static int hf_opcua_reassembled_length = -1;
69 static const fragment_items opcua_frag_items = {
70 /* Fragment subtrees */
76 &hf_opcua_fragment_overlap,
77 &hf_opcua_fragment_overlap_conflicts,
78 &hf_opcua_fragment_multiple_tails,
79 &hf_opcua_fragment_too_long_fragment,
80 &hf_opcua_fragment_error,
81 &hf_opcua_fragment_count,
82 /* Reassembled in field */
83 &hf_opcua_reassembled_in,
84 /* Reassembled length field */
85 &hf_opcua_reassembled_length,
86 /* Reassembled data field */
93 static reassembly_table opcua_reassembly_table;
95 /** OpcUa Transport Message Types */
102 MSG_OPENSECURECHANNEL,
103 MSG_CLOSESECURECHANNEL,
107 /** OpcUa Transport Message Type Names */
108 static const char* g_szMessageTypes[] =
111 "Acknowledge message",
113 "UA Secure Conversation Message",
114 "OpenSecureChannel message",
115 "CloseSecureChannel message",
122 /** header length that is needed to compute
124 * @see get_opcua_message_len
126 #define FRAME_HEADER_LEN 8
128 /** returns the length of an OpcUa message.
129 * This function reads the length information from
130 * the transport header.
132 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb,
133 int offset, void *data _U_)
137 /* the message length starts at offset 4 */
138 plen = tvb_get_letohl(tvb, offset + 4);
143 /** The OpcUa message dissector.
144 * This method dissects full OpcUa messages.
145 * It gets only called with reassembled data
146 * from tcp_dissect_pdus.
148 static int dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
150 FctParse pfctParse = NULL;
151 enum MessageType msgtype = MSG_INVALID;
153 col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
155 /* parse message type */
156 if (tvb_memeql(tvb, 0, "HEL", 3) == 0)
159 pfctParse = parseHello;
161 else if (tvb_memeql(tvb, 0, "ACK", 3) == 0)
163 msgtype = MSG_ACKNOWLEDGE;
164 pfctParse = parseAcknowledge;
166 else if (tvb_memeql(tvb, 0, "ERR", 3) == 0)
169 pfctParse = parseError;
171 else if (tvb_memeql(tvb, 0, "MSG", 3) == 0)
173 msgtype = MSG_MESSAGE;
174 pfctParse = parseMessage;
176 else if (tvb_memeql(tvb, 0, "OPN", 3) == 0)
178 msgtype = MSG_OPENSECURECHANNEL;
179 pfctParse = parseOpenSecureChannel;
181 else if (tvb_memeql(tvb, 0, "CLO", 3) == 0)
183 msgtype = MSG_CLOSESECURECHANNEL;
184 pfctParse = parseCloseSecureChannel;
188 msgtype = MSG_INVALID;
191 /* Clear out stuff in the info column */
192 col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
198 tvbuff_t *next_tvb = tvb;
199 gboolean bParseService = TRUE;
200 gboolean bIsLastFragment = FALSE;
202 /* we are being asked for details */
203 proto_item *ti = NULL;
204 proto_tree *transport_tree = NULL;
206 ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
207 transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
209 /* MSG_MESSAGE might be fragmented, check for that */
210 if (msgtype == MSG_MESSAGE)
212 guint8 chunkType = 0;
213 guint32 opcua_seqid = 0;
214 guint32 opcua_num = 0;
215 guint32 opcua_seqnum = 0;
216 fragment_head *frag_msg = NULL;
220 chunkType = tvb_get_guint8(tvb, offset); offset += 1;
222 offset += 4; /* Message Size */
223 offset += 4; /* SecureChannelId */
224 offset += 4; /* Security Token Id */
226 opcua_num = tvb_get_letohl(tvb, offset); offset += 4; /* Security Sequence Number */
227 opcua_seqid = tvb_get_letohl(tvb, offset); offset += 4; /* Security RequestId */
229 if (chunkType == 'A')
231 fragment_delete(&opcua_reassembly_table, pinfo, opcua_seqid, NULL);
233 col_clear_fence(pinfo->cinfo, COL_INFO);
234 col_set_str(pinfo->cinfo, COL_INFO, "Abort message");
237 (*pfctParse)(transport_tree, tvb, pinfo, &offset);
238 parseAbort(transport_tree, tvb, pinfo, &offset);
240 return tvb_reported_length(tvb);
243 /* check if tvb is part of a chunked message:
244 the UA protocol does not tell us that, so we look into
245 opcua_reassembly_table if the opcua_seqid belongs to a
247 frag_msg = fragment_get(&opcua_reassembly_table, pinfo, opcua_seqid, NULL);
248 if (frag_msg == NULL)
250 frag_msg = fragment_get_reassembled_id(&opcua_reassembly_table, pinfo, opcua_seqid);
253 if (frag_msg != NULL || chunkType != 'F')
255 gboolean bSaveFragmented = pinfo->fragmented;
256 gboolean bMoreFragments = TRUE;
257 tvbuff_t *new_tvb = NULL;
259 pinfo->fragmented = TRUE;
261 if (frag_msg == NULL)
268 /* the UA protocol does not number the chunks beginning from 0 but from a
269 arbitrary value, so we have to fake the numbers in the stored fragments.
270 this way Wireshark reassembles the message, as it expects the fragment
271 sequence numbers to start at 0 */
272 while (frag_msg->next) {frag_msg = frag_msg->next;}
273 opcua_seqnum = frag_msg->offset + 1;
275 if (chunkType == 'F')
277 bMoreFragments = FALSE;
281 frag_msg = fragment_add_seq_check(&opcua_reassembly_table,
285 opcua_seqid, /* ID for fragments belonging together */
287 opcua_seqnum, /* fragment sequence number */
288 tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */
289 bMoreFragments); /* More fragments? */
291 new_tvb = process_reassembled_data(tvb,
294 "Reassembled Message",
303 bIsLastFragment = TRUE;
307 /* Not last packet of reassembled UA message */
308 col_append_fstr(pinfo->cinfo, COL_INFO, " (Message fragment %u)", opcua_num);
318 /* only show transport header */
319 bParseService = FALSE;
320 next_tvb = tvb_new_subset_remaining(tvb, 0);
323 pinfo->fragmented = bSaveFragmented;
329 /* call the transport message dissector */
330 iServiceId = (*pfctParse)(transport_tree, tvb, pinfo, &offset);
332 /* parse the service if not chunked or last chunk */
333 if (msgtype == MSG_MESSAGE && bParseService)
335 if (bIsLastFragment != FALSE)
339 iServiceId = parseService(transport_tree, next_tvb, pinfo, &offset);
342 /* display the service type in addition to the message type */
343 if (iServiceId != -1)
345 const gchar *szServiceName = val_to_str((guint32)iServiceId, g_requesttypes, "ServiceId %d");
347 if (bIsLastFragment == FALSE)
349 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], szServiceName);
353 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s (Message Reassembled)", g_szMessageTypes[msgtype], szServiceName);
358 return tvb_reported_length(tvb);
361 /** The main OpcUa dissector functions.
362 * It uses tcp_dissect_pdus from packet-tcp.h
363 * to reassemble the TCP data.
365 static int dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
367 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
368 get_opcua_message_len, dissect_opcua_message, data);
369 return tvb_reported_length(tvb);
372 /** plugin entry functions.
373 * This registers the OpcUa protocol.
375 void proto_register_opcua(void)
377 static hf_register_info hf[] =
379 /* id full name abbreviation type display strings bitmask blurb HFILL */
380 {&hf_opcua_fragments, {"Message fragments", "opcua.fragments", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL}},
381 {&hf_opcua_fragment, {"Message fragment", "opcua.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
382 {&hf_opcua_fragment_overlap, {"Message fragment overlap", "opcua.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
383 {&hf_opcua_fragment_overlap_conflicts, {"Message fragment overlapping with conflicting data", "opcua.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
384 {&hf_opcua_fragment_multiple_tails, {"Message has multiple tail fragments", "opcua.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
385 {&hf_opcua_fragment_too_long_fragment, {"Message fragment too long", "opcua.fragment.too_long_fragment", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
386 {&hf_opcua_fragment_error, {"Message defragmentation error", "opcua.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
387 {&hf_opcua_fragment_count, {"Message fragment count", "opcua.fragment.count", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL}},
388 {&hf_opcua_reassembled_in, {"Reassembled in", "opcua.reassembled.in", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
389 {&hf_opcua_reassembled_length, {"Reassembled length", "opcua.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL}}
392 /** Setup protocol subtree array */
395 &ett_opcua_extensionobject,
397 &ett_opcua_transport,
402 proto_opcua = proto_register_protocol("OpcUa Binary Protocol", "OpcUa", "opcua");
404 registerTransportLayerTypes(proto_opcua);
405 registerSecurityLayerTypes(proto_opcua);
406 registerApplicationLayerTypes(proto_opcua);
407 registerSimpleTypes(proto_opcua);
408 registerEnumTypes(proto_opcua);
409 registerComplexTypes();
410 registerServiceTypes();
411 registerFieldTypes(proto_opcua);
413 proto_register_subtree_array(ett, array_length(ett));
414 proto_register_field_array(proto_opcua, hf, array_length(hf));
416 reassembly_table_register(&opcua_reassembly_table,
417 &addresses_reassembly_table_functions);
420 void proto_reg_handoff_opcua(void)
422 opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
424 dissector_add_uint_range_with_preference("tcp.port", OPCUA_PORT_RANGE, opcua_handle);
428 * Editor modelines - http://www.wireshark.org/tools/modelines.html
433 * indent-tabs-mode: nil
436 * vi: set shiftwidth=4 tabstop=8 expandtab:
437 * :indentSize=4:tabSize=8:noTabs=true: