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/prefs.h>
24 #include <epan/reassemble.h>
25 #include <epan/dissectors/packet-tcp.h>
26 #include "opcua_transport_layer.h"
27 #include "opcua_security_layer.h"
28 #include "opcua_application_layer.h"
29 #include "opcua_complextypeparser.h"
30 #include "opcua_serviceparser.h"
31 #include "opcua_enumparser.h"
32 #include "opcua_simpletypes.h"
33 #include "opcua_hfindeces.h"
35 void proto_register_opcua(void);
37 extern const value_string g_requesttypes[];
38 extern const int g_NumServices;
40 /* forward reference */
41 void proto_reg_handoff_opcua(void);
42 /* declare parse function pointer */
43 typedef int (*FctParse)(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset);
45 static int proto_opcua = -1;
46 static dissector_handle_t opcua_handle;
47 static range_t *global_tcp_ports_opcua;
48 /** Official IANA registered port for OPC UA Binary Protocol. */
49 #define OPCUA_PORT 4840
51 /** subtree types used in opcua_transport_layer.c */
52 gint ett_opcua_extensionobject = -1;
53 gint ett_opcua_nodeid = -1;
55 /** subtree types used locally */
56 static gint ett_opcua_transport = -1;
57 static gint ett_opcua_fragment = -1;
58 static gint ett_opcua_fragments = -1;
60 static int hf_opcua_fragments = -1;
61 static int hf_opcua_fragment = -1;
62 static int hf_opcua_fragment_overlap = -1;
63 static int hf_opcua_fragment_overlap_conflicts = -1;
64 static int hf_opcua_fragment_multiple_tails = -1;
65 static int hf_opcua_fragment_too_long_fragment = -1;
66 static int hf_opcua_fragment_error = -1;
67 static int hf_opcua_fragment_count = -1;
68 static int hf_opcua_reassembled_in = -1;
69 static int hf_opcua_reassembled_length = -1;
71 static const fragment_items opcua_frag_items = {
72 /* Fragment subtrees */
78 &hf_opcua_fragment_overlap,
79 &hf_opcua_fragment_overlap_conflicts,
80 &hf_opcua_fragment_multiple_tails,
81 &hf_opcua_fragment_too_long_fragment,
82 &hf_opcua_fragment_error,
83 &hf_opcua_fragment_count,
84 /* Reassembled in field */
85 &hf_opcua_reassembled_in,
86 /* Reassembled length field */
87 &hf_opcua_reassembled_length,
88 /* Reassembled data field */
95 static reassembly_table opcua_reassembly_table;
97 /** OpcUa Transport Message Types */
104 MSG_OPENSECURECHANNEL,
105 MSG_CLOSESECURECHANNEL,
109 /** OpcUa Transport Message Type Names */
110 static const char* g_szMessageTypes[] =
113 "Acknowledge message",
115 "UA Secure Conversation Message",
116 "OpenSecureChannel message",
117 "CloseSecureChannel message",
124 /** header length that is needed to compute
126 * @see get_opcua_message_len
128 #define FRAME_HEADER_LEN 8
130 /** returns the length of an OpcUa message.
131 * This function reads the length information from
132 * the transport header.
134 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb,
135 int offset, void *data _U_)
139 /* the message length starts at offset 4 */
140 plen = tvb_get_letohl(tvb, offset + 4);
145 /** The OpcUa message dissector.
146 * This method dissects full OpcUa messages.
147 * It gets only called with reassembled data
148 * from tcp_dissect_pdus.
150 static int dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
152 FctParse pfctParse = NULL;
153 enum MessageType msgtype = MSG_INVALID;
155 col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
157 /* parse message type */
158 if (tvb_memeql(tvb, 0, "HEL", 3) == 0)
161 pfctParse = parseHello;
163 else if (tvb_memeql(tvb, 0, "ACK", 3) == 0)
165 msgtype = MSG_ACKNOWLEDGE;
166 pfctParse = parseAcknowledge;
168 else if (tvb_memeql(tvb, 0, "ERR", 3) == 0)
171 pfctParse = parseError;
173 else if (tvb_memeql(tvb, 0, "MSG", 3) == 0)
175 msgtype = MSG_MESSAGE;
176 pfctParse = parseMessage;
178 else if (tvb_memeql(tvb, 0, "OPN", 3) == 0)
180 msgtype = MSG_OPENSECURECHANNEL;
181 pfctParse = parseOpenSecureChannel;
183 else if (tvb_memeql(tvb, 0, "CLO", 3) == 0)
185 msgtype = MSG_CLOSESECURECHANNEL;
186 pfctParse = parseCloseSecureChannel;
190 msgtype = MSG_INVALID;
193 /* Clear out stuff in the info column */
194 col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
200 tvbuff_t *next_tvb = tvb;
201 gboolean bParseService = TRUE;
202 gboolean bIsLastFragment = FALSE;
204 /* we are being asked for details */
205 proto_item *ti = NULL;
206 proto_tree *transport_tree = NULL;
208 ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
209 transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
211 /* MSG_MESSAGE might be fragmented, check for that */
212 if (msgtype == MSG_MESSAGE)
214 guint8 chunkType = 0;
215 guint32 opcua_seqid = 0;
216 guint32 opcua_num = 0;
217 guint32 opcua_seqnum = 0;
218 fragment_head *frag_msg = NULL;
222 chunkType = tvb_get_guint8(tvb, offset); offset += 1;
224 offset += 4; /* Message Size */
225 offset += 4; /* SecureChannelId */
226 offset += 4; /* Security Token Id */
228 opcua_num = tvb_get_letohl(tvb, offset); offset += 4; /* Security Sequence Number */
229 opcua_seqid = tvb_get_letohl(tvb, offset); offset += 4; /* Security RequestId */
231 /* check if tvb is part of a chunked message:
232 the UA protocol does not tell us that, so we look into
233 opcua_reassembly_table if the opcua_seqid belongs to a
235 frag_msg = fragment_get(&opcua_reassembly_table, pinfo, opcua_seqid, NULL);
236 if (frag_msg == NULL)
238 frag_msg = fragment_get_reassembled_id(&opcua_reassembly_table, pinfo, opcua_seqid);
241 if (frag_msg != NULL || chunkType != 'F')
243 gboolean bSaveFragmented = pinfo->fragmented;
244 gboolean bMoreFragments = TRUE;
245 tvbuff_t *new_tvb = NULL;
247 pinfo->fragmented = TRUE;
249 if (frag_msg == NULL)
256 /* the UA protocol does not number the chunks beginning from 0 but from a
257 arbitrary value, so we have to fake the numbers in the stored fragments.
258 this way Wireshark reassembles the message, as it expects the fragment
259 sequence numbers to start at 0 */
260 while (frag_msg->next) {frag_msg = frag_msg->next;}
261 opcua_seqnum = frag_msg->offset + 1;
263 if (chunkType == 'F')
265 bMoreFragments = FALSE;
269 frag_msg = fragment_add_seq_check(&opcua_reassembly_table,
273 opcua_seqid, /* ID for fragments belonging together */
275 opcua_seqnum, /* fragment sequence number */
276 tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */
277 bMoreFragments); /* More fragments? */
279 new_tvb = process_reassembled_data(tvb,
282 "Reassembled Message",
291 bIsLastFragment = TRUE;
295 /* Not last packet of reassembled UA message */
296 col_append_fstr(pinfo->cinfo, COL_INFO, " (Message fragment %u)", opcua_num);
306 /* only show transport header */
307 bParseService = FALSE;
308 next_tvb = tvb_new_subset_remaining(tvb, 0);
311 pinfo->fragmented = bSaveFragmented;
317 /* call the transport message dissector */
318 iServiceId = (*pfctParse)(transport_tree, tvb, pinfo, &offset);
320 /* parse the service if not chunked or last chunk */
321 if (msgtype == MSG_MESSAGE && bParseService)
323 if (bIsLastFragment != FALSE)
327 iServiceId = parseService(transport_tree, next_tvb, pinfo, &offset);
330 /* display the service type in addition to the message type */
331 if (iServiceId != -1)
333 const gchar *szServiceName = val_to_str((guint32)iServiceId, g_requesttypes, "ServiceId %d");
335 if (bIsLastFragment == FALSE)
337 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], szServiceName);
341 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s (Message Reassembled)", g_szMessageTypes[msgtype], szServiceName);
346 return tvb_reported_length(tvb);
349 /** The main OpcUa dissector functions.
350 * It uses tcp_dissect_pdus from packet-tcp.h
351 * to reassemble the TCP data.
353 static int dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
355 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
356 get_opcua_message_len, dissect_opcua_message, data);
357 return tvb_reported_length(tvb);
360 static void register_tcp_port(guint32 port)
363 dissector_add_uint("tcp.port", port, opcua_handle);
366 static void unregister_tcp_port(guint32 port)
369 dissector_delete_uint("tcp.port", port, opcua_handle);
375 reassembly_table_init(&opcua_reassembly_table,
376 &addresses_reassembly_table_functions);
382 reassembly_table_destroy(&opcua_reassembly_table);
385 /** plugin entry functions.
386 * This registers the OpcUa protocol.
388 void proto_register_opcua(void)
392 static hf_register_info hf[] =
394 /* id full name abbreviation type display strings bitmask blurb HFILL */
395 {&hf_opcua_fragments, {"Message fragments", "opcua.fragments", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL}},
396 {&hf_opcua_fragment, {"Message fragment", "opcua.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
397 {&hf_opcua_fragment_overlap, {"Message fragment overlap", "opcua.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
398 {&hf_opcua_fragment_overlap_conflicts, {"Message fragment overlapping with conflicting data", "opcua.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
399 {&hf_opcua_fragment_multiple_tails, {"Message has multiple tail fragments", "opcua.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
400 {&hf_opcua_fragment_too_long_fragment, {"Message fragment too long", "opcua.fragment.too_long_fragment", FT_BOOLEAN, BASE_NONE, NULL, 0x00, NULL, HFILL}},
401 {&hf_opcua_fragment_error, {"Message defragmentation error", "opcua.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
402 {&hf_opcua_fragment_count, {"Message fragment count", "opcua.fragment.count", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL}},
403 {&hf_opcua_reassembled_in, {"Reassembled in", "opcua.reassembled.in", FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL}},
404 {&hf_opcua_reassembled_length, {"Reassembled length", "opcua.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL}}
407 /** Setup protocol subtree array */
410 &ett_opcua_extensionobject,
412 &ett_opcua_transport,
417 module_t *opcua_module;
419 proto_opcua = proto_register_protocol(
420 "OpcUa Binary Protocol", /* name */
421 "OpcUa", /* short name */
425 registerTransportLayerTypes(proto_opcua);
426 registerSecurityLayerTypes(proto_opcua);
427 registerApplicationLayerTypes(proto_opcua);
428 registerSimpleTypes(proto_opcua);
429 registerEnumTypes(proto_opcua);
430 registerComplexTypes();
431 registerServiceTypes();
432 registerFieldTypes(proto_opcua);
434 proto_register_subtree_array(ett, array_length(ett));
436 tmp = g_strdup_printf("%d", OPCUA_PORT);
437 range_convert_str(&global_tcp_ports_opcua, tmp, 65535);
440 proto_register_field_array(proto_opcua, hf, array_length(hf));
442 register_init_routine(&init_opcua);
443 register_cleanup_routine(&cleanup_opcua);
445 /* register user preferences */
446 opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua);
447 prefs_register_range_preference(opcua_module, "tcp_ports",
449 "The TCP ports for the OPC UA TCP Binary Protocol (comma separated list)",
450 &global_tcp_ports_opcua, 65535);
454 void proto_reg_handoff_opcua(void)
456 static gboolean opcua_initialized = FALSE;
457 static range_t *tcp_ports_opcua = NULL;
459 if(!opcua_initialized)
461 opcua_handle = new_create_dissector_handle(dissect_opcua, proto_opcua);
462 opcua_initialized = TRUE;
466 /* clean up ports and their lists */
467 if (tcp_ports_opcua != NULL)
469 range_foreach(tcp_ports_opcua, unregister_tcp_port);
470 g_free(tcp_ports_opcua);
474 /* If we now have a PDU tree, register for the port or ports we have */
475 tcp_ports_opcua = range_copy(global_tcp_ports_opcua);
476 range_foreach(tcp_ports_opcua, register_tcp_port);
480 * Editor modelines - http://www.wireshark.org/tools/modelines.html
485 * indent-tabs-mode: nil
488 * vi: set shiftwidth=4 tabstop=8 expandtab:
489 * :indentSize=4:tabSize=8:noTabs=true: