/****************************************************************************** ** $Id$ ** ** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved. ** Web: http://www.ascolab.com ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License ** as published by the Free Software Foundation; either version 2 ** of the License, or (at your option) any later version. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** Project: OpcUa Wireshark Plugin ** ** Description: OpcUa Protocol Decoder. ** ** Author: Gerhard Gappmeier ** Last change by: $Author: gergap $ ** ******************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "opcua_transport_layer.h" #include "opcua_security_layer.h" #include "opcua_application_layer.h" #include "opcua_complextypeparser.h" #include "opcua_serviceparser.h" #include "opcua_enumparser.h" #include "opcua_simpletypes.h" #include "opcua_hfindeces.h" /* forward reference */ void proto_register_opcua (void); void proto_reg_handoff_opcua (void); static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); /* declare parse function pointer */ typedef void (*FctParse)(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); static int proto_opcua = -1; /** Official IANA registered port for OPC UA Binary Protocol. */ static int global_opcua_port = 4840; static dissector_handle_t opcua_handle; /** subtree types */ gint ett_opcua_transport = -1; gint ett_opcua_extensionobject = -1; gint ett_opcua_nodeid = -1; /** OpcUa Transport Message Types */ enum MessageType { MSG_HELLO = 0, MSG_ACKNOWLEDGE, MSG_DISCONNECT, MSG_DATA_LAST_CHUNK, MSG_DATA, MSG_ABORT, MSG_ERROR, MSG_INVALID, MSG_UNKNOWN }; /** OpcUa Transport Message Type Names */ static char* g_szMessageTypes[] = { "Hello message", "Acknowledge message", "Disconnect message", "Data message, last chunk in message.", "Data message, further chunks must follow.", "Abort message", "Error message", "Invalid message", "Unknown message" }; /** 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) { module_t *opcua_module; if (proto_opcua == -1) { proto_opcua = proto_register_protocol( "OpcUa Binary Protocol", /* name */ "OpcUa", /* short name */ "opcua" /* abbrev */ ); } opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua); registerTransportLayerTypes(proto_opcua); registerSecurityLayerTypes(proto_opcua); registerApplicationLayerTypes(proto_opcua); registerSimpleTypes(proto_opcua); registerEnumTypes(proto_opcua); registerComplexTypes(); registerServiceTypes(); registerFieldTypes(proto_opcua); proto_register_subtree_array(ett, array_length(ett)); } /** Register sub protocol. * For TCP port 4840. */ void proto_reg_handoff_opcua(void) { static int Initialized=FALSE; if (!Initialized) { opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua); dissector_add("tcp.port", global_opcua_port, opcua_handle); } } /** header length that is needed to compute * the pdu length. * @see get_opcua_message_len */ #define FRAME_HEADER_LEN 8 /** returns the length of an OpcUa message. * This function reads the length information from * the transport header. */ static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset) { gint32 plen; /* the message length starts at offset 4 */ plen = tvb_get_letohl(tvb, offset + 4); return plen; } /** The main OpcUa dissector functions. * It uses tcp_dissect_pdus from packet-tcp.h * to reassemble the TCP data. */ static void dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN, get_opcua_message_len, dissect_opcua_message); } /** The OpcUa message dissector. * This method dissects full OpcUa messages. * It gets only called with reassembled data * from tcp_dissect_pdus. */ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { FctParse pfctParse = NULL; enum MessageType msgtype = MSG_INVALID; if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa"); } /* parse message type */ if (tvb->real_data[0] == 'U' && tvb->real_data[1] == 'A') { if (tvb->real_data[2] == 'T') { switch(tvb->real_data[3]) { case 'H': msgtype = MSG_HELLO; pfctParse = parseHello; break; case 'A': msgtype = MSG_ACKNOWLEDGE; pfctParse = parseAcknowledge; break; case 'D': msgtype = MSG_DISCONNECT; pfctParse = parseDisconnect; break; default: msgtype = MSG_INVALID; break; } } else if (tvb->real_data[2] == 'M') { switch(tvb->real_data[3]) { case 'G': msgtype = MSG_DATA_LAST_CHUNK; pfctParse = parseData; break; case 'C': msgtype = MSG_DATA; pfctParse = parseData; break; case 'A': msgtype = MSG_ABORT; pfctParse = parseAbort; break; case 'E': msgtype = MSG_ERROR; pfctParse = parseError; break; default: msgtype = MSG_INVALID; break; } } } else { msgtype = MSG_UNKNOWN; } /* Clear out stuff in the info column */ if(check_col(pinfo->cinfo, COL_INFO)) { col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]); } if (tree && pfctParse) { gint offset = 0; /* we are being asked for details */ proto_item *ti = NULL; proto_tree *transport_tree = NULL; ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, FALSE); transport_tree = proto_item_add_subtree(ti, ett_opcua_transport); /* call the transport message dissector */ (*pfctParse)(transport_tree, tvb, &offset); } }