summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStig Bjørlykke <stig@bjorlykke.org>2009-06-19 05:45:40 +0000
committerStig Bjørlykke <stig@bjorlykke.org>2009-06-19 05:45:40 +0000
commitb3e10e559aba9f14711ac221255ed59047ba9b01 (patch)
tree85a4442848d7e6ab7b3cc2c51d98ab86c110522d
parentea64b282678b562695741d3d51e629aebee0891e (diff)
downloadwireshark-b3e10e559aba9f14711ac221255ed59047ba9b01.tar.gz
Moved setting of length2 to make it always set.
svn path=/trunk/; revision=28777
-rw-r--r--epan/dissectors/packet-cip.c2787
-rw-r--r--epan/dissectors/packet-cip.h73
-rw-r--r--epan/dissectors/packet-enip.c1503
-rw-r--r--epan/dissectors/packet-enip.h40
-rw-r--r--epan/dissectors/packet-glbp.c3
5 files changed, 3074 insertions, 1332 deletions
diff --git a/epan/dissectors/packet-cip.c b/epan/dissectors/packet-cip.c
index c33a9017db..b22a613c3b 100644
--- a/epan/dissectors/packet-cip.c
+++ b/epan/dissectors/packet-cip.c
@@ -8,8 +8,10 @@
*
* Added support for Connection Configuration Object
* ryan wamsley * Copyright 2007
- * Added Additional Status text in Forward Open Response
- * ryan wamsley * Copyright 2008
+ *
+ * Object dependend services based on IOI
+ * Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
+ * Copyright 2007
*
* $Id$
*
@@ -32,6 +34,23 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+/*
+ &ett_cip, dissect_cip
+ &ett_path, dissect_epath
+ &ett_ekey_path, dissect_epath
+ &ett_mcsc, dissect_epath,
+ &ett_cia_path, dissect_epath
+ &ett_data_seg, dissect_epath
+ &ett_port_path, dissect_epath
+ &ett_rrsc, dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+ &ett_status_item dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+
+ &ett_cmd_data, dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+ &ett_ncp, dissect_cip_cm_data
+ &ett_mes_req, dissect_cip_cm_data
+ &ett_mult_ser, dissect_cip_mr_data
+*/
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -43,29 +62,51 @@
#include <glib.h>
#include <epan/packet.h>
-#include "packet-tcp.h"
+#include "packet-enip.h"
#include "packet-cip.h"
#define ENIP_CIP_INTERFACE 0
+typedef struct cip_req_info {
+ dissector_handle_t dissector;
+ guint8 bService;
+ guint IOILen;
+ void *pIOI;
+ void *pData;
+} cip_req_info_t;
+
+dissector_handle_t cip_handle;
+dissector_handle_t cip_class_generic_handle,cip_class_cm_handle,cip_class_mr_handle;
+dissector_handle_t cip_class_cco_handle;
+
/* Initialize the protocol and registered fields */
-static int proto_cip = -1;
-
-static int hf_cip_sc = -1;
-static int hf_cip_rr = -1;
-static int hf_cip_epath = -1;
-static int hf_cip_genstat = -1;
-
-static int hf_cip_fwo_comp = -1;
-static int hf_cip_fwo_mrev = -1;
-static int hf_cip_fwo_con_size = -1;
-static int hf_cip_fwo_fixed_var = -1;
-static int hf_cip_fwo_prio = -1;
-static int hf_cip_fwo_typ = -1;
-static int hf_cip_fwo_own = -1;
-static int hf_cip_fwo_dir = -1;
-static int hf_cip_fwo_trigg = -1;
-static int hf_cip_fwo_class = -1;
+static int proto_cip = -1;
+static int proto_cip_class_generic = -1;
+static int proto_cip_class_cm = -1;
+static int proto_cip_class_mr = -1;
+static int proto_cip_class_cco = -1;
+static int proto_enip = -1;
+
+static int hf_cip_sc = -1;
+static int hf_cip_rr = -1;
+static int hf_cip_epath = -1;
+static int hf_cip_genstat = -1;
+
+static int hf_cip_fwo_comp = -1;
+static int hf_cip_fwo_mrev = -1;
+
+static int hf_cip_cm_sc = -1;
+static int hf_cip_cm_rr = -1;
+static int hf_cip_cm_fwo_comp = -1;
+static int hf_cip_cm_fwo_mrev = -1;
+static int hf_cip_cm_fwo_con_size = -1;
+static int hf_cip_cm_fwo_fixed_var = -1;
+static int hf_cip_cm_fwo_prio = -1;
+static int hf_cip_cm_fwo_typ = -1;
+static int hf_cip_cm_fwo_own = -1;
+static int hf_cip_cm_fwo_dir = -1;
+static int hf_cip_cm_fwo_trigg = -1;
+static int hf_cip_cm_fwo_class = -1;
static int hf_cip_vendor = -1;
static int hf_cip_devtype = -1;
@@ -78,6 +119,9 @@ static int hf_cip_class32 = -1;
static int hf_cip_instance8 = -1;
static int hf_cip_instance16 = -1;
static int hf_cip_instance32 = -1;
+static int hf_cip_member8 = -1;
+static int hf_cip_member16 = -1;
+static int hf_cip_member32 = -1;
static int hf_cip_attribute8 = -1;
static int hf_cip_attribute16 = -1;
static int hf_cip_attribute32 = -1;
@@ -86,168 +130,205 @@ static int hf_cip_conpoint16 = -1;
static int hf_cip_conpoint32 = -1;
static int hf_cip_symbol = -1;
-static int hf_cip_data = -1;
+static int hf_cip_mr_sc = -1;
+static int hf_cip_mr_rr = -1;
-/* Initialize the subtree pointers */
-static gint ett_cip = -1;
-static gint ett_ekey_path = -1;
-static gint ett_cia_path = -1;
-static gint ett_data_seg = -1;
-static gint ett_rrsc = -1;
-static gint ett_mcsc = -1;
-static gint ett_ncp = -1;
-static gint ett_lsrcf = -1;
-static gint ett_mes_req = -1;
-static gint ett_cmd_data = -1;
-static gint ett_port_path = -1;
-static gint ett_mult_ser = -1;
-static gint ett_path = -1;
-static gint ett_status_item = -1;
+static int hf_cip_cco_sc = -1;
+static int hf_cip_cco_rr = -1;
+/* Initialize the subtree pointers */
+static gint ett_cip = -1;
+static gint ett_cip_class_generic = -1;
+static gint ett_cip_class_mr = -1;
+static gint ett_cip_class_cm = -1;
+static gint ett_cip_class_cco = -1;
+
+static gint ett_path = -1;
+static gint ett_ekey_path = -1;
+static gint ett_mcsc = -1;
+static gint ett_cia_path = -1;
+static gint ett_data_seg = -1;
+static gint ett_port_path = -1;
+
+static gint ett_rrsc = -1;
+static gint ett_status_item = -1;
+static gint ett_cmd_data = -1;
+
+static gint ett_cm_rrsc = -1;
+static gint ett_cm_ncp = -1;
+static gint ett_cm_mes_req = -1;
+static gint ett_cm_cmd_data = -1;
+
+static gint ett_mr_rrsc = -1;
+static gint ett_mr_mult_ser = -1;
+static gint ett_mr_cmd_data = -1;
+
+static gint ett_cco_rrsc = -1;
+static gint ett_cco_cmd_data = -1;
+
+static dissector_table_t subdissector_class_table;
+static dissector_table_t subdissector_symbol_table;
/* Translate function to string - CIP Service codes */
static const value_string cip_sc_vals[] = {
- { SC_GET_ATT_ALL, "Get Attribute All" },
- { SC_SET_ATT_ALL, "Set Attribute All" },
- { SC_GET_ATT_LIST, "Get Attribute List" },
- { SC_SET_ATT_LIST, "Set Attribute List" },
- { SC_RESET, "Reset" },
- { SC_START, "Start" },
- { SC_STOP, "Stop" },
- { SC_CREATE, "Create" },
- { SC_DELETE, "Delete" },
- { SC_APPLY_ATTRIBUTES, "Apply Attributes" },
- { SC_GET_ATT_SINGLE, "Get Attribute Single" },
- { SC_SET_ATT_SINGLE, "Set Attribute Single" },
- { SC_FIND_NEXT_OBJ_INST, "Find Next Object Instance" },
- { SC_RESTOR, "Restore" },
- { SC_SAVE, "Save" },
- { SC_NO_OP, "Nop" },
- { SC_GET_MEMBER, "Get Member" },
- { SC_SET_MEMBER, "Set Member" },
- { SC_MULT_SERV_PACK, "Multiple Service Packet" },
-
- /* Some class specific services */
- { SC_FWD_CLOSE, "Forward Close" },
- { SC_FWD_OPEN, "Forward Open" },
- { SC_UNCON_SEND, "Unconnected Send" },
-
- /* Connection Configuration Object services */
- { SC_KICK_TIMER, "Kick Timer" },
- { SC_OPEN_CONN, "Open Connection" },
- { SC_CLOSE_CONN, "Close Connection" },
- { SC_CHANGE_START, "Change Start" },
- { SC_GET_STATUS, "Get Status" },
- { SC_CHANGE_COMPLETE, "Change Complete" },
-
- { 0, NULL }
+ { SC_GET_ATT_ALL, "Get Attribute All" },
+ { SC_SET_ATT_ALL, "Set Attribute All" },
+ { SC_GET_ATT_LIST, "Get Attribute List" },
+ { SC_SET_ATT_LIST, "Set Attribute List" },
+ { SC_RESET, "Reset" },
+ { SC_START, "Start" },
+ { SC_STOP, "Stop" },
+ { SC_CREATE, "Create" },
+ { SC_DELETE, "Delete" },
+ { SC_APPLY_ATTRIBUTES, "Apply Attributes" },
+ { SC_GET_ATT_SINGLE, "Get Attribute Single" },
+ { SC_SET_ATT_SINGLE, "Set Attribute Single" },
+ { SC_FIND_NEXT_OBJ_INST, "Find Next Object Instance" },
+ { SC_RESTOR, "Restore" },
+ { SC_SAVE, "Save" },
+ { SC_NO_OP, "Nop" },
+ { SC_GET_MEMBER, "Get Member" },
+ { SC_SET_MEMBER, "Set Member" },
+
+ { 0, NULL }
};
-/* Translate function to string - CIP Service codes that collide with cip_sc_vals */
-static const value_string cip_sc_vals_cco[] = {
- /* Connection Configuration Object services */
- { SC_STOP_CONN, "Stop Connection" }, /* collision with SC_UNCON_SEND */
- { SC_AUDIT_CHANGE, "Audit Changes" }, /* collision with SC_UNCON_SEND */
+/* Translate function to string - CIP Service codes for MR */
+static const value_string cip_sc_vals_mr[] = {
+ { SC_GET_ATT_ALL, "Get Attribute All" },
+ { SC_SET_ATT_ALL, "Set Attribute All" },
+ { SC_GET_ATT_LIST, "Get Attribute List" },
+ { SC_SET_ATT_LIST, "Set Attribute List" },
+ /* Some class specific services */
+ { SC_MULT_SERV_PACK, "Multiple Service Packet" },
- { 0, NULL }
+ { 0, NULL }
+};
+
+/* Translate function to string - CIP Service codes for CM */
+static const value_string cip_sc_vals_cm[] = {
+ { SC_GET_ATT_ALL, "Get Attribute All" },
+ { SC_SET_ATT_ALL, "Set Attribute All" },
+ { SC_GET_ATT_LIST, "Get Attribute List" },
+ { SC_SET_ATT_LIST, "Set Attribute List" },
+ /* Some class specific services */
+ { SC_CM_FWD_CLOSE, "Forward Close" },
+ { SC_CM_FWD_OPEN, "Forward Open" },
+ { SC_CM_UNCON_SEND, "Unconnected Send" },
+
+ { 0, NULL }
+};
+
+/* Translate function to string - CIP Service codes for CCO */
+static const value_string cip_sc_vals_cco[] = {
+ { SC_CCO_KICK_TIMER, "Kick Timer" },
+ { SC_CCO_OPEN_CONN, "Open Connection" },
+ { SC_CCO_CLOSE_CONN, "Close Connection" },
+ { SC_CCO_STOP_CONN, "Stop Connection" },
+ { SC_CCO_CHANGE_START, "Change Start" },
+ { SC_CCO_GET_STATUS, "Get Status" },
+ { SC_CCO_CHANGE_COMPLETE, "Change Complete" },
+ { SC_CCO_AUDIT_CHANGE, "Audit Changes" },
+
+ { 0, NULL }
};
/* Translate function to string - CIP Request/Response */
static const value_string cip_sc_rr[] = {
- { 0, "Request" },
- { 1, "Response" },
+ { 0, "Request" },
+ { 1, "Response" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Compatibility */
static const value_string cip_com_bit_vals[] = {
- { 0, "Bit Cleared" },
- { 1, "Bit Set" },
+ { 0, "Bit Cleared" },
+ { 1, "Bit Set" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Connection priority */
static const value_string cip_con_prio_vals[] = {
- { 0, "Low Priority" },
- { 1, "High Priority" },
- { 2, "Scheduled" },
- { 3, "Urgent" },
+ { 0, "Low Priority" },
+ { 1, "High Priority" },
+ { 2, "Scheduled" },
+ { 3, "Urgent" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Connection size fixed or variable */
static const value_string cip_con_fw_vals[] = {
- { 0, "Fixed" },
- { 1, "Variable" },
+ { 0, "Fixed" },
+ { 1, "Variable" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Connection owner */
static const value_string cip_con_owner_vals[] = {
- { 0, "Exclusive" },
- { 1, "Redundant" },
+ { 0, "Exclusive" },
+ { 1, "Redundant" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Connection direction */
static const value_string cip_con_dir_vals[] = {
- { 0, "Client" },
- { 1, "Server" },
+ { 0, "Client" },
+ { 1, "Server" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Production trigger */
static const value_string cip_con_trigg_vals[] = {
- { 0, "Cyclic" },
- { 1, "Change-Of-State" },
- { 2, "Application Object" },
+ { 0, "Cyclic" },
+ { 1, "Change-Of-State" },
+ { 2, "Application Object" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Transport class */
static const value_string cip_con_class_vals[] = {
- { 0, "0" },
- { 1, "1" },
- { 2, "2" },
- { 3, "3" },
+ { 0, "0" },
+ { 1, "1" },
+ { 2, "2" },
+ { 3, "3" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Connection type */
static const value_string cip_con_type_vals[] = {
- { 0, "Null" },
- { 1, "Multicast" },
- { 2, "Point to Point" },
- { 3, "Reserved" },
+ { 0, "Null" },
+ { 1, "Multicast" },
+ { 2, "Point to Point" },
+ { 3, "Reserved" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate function to string - Timeout Multiplier */
static const value_string cip_con_time_mult_vals[] = {
- { 0, "*4" },
- { 1, "*8" },
- { 2, "*16" },
- { 3, "*32" },
- { 4, "*64" },
- { 5, "*128" },
- { 6, "*256" },
- { 7, "*512" },
+ { 0, "*4" },
+ { 1, "*8" },
+ { 2, "*16" },
+ { 3, "*32" },
+ { 4, "*64" },
+ { 5, "*128" },
+ { 6, "*256" },
+ { 7, "*512" },
{ 0, NULL }
};
/* Translate function to string - CIP General Status codes */
static const value_string cip_gs_vals[] = {
- { CI_GRC_SUCCESS, "Success" },
+ { CI_GRC_SUCCESS, "Success" },
{ CI_GRC_FAILURE, "Connection failure" },
{ CI_GRC_NO_RESOURCE, "Resource unavailable" },
{ CI_GRC_BAD_DATA, "Invalid parameter value" },
@@ -288,60 +369,14 @@ static const value_string cip_gs_vals[] = {
{ CI_GRC_INVALID_MEMBER, "Invalid Member ID" },
{ CI_GRC_MEMBER_NOT_SETTABLE, "Member not settable" },
- { 0, NULL }
+ { 0, NULL }
};
-/* Translate function to string - CIP Extended Status codes */
-static const value_string cip_es_vals[] = {
- { CI_GRC_SUCCESS, "Success" },
- { CI_SREC_CONNECTION_IN_USE, "Connection in Use or Duplicate Forward Open" },
- { CI_SREC_TCLASS_TRIGGER_ERR, "Transport Class and Trigger combination not supported" },
- { CI_SREC_OWNERSHIP_CONFLICT, "Ownership Conflict" },
- { CI_SREC_CONN_NOT_FOUND, "Connection not found at target application" },
- { CI_SREC_INVALID_CONN_TYPE, "Invalid Connection Type" },
- { CI_SREC_INVALID_CONN_SIZE, "Invalid Connection Size" },
- { CI_SREC_DEV_NOT_CONFIGURED, "Device not configured" },
- { CI_SREC_UNSUPPORTED_RPI, "RPI not supported" },
- { CI_SREC_NO_MORE_CONNS, "Connection Manager cannot support any more connections" },
- { CI_SREC_VEN_OR_PCODE_MISMATCH, "The Vendor Id or Product Code did not match the device" },
- { CI_SREC_PRODTYPE_MISMATCH, "The Product Type did not match the device" },
- { CI_SREC_REVISION_MISMATCH, "The Major or Minor Revision did not match the device" },
- { CI_SREC_BAD_CONN_POINT, "Invalid Connection Point" },
- { CI_SREC_INVAL_CONFIG_FRMT, "Invalid Configuration Format" },
- { CI_SREC_NO_CONTROL_CONN, "There is no controlling connection currently open" },
- { CI_SREC_NO_MORE_CONN_SUPPORT, "Target Application cannot support any more connections" },
- { CI_SREC_RPI_SMALLERTHAN_PIT, "RPI is smaller than the Production Inhibit Time" },
- { CI_SREC_CONN_ALREADY_CLOSED, "Connection cannot be closed: connection has timed out" },
- { CI_SREC_UNCONN_SND_TIMEOUT, "Unconnected Send timed out" },
- { CI_SREC_UNCONN_PARM_ERR, "Parameter Error in Unconnected Send Service" },
- { CI_SREC_UCONN_TOO_LARGE, "Message too large for Unconnected message service" },
- { CI_SREC_UCONN_ACK_NO_REP, "Unconnected acknowledge without reply" },
- { CI_SREC_NO_MEMORY, "No buffer memory available" },
- { CI_SREC_NO_NET_BANDWIDTH, "Network Bandwidth not available for data" },
- { CI_SREC_NO_SCREENERS, "No screeners available" },
- { CI_SREC_NO_REALTIME_CONFIG, "Not Configured to send real-time data" },
- { CI_SREC_INVALID_PORT, "Port specified in Port Segment Not Available" },
- { CI_SREC_LINKADDR_NOT_AVAIL, "Link Address specified in Port Segment Not Available" },
- { CI_SREC_INVALID_SEGMENT_TYP, "Invalid Segment Type or Segment Value in Path" },
- { CI_SREC_CLOSE_PATH_ERR, "Error in close path" },
- { CI_SREC_NO_SCHED, "Scheduling not specified" },
- { CI_SREC_INVALID_LINK_ADDR, "Link Address to Self Invalid" },
- { CI_SREC_UNAVAIL_RESOURCE, "Resources on Secondary Unavailable" },
- { CI_SREC_CONN_ALREADY_ESTAB, "Connection already established" },
- { CI_SREC_DCONN_ALREADY_ESTAB, "Direct connection already established" },
- { CI_SREC_MISC, "Miscellaneous" },
- { CI_SREC_REDUNDANT_MISMATCH, "Redundant connection mismatch" },
- { CI_SREC_NO_CONSUME_RESRC, "No more consumer resources available in the producing module" },
- { CI_SREC_NO_CONN_RESRC, "No connection resources exist for target path" },
-
- { 0, NULL }
-} ;
-
/* Translate Vendor ID:s */
const value_string cip_vendor_vals[] = {
VENDOR_ID_LIST
- { 0, NULL }
+ { 0, NULL }
};
/* Translate Device Profile:s */
@@ -368,64 +403,130 @@ const value_string cip_devtype_vals[] = {
{ 0, NULL }
};
+#define CI_CLS_MR 0x02 /* Message Router */
+#define CI_CLS_CM 0x06 /* Connection Manager */
+#define CI_CLS_CCO 0xF3 /* Connection Configuration Object */
+
/* Translate class names */
static const value_string cip_class_names_vals[] = {
- { 0x01, "Identity Object" },
- { 0x02, "Message Router" },
- { 0x03, "DeviceNet Object" },
- { 0x04, "Assembly Object" },
- { 0x05, "Connection Object" },
- { 0x06, "Connection Manager" },
- { 0x07, "Register Object" },
- { 0x08, "Discrete Input Point Object" },
- { 0x09, "Discrete Output Point Object" },
- { 0x0A, "Analog Input Point Object" },
- { 0x0B, "Analog Output Point Object" },
- { 0x0E, "Presence Sensing Object" },
- { 0x0F, "Parameter Object" },
- { 0x10, "Parameter Group Object" },
- { 0x12, "Group Object" },
- { 0x1D, "Discrete Input Group Object" },
- { 0x1E, "Discrete Output Group Object" },
- { 0x1F, "Discrete Group Object" },
- { 0x20, "Analog Input Group Object" },
- { 0x21, "Analog Output Group Object" },
- { 0x22, "Analog Group Object" },
- { 0x23, "Position Sensor Object" },
- { 0x24, "Position Controller Supervisor Object" },
- { 0x25, "Position Controller Object" },
- { 0x26, "Block Sequencer Object" },
- { 0x27, "Command Block Object" },
- { 0x28, "Motor Data Object" },
- { 0x29, "Control Supervisor Object" },
- { 0x2A, "AC/DC Drive Object" },
- { 0x2B, "Acknowledge Handler Object" },
- { 0x2C, "Overload Object" },
- { 0x2D, "Softstart Object" },
- { 0x2E, "Selection Object" },
- { 0x30, "S-Device Supervisor Object" },
- { 0x31, "S-Analog Sensor Object" },
- { 0x32, "S-Analog Actuator Object" },
- { 0x33, "S-Single Stage Controller Object" },
- { 0x34, "S-Gas Calibration Object" },
- { 0x35, "Trip Point Object" },
- { 0x37, "File Object" },
- { 0x38, "S-Partial Pressure Object" },
- { 0xF0, "ControlNet Object" },
- { 0xF1, "ControlNet Keeper Object" },
- { 0xF2, "ControlNet Scheduling Object" },
- { 0xF3, "Connection Configuration Object" },
- { 0xF4, "Port Object" },
- { 0xF5, "TCP/IP Interface Object" },
- { 0xF6, "EtherNet Link Object" },
-
- { 0, NULL }
+ { 0x01, "Identity Object" },
+ { 0x02, "Message Router" },
+ { 0x03, "DeviceNet Object" },
+ { 0x04, "Assembly Object" },
+ { 0x05, "Connection Object" },
+ { 0x06, "Connection Manager" },
+ { 0x07, "Register Object" },
+ { 0x08, "Discrete Input Point Object" },
+ { 0x09, "Discrete Output Point Object" },
+ { 0x0A, "Analog Input Point Object" },
+ { 0x0B, "Analog Output Point Object" },
+ { 0x0E, "Presence Sensing Object" },
+ { 0x0F, "Parameter Object" },
+ { 0x10, "Parameter Group Object" },
+ { 0x12, "Group Object" },
+ { 0x1D, "Discrete Input Group Object" },
+ { 0x1E, "Discrete Output Group Object" },
+ { 0x1F, "Discrete Group Object" },
+ { 0x20, "Analog Input Group Object" },
+ { 0x21, "Analog Output Group Object" },
+ { 0x22, "Analog Group Object" },
+ { 0x23, "Position Sensor Object" },
+ { 0x24, "Position Controller Supervisor Object" },
+ { 0x25, "Position Controller Object" },
+ { 0x26, "Block Sequencer Object" },
+ { 0x27, "Command Block Object" },
+ { 0x28, "Motor Data Object" },
+ { 0x29, "Control Supervisor Object" },
+ { 0x2A, "AC/DC Drive Object" },
+ { 0x2B, "Acknowledge Handler Object" },
+ { 0x2C, "Overload Object" },
+ { 0x2D, "Softstart Object" },
+ { 0x2E, "Selection Object" },
+ { 0x30, "S-Device Supervisor Object" },
+ { 0x31, "S-Analog Sensor Object" },
+ { 0x32, "S-Analog Actuator Object" },
+ { 0x33, "S-Single Stage Controller Object" },
+ { 0x34, "S-Gas Calibration Object" },
+ { 0x35, "Trip Point Object" },
+ { 0x37, "File Object" },
+ { 0x38, "S-Partial Pressure Object" },
+ { 0xF0, "ControlNet Object" },
+ { 0xF1, "ControlNet Keeper Object" },
+ { 0xF2, "ControlNet Scheduling Object" },
+ { 0xF3, "Connection Configuration Object" },
+ { 0xF4, "Port Object" },
+ { 0xF5, "TCP/IP Interface Object" },
+ { 0xF6, "EtherNet Link Object" },
+
+ { 0, NULL }
};
+static void
+dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo, cip_req_info_t *preq_info );
+
+static proto_item*
+add_byte_array_text_to_proto_tree( proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char* str )
+{
+ const guint8 *tmp;
+ char *tmp2, *tmp2start;
+ proto_item *pi;
+ int i,tmp_length,tmp2_length;
+ guint32 octet;
+ /* At least one version of Apple's C compiler/linker is buggy, causing
+ a complaint from the linker about the "literal C string section"
+ not ending with '\0' if we initialize a 16-element "char" array with
+ a 16-character string, the fact that initializing such an array with
+ such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
+ '\0' byte in the string nonwithstanding. */
+ static const char my_hex_digits[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+
+ if( ( length * 2 ) > 32 )
+ {
+ tmp_length = 16;
+ tmp2_length = 36;
+ }
+ else
+ {
+ tmp_length = length;
+ tmp2_length = ( length * 2 ) + 1;
+ }
+
+ tmp = tvb_get_ptr( tvb, start, tmp_length );
+ tmp2 = (char*)ep_alloc( tmp2_length );
+
+ tmp2start = tmp2;
+
+ for( i = 0; i < tmp_length; i++ )
+ {
+ octet = tmp[i];
+ octet >>= 4;
+ *tmp2++ = my_hex_digits[octet&0xF];
+ octet = tmp[i];
+ *tmp2++ = my_hex_digits[octet&0xF];
+ }
+
+ if( tmp_length != length )
+ {
+ *tmp2++ = '.';
+ *tmp2++ = '.';
+ *tmp2++ = '.';
+ }
+
+ *tmp2 = 0;
+
+ pi = proto_tree_add_text( tree, tvb, start, length, "%s%s", str, tmp2start );
+
+ return( pi );
+
+} /* end of add_byte_array_text_to_proto_tree() */
+
/* Dissect EPATH */
static void
-dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_length )
+dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_length, gboolean generate )
{
int pathpos, temp_data, temp_data2, seg_size, i, temp_word;
unsigned char segment_type, opt_link_size;
@@ -434,14 +535,18 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
proto_tree *e_key_tree, *cia_tree, *ds_tree;
proto_item *mcpi, *port_item, *net_item;
proto_tree *mc_tree;
+ proto_item *it;
proto_item *hidden_item;
/* Create a sub tree for the epath */
path_tree = proto_item_add_subtree( epath_item, ett_path );
- hidden_item = proto_tree_add_item(path_tree, hf_cip_epath,
- tvb, offset, path_length, TRUE );
- PROTO_ITEM_SET_HIDDEN(hidden_item);
+ if ( !generate )
+ {
+ hidden_item = proto_tree_add_item(path_tree, hf_cip_epath,
+ tvb, offset, path_length, TRUE );
+ PROTO_ITEM_SET_HIDDEN(hidden_item);
+ }
pathpos = 0;
@@ -457,24 +562,55 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
case CI_PORT_SEGMENT:
port_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 0, "Port Segment" );
+ if ( generate )
+ {
+ port_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Port Segment" );
+ PROTO_ITEM_SET_GENERATED(port_item);
+ }
+ else
+ port_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 0, "Port Segment" );
port_tree = proto_item_add_subtree( port_item, ett_port_path );
/* Add port number */
- proto_tree_add_item( port_tree, hf_cip_port, tvb, offset + pathpos, 1, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(port_tree, hf_cip_port, NULL, 0, 0, ( segment_type & 0x0F ) );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( port_tree, hf_cip_port, tvb, offset + pathpos, 1, TRUE );
proto_item_append_text( epath_item, "Port: %d", ( segment_type & 0x0F ) );
proto_item_append_text( port_item, ": Port: %d", ( segment_type & 0x0F ) );
if( segment_type & 0x10 )
{
/* Add Extended Link Address flag */
- proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: TRUE" );
+ if ( generate )
+ {
+ it = proto_tree_add_text( port_tree, NULL, 0, 0, "Extended Link Address: TRUE" );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ it = proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: TRUE" );
/* Add size of extended link address */
opt_link_size = tvb_get_guint8( tvb, offset + pathpos + 1 );
- proto_tree_add_text( port_tree, tvb, offset+pathpos+1, 1, "Link Address Size: %d", opt_link_size );
+ if ( generate )
+ {
+ it = proto_tree_add_text( port_tree, NULL, 0, 0, "Link Address Size: %d", opt_link_size );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ it = proto_tree_add_text( port_tree, tvb, offset+pathpos+1, 1, "Link Address Size: %d", opt_link_size );
/* Add extended link address */
- proto_tree_add_item( port_tree, hf_cip_link_address_string, tvb, offset+pathpos+2, opt_link_size, FALSE );
+ if ( generate )
+ {
+ it = proto_tree_add_string(port_tree, hf_cip_link_address_string, NULL, 0, 0, tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( port_tree, hf_cip_link_address_string, tvb, offset+pathpos+2, opt_link_size, FALSE );
proto_item_append_text( epath_item, ", Address: %s", tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
proto_item_append_text( port_item, ", Address: %s", tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
@@ -493,10 +629,22 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else
{
/* Add Extended Link Address flag */
- proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: FALSE" );
+ if ( generate )
+ {
+ it = proto_tree_add_text( port_tree, NULL, 0, 0, "Extended Link Address: FALSE" );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ it = proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: FALSE" );
/* Add Link Address */
- proto_tree_add_item( port_tree, hf_cip_link_address_byte, tvb, offset+pathpos+1, 1, FALSE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(port_tree, hf_cip_link_address_byte, NULL, 0, 0, tvb_get_guint8( tvb, offset + pathpos + 1 ) );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( port_tree, hf_cip_link_address_byte, tvb, offset+pathpos+1, 1, FALSE );
proto_item_append_text( epath_item, ", Address: %d",tvb_get_guint8( tvb, offset + pathpos + 1 ) );
proto_item_append_text( port_item, ", Address: %d",tvb_get_guint8( tvb, offset + pathpos + 1 ) );
@@ -516,16 +664,28 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
/* Logical Class ID, do a format check */
- if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
- {
- temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Class Segment (0x%02X)", segment_type );
+ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+ {
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Class Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Class Segment (0x%02X)", segment_type );
/* Create a sub tree for the class */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 8-bit class number */
- proto_tree_add_item( cia_tree, hf_cip_class8, tvb, offset + pathpos + 1, 1, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_class8, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_class8, tvb, offset + pathpos + 1, 1, TRUE );
proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%02X" ) );
/* 2 bytes of path used */
@@ -534,13 +694,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
{
temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Class Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Class Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Class Segment (0x%02X)", segment_type );
/* Create a sub tree for the class */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 16-bit class number */
- proto_tree_add_item( cia_tree, hf_cip_class16, tvb, offset + pathpos + 2, 2, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_class16, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_class16, tvb, offset + pathpos + 2, 2, TRUE );
proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%04X" ) );
/* 4 bytes of path used */
@@ -549,13 +721,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
{
temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
/* Create a sub tree for the class */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 32-bit class number */
- proto_tree_add_item( cia_tree, hf_cip_class32, tvb, offset + pathpos + 2, 4, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_class32, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_class32, tvb, offset + pathpos + 2, 4, TRUE );
proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%08X" ) );
/* 6 bytes of path used */
@@ -574,16 +758,28 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
/* Logical Instance ID, do a format check */
- if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
- {
- temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
+ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+ {
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
/* Create a sub tree for the instance */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 8-bit instance number */
- proto_tree_add_item( cia_tree, hf_cip_instance8, tvb, offset + pathpos + 1, 1, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_instance8, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_instance8, tvb, offset + pathpos + 1, 1, TRUE );
proto_item_append_text( epath_item, "Instance: 0x%02X", temp_data );
/* 2 bytes of path used */
@@ -592,13 +788,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
{
temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
/* Create a sub tree for the instance */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 16-bit instance number */
- proto_tree_add_item( cia_tree, hf_cip_instance16, tvb, offset + pathpos + 2, 2, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_instance16, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_instance16, tvb, offset + pathpos + 2, 2, TRUE );
proto_item_append_text( epath_item, "Instance: 0x%04X", temp_data );
/* 4 bytes of path used */
@@ -607,13 +815,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
{
temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
/* Create a sub tree for the instance */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 32-bit instance number */
- proto_tree_add_item( cia_tree, hf_cip_instance32, tvb, offset + pathpos + 2, 4, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_uint(cia_tree, hf_cip_instance32, NULL, 0, 0, temp_data );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_instance32, tvb, offset + pathpos + 2, 4, TRUE );
proto_item_append_text( epath_item, "Instance: 0x%08X", temp_data );
@@ -629,20 +849,125 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
break;
+ case CI_LOGICAL_SEG_MBR_ID:
+
+ /* Logical Member ID, do a format check */
+
+ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+ {
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Member Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Member Segment (0x%02X)", segment_type );
+
+ /* Create a sub tree for the attribute */
+ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+ /* Display the 8-bit attribute number */
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_member8, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_member8, tvb, offset + pathpos + 1, 1, TRUE );
+ proto_item_append_text( epath_item, "Member: 0x%02X", temp_data );
+
+ /* 2 bytes of path used */
+ pathpos += 2;
+ }
+ else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
+ {
+ temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Member Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Member Segment (0x%02X)", segment_type );
+
+ /* Create a sub tree for the attribute */
+ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+ /* Display the 16-bit attribute number */
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_member16, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_member16, tvb, offset + pathpos + 2, 2, TRUE );
+ proto_item_append_text( epath_item, "Member: 0x%04X", temp_data );
+
+ /* 4 bytes of path used */
+ pathpos += 4;
+ }
+ else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
+ {
+ temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Member Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Member Segment (0x%02X)", segment_type );
+
+ /* Create a sub tree for the attribute */
+ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+ /* Display the 32-bit attribute number */
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_member32, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_member32, tvb, offset + pathpos + 2, 4, TRUE );
+ proto_item_append_text( epath_item, "Member: 0x%08X", temp_data );
+
+ /* 6 bytes of path used */
+ pathpos += 6;
+ }
+ else
+ {
+ /* Unsupported logical segment format */
+ proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Logical Segment Format" );
+ return;
+ }
+ break;
+
case CI_LOGICAL_SEG_ATTR_ID:
/* Logical Attribute ID, do a format check */
- if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
- {
- temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+ {
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
/* Create a sub tree for the attribute */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 8-bit attribute number */
- proto_tree_add_item( cia_tree, hf_cip_attribute8, tvb, offset + pathpos + 1, 1, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_attribute8, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_attribute8, tvb, offset + pathpos + 1, 1, TRUE );
proto_item_append_text( epath_item, "Attribute: 0x%02X", temp_data );
/* 2 bytes of path used */
@@ -651,13 +976,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
{
temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
/* Create a sub tree for the attribute */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 16-bit attribute number */
- proto_tree_add_item( cia_tree, hf_cip_attribute16, tvb, offset + pathpos + 2, 2, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_attribute16, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_attribute16, tvb, offset + pathpos + 2, 2, TRUE );
proto_item_append_text( epath_item, "Attribute: 0x%04X", temp_data );
/* 4 bytes of path used */
@@ -666,13 +1003,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
{
temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
- cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(cia_item);
+ }
+ else
+ cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
/* Create a sub tree for the attribute */
cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
/* Display the 32-bit attribute number */
- proto_tree_add_item( cia_tree, hf_cip_attribute32, tvb, offset + pathpos + 2, 4, TRUE );
+ if ( generate )
+ {
+ it = proto_tree_add_item( cia_tree, hf_cip_attribute32, NULL, 0, 0, TRUE );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_item( cia_tree, hf_cip_attribute32, tvb, offset + pathpos + 2, 4, TRUE );
proto_item_append_text( epath_item, "Attribute: 0x%08X", temp_data );
/* 6 bytes of path used */
@@ -691,9 +1040,9 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
/* Logical Connection point , do a format check */
- if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
- {
- temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+ {
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Connection Point Segment (0x%02X)", segment_type );
/* Create a sub tree for the connection point */
@@ -765,37 +1114,37 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 1, 1, "Key Format: 0x%02X", temp_data );
/* Get the Vendor ID */
- temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
+ temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
proto_tree_add_item( e_key_tree, hf_cip_vendor, tvb, offset + pathpos + 2, 2, TRUE);
proto_item_append_text( qi, "VendorID: 0x%04X", temp_data );
/* Get Device Type */
- temp_data = tvb_get_letohs( tvb, offset + pathpos + 4 );
- proto_tree_add_item( e_key_tree, hf_cip_devtype, tvb, offset + pathpos + 4, 2, TRUE);
+ temp_data = tvb_get_letohs( tvb, offset + pathpos + 4 );
+ proto_tree_add_item( e_key_tree, hf_cip_devtype, tvb, offset + pathpos + 4, 2, TRUE);
proto_item_append_text( qi, ", DevTyp: 0x%04X", temp_data );
/* Product Code */
- temp_data = tvb_get_letohs( tvb, offset + pathpos + 6 );
+ temp_data = tvb_get_letohs( tvb, offset + pathpos + 6 );
proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 6, 2, "Product Code: 0x%04X", temp_data );
/* Major revision/Compatibility */
- temp_data = tvb_get_guint8( tvb, offset + pathpos + 8 );
+ temp_data = tvb_get_guint8( tvb, offset + pathpos + 8 );
- /* Add Major revision/Compatibility tree */
- mcpi = proto_tree_add_text(e_key_tree, tvb, offset + pathpos + 8, 1, "Compatibility ");
- mc_tree = proto_item_add_subtree(mcpi, ett_mcsc);
+ /* Add Major revision/Compatibility tree */
+ mcpi = proto_tree_add_text(e_key_tree, tvb, offset + pathpos + 8, 1, "Compatibility ");
+ mc_tree = proto_item_add_subtree(mcpi, ett_mcsc);
- /* Add Compatibility bit info */
+ /* Add Compatibility bit info */
proto_tree_add_item(mc_tree, hf_cip_fwo_comp,
- tvb, offset + pathpos + 8, 1, TRUE );
+ tvb, offset + pathpos + 8, 1, TRUE );
proto_item_append_text( mcpi, "%s, Major Revision: %d",
val_to_str( ( temp_data & 0x80 )>>7, cip_com_bit_vals , "" ),
temp_data & 0x7F );
- /* Major revision */
- proto_tree_add_item(mc_tree, hf_cip_fwo_mrev,
- tvb, offset + pathpos + 8, 1, TRUE );
+ /* Major revision */
+ proto_tree_add_item(mc_tree, hf_cip_fwo_mrev,
+ tvb, offset + pathpos + 8, 1, TRUE );
/* Minor revision */
temp_data2 = tvb_get_guint8( tvb, offset + pathpos + 9 );
@@ -842,7 +1191,13 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
case CI_DATA_SEG_SIMPLE:
/* Simple data segment */
- ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Simple Data Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ ds_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Simple Data Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(ds_item);
+ }
+ else
+ ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Simple Data Segment (0x%02X)", segment_type );
/* Create a sub tree */
ds_tree = proto_item_add_subtree( ds_item, ett_data_seg );
@@ -875,36 +1230,57 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
case CI_DATA_SEG_SYMBOL:
/* ANSI extended symbol segment */
- ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Extended Symbol Segment (0x%02X)", segment_type );
+ if ( generate )
+ {
+ ds_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Extended Symbol Segment (0x%02X)", segment_type );
+ PROTO_ITEM_SET_GENERATED(ds_item);
+ }
+ else
+ ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Extended Symbol Segment (0x%02X)", segment_type );
/* Create a sub tree */
ds_tree = proto_item_add_subtree( ds_item, ett_data_seg );
/* Segment size */
seg_size = tvb_get_guint8( tvb, offset + pathpos+1 );
- proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d", seg_size );
+ if ( generate )
+ {
+ it = proto_tree_add_text( ds_tree, NULL, 0, 0, "Data Size: %d", seg_size );
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ else
+ proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d", seg_size );
/* Segment data */
if( seg_size != 0 )
{
- qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2, seg_size, "Data: %s",
- tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+ if ( generate )
+ {
+ qi = proto_tree_add_text( ds_tree, NULL, 0, 0, "Data: %s",
+ tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+ PROTO_ITEM_SET_GENERATED(qi);
+ }
+ else
+ qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2, seg_size, "Data: %s",
+ tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
proto_item_append_text(epath_item, "%s", tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+
hidden_item = proto_tree_add_item( ds_tree, hf_cip_symbol, tvb, offset + pathpos + 2, seg_size, FALSE );
PROTO_ITEM_SET_HIDDEN(hidden_item);
if( seg_size %2 )
{
/* We have a PAD BYTE */
- proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2 + seg_size, 1, "Pad Byte (0x%02X)",
- tvb_get_guint8( tvb, offset + pathpos + 2 + seg_size ) );
- pathpos++;
+ if ( !generate )
+ proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2 + seg_size, 1, "Pad Byte (0x%02X)",
+ tvb_get_guint8( tvb, offset + pathpos + 2 + seg_size ) );
seg_size++;
}
}
- proto_item_set_len( ds_item, 2 + seg_size );
+ if ( !generate )
+ proto_item_set_len( ds_item, 2 + seg_size );
pathpos = pathpos + 2 + seg_size;
break;
@@ -977,133 +1353,573 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
} /* end of dissect_epath() */
+/************************************************
+ *
+ * Dissector for generic CIP object
+ *
+ ************************************************/
static void
-dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+dissect_cip_generic_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo, proto_item *ti )
{
- proto_item *pi, *rrsc_item, *ncppi, *ar_item, *temp_item, *temp_item2, *status_item;
- proto_tree *temp_tree, *rrsc_tree, *ncp_tree, *cmd_data_tree, *status_tree;
- int req_path_size, conn_path_size, temp_data;
- unsigned char gen_status;
+ proto_item *pi, *temp_item;
+ proto_tree *cmd_data_tree;
+ int req_path_size;
unsigned char add_stat_size;
- unsigned char temp_byte, route_path_size;
- unsigned char app_rep_size, i, collision;
- int msg_req_siz, num_services, serv_offset;
+ unsigned char i;
- /* Add Service code & Request/Response tree */
- rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
- rrsc_tree = proto_item_add_subtree( rrsc_item, ett_rrsc );
-
- /* Add Request/Response */
- proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
- /* watch for service collisions with CCO */
- temp_byte = tvb_get_guint8( tvb, offset );
- collision = 0;
- if ( SC_STOP_CONN == temp_byte || SC_AUDIT_CHANGE == temp_byte )
+ if( tvb_get_guint8( tvb, offset ) & 0x80 )
{
- /* check for CCO object in path... */
- temp_data = tvb_get_guint8( tvb, offset+3 );
- /* F3 is the CCO */
- if ( temp_data == 0xF3 )
+ /* Response message */
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+
+ /* If there is any command specific data create a sub-tree for it */
+ if( ( item_length-4-add_stat_size ) != 0 )
{
- collision = 1;
- proto_item_append_text( rrsc_item, "%s (%s)",
- val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
- cip_sc_vals_cco , "Unknown Service (%x)"),
- val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
- cip_sc_rr, "") );
+ pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+ }
+ else
+ {
+ PROTO_ITEM_SET_HIDDEN( ti );
}
- }
- if (!collision)
+ } /* End of if reply */
+ else
{
- proto_item_append_text( rrsc_item, "%s (%s)",
+ /* Request message */
+
+ /* Add service to info column */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_str( pinfo->cinfo, COL_INFO,
val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
- cip_sc_vals , "Unknown Service (%x)"),
- val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
- cip_sc_rr, "") );
+ cip_sc_vals , "Unknown Service (%x)") );
+ }
+
+ req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+
+ /* If there is any command specific data creat a sub-tree for it */
+ if( (item_length-req_path_size-2) != 0 )
+ {
+
+ pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+
+ /* Check what service code that recived */
+
+ if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
+ {
+ /* Get attribute list request */
+
+ int att_count;
+
+ /* Add number of services */
+ att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
+
+ /* Add Attribute List */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
+
+ for( i=0; i < att_count; i++ )
+ {
+ if( i == (att_count-1) )
+ proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ else
+ proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ }
+
+ } /* End of Get attribute list request */
+ else
+ {
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+ } /* End of check service code */
+
+ }
+ else
+ {
+ PROTO_ITEM_SET_HIDDEN( ti );
+ } /* End of if command-specific data present */
+
+ } /* End of if-else( request ) */
+
+} /* End of dissect_cip_generic_data() */
+
+static int
+dissect_cip_class_generic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *class_tree;
+
+ if( tree )
+ {
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip_class_generic, tvb, 0, -1, FALSE);
+ class_tree = proto_item_add_subtree( ti, ett_cip_class_generic );
+
+ dissect_cip_generic_data( class_tree, tvb, 0, tvb_length(tvb), pinfo, ti );
}
+ return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Message Router
+ *
+ ************************************************/
+
+static void
+dissect_cip_mr_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+
+typedef struct mr_mult_req_info {
+ guint8 service;
+ int num_services;
+ cip_req_info_t *requests;
+} mr_mult_req_info_t;
+
+ proto_item *pi, *rrsc_item, *temp_item, *temp_item2;
+ proto_tree *temp_tree, *rrsc_tree, *cmd_data_tree;
+ int req_path_size;
+ int i;
+ unsigned char gen_status;
+ unsigned char add_stat_size;
+ int num_services, serv_offset;
+ unsigned char service;
+ mr_mult_req_info_t *mr_mult_req_info;
+ cip_req_info_t *mr_single_req_info;
+ cip_req_info_t *cip_req_info;
+
+ if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+ col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP MR" );
+
+ /* Add Service code & Request/Response tree */
+ rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+ rrsc_tree = proto_item_add_subtree( rrsc_item, ett_mr_rrsc );
+
+ /* Add Request/Response */
+ proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+ /* watch for service collisions */
+ service = tvb_get_guint8( tvb, offset );
+
+ proto_item_append_text( rrsc_item, "%s (%s)",
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals_mr , "Unknown Service (%x)"),
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+ cip_sc_rr, "") );
+
/* Add Service code */
proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
if( tvb_get_guint8( tvb, offset ) & 0x80 )
{
- /* Response message */
- status_item = proto_tree_add_text( item_tree, tvb, offset+2, 1, "Status: " );
- status_tree = proto_item_add_subtree( status_item, ett_status_item );
-
- /* Add general status */
gen_status = tvb_get_guint8( tvb, offset+2 );
- proto_tree_add_item(status_tree, hf_cip_genstat, tvb, offset+2, 1, TRUE );
- proto_item_append_text( status_item, "%s", val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
- cip_gs_vals , "Unknown Response (%x)") );
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
- /* Add reply status to info column */
+ /* If there is any command specific data create a sub-tree for it */
+ if( ( item_length-4-add_stat_size ) != 0 )
+ {
+ pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_mr_cmd_data );
+
+ if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
+ {
+ /* Success responses */
+
+ if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_MULT_SERV_PACK )
+ {
+ /* Multiple Service Reply (Success)*/
+
+ /* Add number of replies */
+ num_services = tvb_get_letohs( tvb, offset+4+add_stat_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Number of Replies: %d", num_services );
+
+ /* Add replies */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+add_stat_size+4, num_services*2, "Offsets: " );
+
+ cip_req_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
+ mr_mult_req_info = NULL;
+ if ( cip_req_info )
+ {
+ mr_mult_req_info = (mr_mult_req_info_t*)cip_req_info->pData;
+
+ if ( mr_mult_req_info
+ && ( mr_mult_req_info->service != SC_MULT_SERV_PACK
+ || mr_mult_req_info->num_services != num_services
+ )
+ )
+ mr_mult_req_info = NULL;
+ }
+
+ for( i=0; i < num_services; i++ )
+ {
+ int serv_length;
+ tvbuff_t *next_tvb;
+
+ serv_offset = tvb_get_letohs( tvb, offset+6+add_stat_size+(i*2) );
+
+ if( i == (num_services-1) )
+ {
+ /* Last service to add */
+ proto_item_append_text(temp_item, "%d", serv_offset );
+ serv_length = item_length-add_stat_size-serv_offset-4;
+ }
+ else
+ {
+ proto_item_append_text(temp_item, "%d, ", serv_offset );
+ serv_length = tvb_get_letohs( tvb, offset+6+add_stat_size+((i+1)*2) ) - serv_offset;
+ }
+
+ temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+4, serv_length, "Service Reply #%d", i+1 );
+ temp_tree = proto_item_add_subtree( temp_item2, ett_mr_mult_ser );
+
+ /*
+ ** We call our selves again to disect embedded packet
+ */
+
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_append_str( pinfo->cinfo, COL_INFO, ", ");
+
+ next_tvb = tvb_new_subset(tvb, offset+serv_offset+4, serv_length, serv_length);
+ if ( mr_mult_req_info )
+ {
+ mr_single_req_info = mr_mult_req_info->requests + i;
+ dissect_cip_data( temp_tree, next_tvb, 0, serv_length, pinfo, mr_single_req_info );
+ }
+ else
+ {
+ dissect_cip_data( temp_tree, next_tvb, 0, serv_length, pinfo, NULL );
+ }
+ }
+ } /* End if Multiple service Packet */
+ else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_GET_ATT_LIST )
+ {
+ /* Get Attribute List Reply (Success)*/
+
+ int att_count;
+
+ /* Add Attribute Count */
+ att_count = tvb_get_letohs( tvb, offset+4+add_stat_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Attribute Count: %d", att_count );
+
+ /* Add the data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, "Data: " );
+
+ } /* End if GetAttrList */
+ else
+ {
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+ } /* end of check service code */
+
+ }
+ else
+ {
+ /* Error responses */
+
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+
+ } /* end of if-else( CI_CRC_SUCCESS ) */
+
+ } /* End of if command-specific data present */
+
+ } /* End of if reply */
+ else
+ {
+ /* Request message */
+
+ /* Add service to info column */
if(check_col(pinfo->cinfo, COL_INFO))
{
col_append_str( pinfo->cinfo, COL_INFO,
- val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
- cip_gs_vals , "Unknown Response (%x)") );
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals_mr, "Unknown Service (%x)") );
}
- /* Add additional status size */
- proto_tree_add_text( status_tree, tvb, offset+3, 1, "Additional Status Size: %d (word)",
- tvb_get_guint8( tvb, offset+3 ) );
-
- add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+ /* Add path size to tree */
+ req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
- if( add_stat_size )
+ /* If there is any command specific data creat a sub-tree for it */
+ if( (item_length-req_path_size-2) != 0 )
{
- proto_item_append_text( status_item, ", Extended:" );
- /* Add additional status */
- pi = proto_tree_add_text( status_tree, tvb, offset+4, add_stat_size, "Additional Status:" );
+ pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_mr_cmd_data );
- for( i=0; i < add_stat_size/2; i ++ )
+ /* Check what service code that recived */
+
+ if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK )
{
- proto_item_append_text( pi, " %s", val_to_str( ( tvb_get_letohs( tvb, offset+4+(i*2) ) ),
- cip_es_vals , "Unknown Status (%x)") );
- proto_item_append_text( pi, " (0x%04X)", tvb_get_letohs( tvb, offset+4+(i*2) ) );
- proto_item_append_text( status_item, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
+ /* Multiple service packet */
+
+ /* Add number of services */
+ num_services = tvb_get_letohs( tvb, offset+2+req_path_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Number of Services: %d", num_services );
+
+ /* Add services */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, num_services*2, "Offsets: " );
+
+ cip_req_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
+
+ mr_mult_req_info = NULL;
+ if ( cip_req_info )
+ {
+ if ( cip_req_info->pData == NULL )
+ {
+ mr_mult_req_info = se_alloc(sizeof(mr_mult_req_info_t));
+ mr_mult_req_info->service = SC_MULT_SERV_PACK;
+ mr_mult_req_info->num_services = num_services;
+ mr_mult_req_info->requests = se_alloc(sizeof(cip_req_info_t)*num_services);
+ cip_req_info->pData = mr_mult_req_info;
+ }
+ else
+ {
+ mr_mult_req_info = (mr_mult_req_info_t*)cip_req_info->pData;
+ if ( mr_mult_req_info && mr_mult_req_info->num_services != num_services )
+ mr_mult_req_info = NULL;
+ }
+ }
+ for( i=0; i < num_services; i++ )
+ {
+ int serv_length;
+ tvbuff_t *next_tvb;
+
+ serv_offset = tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) );
+
+ if( i == (num_services-1) )
+ {
+ /* Last service to add */
+ serv_length = item_length-2-req_path_size-serv_offset;
+ proto_item_append_text(temp_item, "%d", serv_offset );
+ }
+ else
+ {
+ serv_length = tvb_get_letohs( tvb, offset+4+req_path_size+((i+1)*2) ) - serv_offset;
+ proto_item_append_text(temp_item, "%d, ", serv_offset );
+ }
+
+ temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+6, serv_length, "Service Packet #%d", i+1 );
+ temp_tree = proto_item_add_subtree( temp_item2, ett_mr_mult_ser );
+
+ /*
+ ** We call our selves again to disect embedded packet
+ */
+
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_append_str( pinfo->cinfo, COL_INFO, ", ");
+
+ next_tvb = tvb_new_subset(tvb, offset+serv_offset+6, serv_length, serv_length);
+
+ if ( mr_mult_req_info )
+ {
+ mr_single_req_info = mr_mult_req_info->requests + i;
+ mr_single_req_info->bService = 0;
+ mr_single_req_info->dissector = NULL;
+ mr_single_req_info->IOILen = 0;
+ mr_single_req_info->pIOI = NULL;
+ mr_single_req_info->pData = NULL;
+
+ dissect_cip_data( temp_tree, next_tvb, 0, serv_length, pinfo, mr_single_req_info );
+ }
+ else
+ {
+ dissect_cip_data( temp_tree, next_tvb, 0, serv_length, pinfo, NULL );
+ }
+ }
+ } /* End if Multiple service Packet */
+ else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
+ {
+ /* Get attribute list request */
+
+ int att_count;
+
+ /* Add number of services */
+ att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
+
+ /* Add Attribute List */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
+
+ for( i=0; i < att_count; i++ )
+ {
+ if( i == (att_count-1) )
+ proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ else
+ proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ }
+
+ } /* End of Get attribute list request */
+ else
+ {
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+ } /* End of check service code */
+
+ } /* End of if command-specific data present */
+
+ } /* End of if-else( request ) */
+
+} /* End of dissect_cip_mr() */
+
+static int
+dissect_cip_class_mr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *class_tree;
+
+ if( tree )
+ {
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip_class_mr, tvb, 0, -1, FALSE);
+ class_tree = proto_item_add_subtree( ti, ett_cip_class_mr );
+
+ dissect_cip_mr_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+ }
+
+ return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Connection Manager
+ *
+ ************************************************/
+
+static void
+dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+ proto_item *pi, *rrsc_item, *ncppi, *ar_item, *temp_item;
+ proto_tree *temp_tree, *rrsc_tree, *ncp_tree, *cmd_data_tree;
+ int req_path_size, conn_path_size, temp_data;
+ unsigned char gen_status;
+ unsigned char add_stat_size;
+ unsigned short add_status;
+ unsigned char temp_byte, route_path_size;
+ unsigned char app_rep_size, i;
+ int msg_req_siz;
+ unsigned char service;
+ cip_req_info_t *preq_info;
+ cip_req_info_t *pembedded_req_info;
+
+ /* Special handling for Unconnected send response. If successful, embedded service code is sent.
+ * If failed, it can be either an Unconnected send response or the embedded service code response. */
+ preq_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
+ if ( preq_info && ( tvb_get_guint8( tvb, offset ) & 0x80 )
+ && preq_info->bService == SC_CM_UNCON_SEND
+ )
+ {
+ gen_status = tvb_get_guint8( tvb, offset+2 );
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+ if ( add_stat_size == 2 )
+ add_status = tvb_get_letohs( tvb, offset + 4 );
+ else
+ add_status = 0;
+ if( gen_status == 0 /* success response ) */
+ || ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) != SC_CM_UNCON_SEND )
+ || !( ( gen_status == 0x01 && ( add_status == 0x0204 || add_status == 0x0311 || add_status == 0x0312 || add_status == 0x0315 ) )
+ || gen_status == 0x02
+ || gen_status == 0x04
+ )
+ )
+ {
+ pembedded_req_info = (cip_req_info_t*)preq_info->pData;
+
+ if ( pembedded_req_info )
+ {
+ tvbuff_t *next_tvb;
+ void *p_save_proto_data;
+
+ p_save_proto_data = p_get_proto_data( pinfo->fd, proto_cip );
+ p_remove_proto_data(pinfo->fd, proto_cip);
+ p_add_proto_data(pinfo->fd, proto_cip, pembedded_req_info );
+
+ rrsc_item = proto_tree_add_text( item_tree, NULL, 0, 0, "(Service: Unconnected Send (Response))" );
+ next_tvb = tvb_new_subset(tvb, offset, item_length, item_length);
+ if ( pembedded_req_info && pembedded_req_info->dissector )
+ call_dissector(pembedded_req_info->dissector, next_tvb, pinfo, item_tree );
+ else
+ call_dissector( cip_class_generic_handle, next_tvb, pinfo, item_tree );
+
+ p_remove_proto_data(pinfo->fd, proto_cip);
+ p_add_proto_data(pinfo->fd, proto_cip, p_save_proto_data);
+ return;
}
}
+ }
- proto_item_set_len( status_item, 2 + add_stat_size );
+ if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+ col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP CM" );
+
+ /* Add Service code & Request/Response tree */
+ rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+ rrsc_tree = proto_item_add_subtree( rrsc_item, ett_cm_rrsc );
+
+ /* Add Request/Response */
+ proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+ /* watch for service collisions */
+ service = tvb_get_guint8( tvb, offset );
+ proto_item_append_text( rrsc_item, "%s (%s)",
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals_cm , "Unknown Service (%x)"),
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+ cip_sc_rr, "") );
+
+ /* Add Service code */
+ proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+ if( tvb_get_guint8( tvb, offset ) & 0x80 )
+ {
+ /* Response message */
+ gen_status = tvb_get_guint8( tvb, offset+2 );
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
/* If there is any command specific data create a sub-tree for it */
if( ( item_length-4-add_stat_size ) != 0 )
{
- pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific data" );
- cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+ pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cm_cmd_data );
if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
{
- /* Success responses */
+ /* Success responses */
- if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_OPEN )
- {
+ if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_OPEN )
+ {
/* Forward open Response (Success) */
+ guint32 O2TConnID;
+ guint32 T2OConnID;
+ guint16 ConnSerialNumber;
+ guint32 DeviceSerialNumber;
+ guint16 VendorID;
/* Display originator to target connection ID */
- temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 4, "O->T Network Connection ID: 0x%08X", temp_data );
+ O2TConnID = tvb_get_letohl( tvb, offset+4+add_stat_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 4, "O->T Network Connection ID: 0x%08X", O2TConnID );
/* Display target to originator connection ID */
- temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "T->O Network Connection ID: 0x%08X", temp_data );
+ T2OConnID = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "T->O Network Connection ID: 0x%08X", T2OConnID );
/* Display connection serial number */
- temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size+8 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 2, "Connection Serial Number: 0x%04X", temp_data );
+ ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size+8 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 2, "Connection Serial Number: 0x%04X", ConnSerialNumber );
/* Display the originator vendor id */
+ VendorID = tvb_get_letohs( tvb, offset+4+add_stat_size+10 );
proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+10, 2, TRUE);
/* Display the originator serial number */
- temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+12 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+12, 4, "Originator Serial Number: 0x%08X", temp_data );
+ DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+12 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+12, 4, "Originator Serial Number: 0x%08X", DeviceSerialNumber );
/* Display originator to target actual packet interval */
temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+16 );
@@ -1132,23 +1948,29 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
proto_item_append_text(ar_item, " 0x%02X", temp_byte );
}
- } /* End of if reply data */
+ } /* End of if reply data */
+
+ enip_open_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber, O2TConnID, T2OConnID );
} /* End of if forward open response */
- else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_CLOSE )
+ else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_CLOSE )
{
/* Forward close response (Success) */
+ guint16 ConnSerialNumber;
+ guint32 DeviceSerialNumber;
+ guint16 VendorID;
/* Display connection serial number */
- temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", temp_data );
+ ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", ConnSerialNumber );
/* Display the originator vendor id */
+ VendorID = tvb_get_letohs( tvb, offset+4+add_stat_size+2 );
proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE);
/* Display the originator serial number */
- temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", temp_data );
+ DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", DeviceSerialNumber );
/* Display the application reply size */
app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+8 ) * 2;
@@ -1165,62 +1987,22 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
for( i=0; i < app_rep_size; i ++ )
{
- temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+10+i );
- proto_item_append_text(ar_item, " 0x%02X", temp_byte );
+ temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+10+i );
+ proto_item_append_text(ar_item, " 0x%02X", temp_byte );
}
- } /* End of if reply data */
+ } /* End of if reply data */
+
+ enip_close_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber );
} /* End of if forward close response */
- else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_UNCON_SEND )
+ else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_UNCON_SEND )
{
/* Unconnected send response (Success) */
/* Display service response data */
- proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
}
- else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_MULT_SERV_PACK )
- {
- /* Multiple Service Reply (Success)*/
-
- /* Add number of replies */
- num_services = tvb_get_letohs( tvb, offset+4+add_stat_size );
- proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Number of Replies: %d", num_services );
-
- /* Add replies */
- temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+add_stat_size+4, num_services*2, "Offsets: " );
-
- for( i=0; i < num_services; i++ )
- {
- int serv_length;
-
- serv_offset = tvb_get_letohs( tvb, offset+6+add_stat_size+(i*2) );
-
- if( i == (num_services-1) )
- {
- /* Last service to add */
- proto_item_append_text(temp_item, "%d", serv_offset );
- serv_length = item_length-add_stat_size-serv_offset-4;
- }
- else
- {
- proto_item_append_text(temp_item, "%d, ", serv_offset );
- serv_length = tvb_get_letohs( tvb, offset+6+add_stat_size+((i+1)*2) ) - serv_offset;
- }
-
- temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+4, serv_length, "Service Reply #%d", i+1 );
- temp_tree = proto_item_add_subtree( temp_item2, ett_mult_ser );
-
- /*
- ** We call our selves again to disect embedded packet
- */
-
- if(check_col(pinfo->cinfo, COL_INFO))
- col_append_str( pinfo->cinfo, COL_INFO, ", ");
-
- dissect_cip_data( temp_tree, tvb, offset+serv_offset+4, serv_length, pinfo );
- }
- } /* End if Multiple service Packet */
else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_GET_ATT_LIST )
{
/* Get Attribute List Reply (Success)*/
@@ -1232,12 +2014,13 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Attribute Count: %d", att_count );
/* Add the data */
- proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, FALSE);
- } /* End if Multiple service Packet */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, "Data: " );
+
+ } /* Get Attribute List Reply */
else
{
/* Add data */
- proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
} /* end of check service code */
}
@@ -1245,8 +2028,8 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
{
/* Error responses */
- if( ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_OPEN ) ||
- ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_CLOSE ) )
+ if( ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_OPEN ) ||
+ ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_CLOSE ) )
{
/* Forward open and forward close error response look the same */
@@ -1269,7 +2052,7 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+9 );
proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+9, 1, "Reserved: 0x%02X", temp_data );
}
- else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_UNCON_SEND )
+ else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_UNCON_SEND )
{
/* Unconnected send response (Unsuccess) */
@@ -1280,7 +2063,7 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
else
{
/* Add data */
- proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
}
} /* end of if-else( CI_CRC_SUCCESS ) */
@@ -1297,27 +2080,20 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
{
col_append_str( pinfo->cinfo, COL_INFO,
val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
- cip_sc_vals , "Unknown Service (%x)") );
+ cip_sc_vals_cm , "Unknown Service (%x)") );
}
-
- /* Add path size to tree */
req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
- proto_tree_add_text( item_tree, tvb, offset+1, 1, "Request Path Size: %d (words)", req_path_size/2 );
-
- /* Add the epath */
- pi = proto_tree_add_text(item_tree, tvb, offset+2, req_path_size, "Request Path: ");
- dissect_epath( tvb, pi, offset+2, req_path_size );
/* If there is any command specific data creat a sub-tree for it */
if( (item_length-req_path_size-2) != 0 )
{
pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
- cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cm_cmd_data );
/* Check what service code that recived */
- if( tvb_get_guint8( tvb, offset ) == SC_FWD_OPEN )
+ if( tvb_get_guint8( tvb, offset ) == SC_CM_FWD_OPEN )
{
/* Forward open Request*/
@@ -1366,19 +2142,19 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
/* Display originator to target network connection patameterts, in a tree */
temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+26 );
ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+26, 2, "O->T Network Connection Parameters: 0x%04X", temp_data );
- ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+ ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
/* Add the data to the tree */
- proto_tree_add_item(ncp_tree, hf_cip_fwo_own,
- tvb, offset+2+req_path_size+26, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_typ,
- tvb, offset+2+req_path_size+26, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_prio,
- tvb, offset+2+req_path_size+26, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_fixed_var,
- tvb, offset+2+req_path_size+26, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_con_size,
- tvb, offset+2+req_path_size+26, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own,
+ tvb, offset+2+req_path_size+26, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ,
+ tvb, offset+2+req_path_size+26, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio,
+ tvb, offset+2+req_path_size+26, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var,
+ tvb, offset+2+req_path_size+26, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size,
+ tvb, offset+2+req_path_size+26, 2, TRUE );
/* Display target to originator requested packet interval */
temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+28 );
@@ -1387,35 +2163,35 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
/* Display target to originator network connection patameterts, in a tree */
temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+32 );
ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+32, 2, "T->O Network Connection Parameters: 0x%04X", temp_data );
- ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+ ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
/* Add the data to the tree */
- proto_tree_add_item(ncp_tree, hf_cip_fwo_own,
- tvb, offset+2+req_path_size+32, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_typ,
- tvb, offset+2+req_path_size+32, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_prio,
- tvb, offset+2+req_path_size+32, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_fixed_var,
- tvb, offset+2+req_path_size+32, 2, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_con_size,
- tvb, offset+2+req_path_size+32, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own,
+ tvb, offset+2+req_path_size+32, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ,
+ tvb, offset+2+req_path_size+32, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio,
+ tvb, offset+2+req_path_size+32, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var,
+ tvb, offset+2+req_path_size+32, 2, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size,
+ tvb, offset+2+req_path_size+32, 2, TRUE );
/* Transport type/trigger in tree*/
temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+34 );
ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+34, 1, "Transport Type/Trigger: 0x%02X", temp_data );
- ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+ ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
/* Add the data to the tree */
- proto_tree_add_item(ncp_tree, hf_cip_fwo_dir,
- tvb, offset+2+req_path_size+34, 1, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_dir,
+ tvb, offset+2+req_path_size+34, 1, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_trigg,
- tvb, offset+2+req_path_size+34, 1, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_trigg,
+ tvb, offset+2+req_path_size+34, 1, TRUE );
- proto_tree_add_item(ncp_tree, hf_cip_fwo_class,
- tvb, offset+2+req_path_size+34, 1, TRUE );
+ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_class,
+ tvb, offset+2+req_path_size+34, 1, TRUE );
/* Add path size */
conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+35 )*2;
@@ -1423,9 +2199,9 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
/* Add the epath */
pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+36, conn_path_size, "Connection Path: ");
- dissect_epath( tvb, pi, offset+2+req_path_size+36, conn_path_size );
+ dissect_epath( tvb, pi, offset+2+req_path_size+36, conn_path_size, FALSE );
}
- else if( tvb_get_guint8( tvb, offset ) == SC_FWD_CLOSE && ! collision)
+ else if( tvb_get_guint8( tvb, offset ) == SC_CM_FWD_CLOSE )
{
/* Forward Close Request */
@@ -1462,123 +2238,83 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
/* Add the EPATH */
pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+12, conn_path_size, "Connection Path: ");
- dissect_epath( tvb, pi, offset+2+req_path_size+12, conn_path_size );
+ dissect_epath( tvb, pi, offset+2+req_path_size+12, conn_path_size, FALSE );
} /* End of forward close */
- else if( tvb_get_guint8( tvb, offset ) == SC_UNCON_SEND )
+ else if( tvb_get_guint8( tvb, offset ) == SC_CM_UNCON_SEND )
{
- /* check for collision */
- if ( collision )
- {
- /* Audit Change */
-
- temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
- temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
-
- if (temp_data == 0)
- proto_item_append_text(temp_item, "Full" );
- else if (temp_data == 1)
- proto_item_append_text(temp_item, "Incremental" );
- else
- proto_item_append_text(temp_item, "Reserved" );
- }
- else
- {
- /* Unconnected send */
-
- /* Display the priority/tick timer */
- temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
-
- /* Display the time-out ticks */
- temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
-
- /* Display the actual time out */
- temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
-
- /* Message request size */
- msg_req_siz = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 2, "Message Request Size: 0x%04X", msg_req_siz );
-
- /* Message Request */
- temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4, msg_req_siz, "Message Request" );
- temp_tree = proto_item_add_subtree(temp_item, ett_mes_req );
-
- /*
- ** We call our selves again to disect embedded packet
- */
-
- if(check_col(pinfo->cinfo, COL_INFO))
- col_append_str( pinfo->cinfo, COL_INFO, ": ");
-
- dissect_cip_data( temp_tree, tvb, offset+2+req_path_size+4, msg_req_siz, pinfo );
-
- if( msg_req_siz % 2 )
- {
- /* Pad byte */
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Pad Byte (0x%02X)",
- tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz ) );
- msg_req_siz++; /* include the padding */
- }
-
- /* Route Path Size */
- route_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz )*2;
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Route Path Size: %d (words)", route_path_size/2 );
-
- /* Reserved */
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+5+msg_req_siz, 1, "Reserved (0x%02X)",
- tvb_get_guint8( tvb, offset+2+req_path_size+5+msg_req_siz ) );
-
- /* Route Path */
- temp_item = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+6+msg_req_siz, route_path_size, "Route Path: ");
- dissect_epath( tvb, temp_item, offset+2+req_path_size+6+msg_req_siz, route_path_size );
- }
+ /* Unconnected send */
+ tvbuff_t *next_tvb;
- } /* End if unconnected send */
- else if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK )
- {
- /* Multiple service packet */
+ /* Display the priority/tick timer */
+ temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
- /* Add number of services */
- num_services = tvb_get_letohs( tvb, offset+2+req_path_size );
- proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Number of Services: %d", num_services );
+ /* Display the time-out ticks */
+ temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
- /* Add services */
- temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, num_services*2, "Offsets: " );
+ /* Display the actual time out */
+ temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
- for( i=0; i < num_services; i++ )
- {
- int serv_length;
+ /* Message request size */
+ msg_req_siz = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 2, "Message Request Size: 0x%04X", msg_req_siz );
- serv_offset = tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) );
+ /* Message Request */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4, msg_req_siz, "Message Request" );
+ temp_tree = proto_item_add_subtree(temp_item, ett_cm_mes_req );
- if( i == (num_services-1) )
+ /*
+ ** We call our selves again to disect embedded packet
+ */
+
+ if(check_col(pinfo->cinfo, COL_INFO))
+ col_append_str( pinfo->cinfo, COL_INFO, ": ");
+
+ next_tvb = tvb_new_subset(tvb, offset+2+req_path_size+4, msg_req_siz, msg_req_siz);
+ preq_info = p_get_proto_data( pinfo->fd, proto_cip );
+ pembedded_req_info = NULL;
+ if ( preq_info )
+ {
+ if ( preq_info->pData == NULL )
{
- /* Last service to add */
- serv_length = item_length-2-req_path_size-serv_offset;
- proto_item_append_text(temp_item, "%d", serv_offset );
+ pembedded_req_info = (cip_req_info_t*)se_alloc(sizeof(cip_req_info_t));
+ pembedded_req_info->bService = 0;
+ pembedded_req_info->dissector = NULL;
+ pembedded_req_info->IOILen = 0;
+ pembedded_req_info->pIOI = NULL;
+ pembedded_req_info->pData = NULL;
+ preq_info->pData = pembedded_req_info;
}
else
{
- serv_length = tvb_get_letohs( tvb, offset+4+req_path_size+((i+1)*2) ) - serv_offset;
- proto_item_append_text(temp_item, "%d, ", serv_offset );
+ pembedded_req_info = (cip_req_info_t*)preq_info->pData;
}
+ }
+ dissect_cip_data( temp_tree, next_tvb, 0, msg_req_siz, pinfo, pembedded_req_info );
- temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+6, serv_length, "Service Packet #%d", i+1 );
- temp_tree = proto_item_add_subtree( temp_item2, ett_mult_ser );
+ if( msg_req_siz % 2 )
+ {
+ /* Pad byte */
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Pad Byte (0x%02X)",
+ tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz ) );
+ msg_req_siz++; /* include the padding */
+ }
- /*
- ** We call our selves again to disect embedded packet
- */
+ /* Route Path Size */
+ route_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz )*2;
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Route Path Size: %d (words)", route_path_size/2 );
- if(check_col(pinfo->cinfo, COL_INFO))
- col_append_str( pinfo->cinfo, COL_INFO, ", ");
+ /* Reserved */
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+5+msg_req_siz, 1, "Reserved (0x%02X)",
+ tvb_get_guint8( tvb, offset+2+req_path_size+5+msg_req_siz ) );
- dissect_cip_data( temp_tree, tvb, offset+serv_offset+6, serv_length, pinfo );
- }
- } /* End if Multiple service Packet */
+ /* Route Path */
+ temp_item = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+6+msg_req_siz, route_path_size, "Route Path: ");
+ dissect_epath( tvb, temp_item, offset+2+req_path_size+6+msg_req_siz, route_path_size, FALSE );
+ } /* End if unconnected send */
else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
{
/* Get attribute list request */
@@ -1601,39 +2337,431 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
}
} /* End of Get attribute list request */
- else if ( tvb_get_guint8( tvb, offset ) == SC_CHANGE_COMPLETE )
- {
- /* Change complete request */
+ else
+ {
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+ } /* End of check service code */
+
+ } /* End of if command-specific data present */
+
+ } /* End of if-else( request ) */
+
+} /* End of dissect_cip_cm_data() */
+
+static int
+dissect_cip_class_cm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *class_tree;
+
+ if( tree )
+ {
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip_class_cm, tvb, 0, -1, FALSE);
+ class_tree = proto_item_add_subtree( ti, ett_cip_class_cm );
+
+ dissect_cip_cm_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+ }
+
+ return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Connection Configuration Object
+ *
+ ************************************************/
+
+static void
+dissect_cip_cco_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+ proto_item *pi, *rrsc_item, *temp_item;
+ proto_tree *rrsc_tree, *cmd_data_tree;
+ int req_path_size, temp_data;
+ unsigned char gen_status;
+ unsigned char add_stat_size;
+ unsigned char i;
+
+ if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+ col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP CCO" );
+
+ /* Add Service code & Request/Response tree */
+ rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+ rrsc_tree = proto_item_add_subtree( rrsc_item, ett_cco_rrsc );
+
+ /* Add Request/Response */
+ proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+ proto_item_append_text( rrsc_item, "%s (%s)",
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals_cco , "Unknown Service (%x)"),
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+ cip_sc_rr, "") );
+
+ /* Add Service code */
+ proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+ if( tvb_get_guint8( tvb, offset ) & 0x80 )
+ {
+ /* Response message */
+
+ /* Add additional status size */
+ gen_status = tvb_get_guint8( tvb, offset+2 );
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+
+ /* If there is any command specific data create a sub-tree for it */
+ if( ( item_length-4-add_stat_size ) != 0 )
+ {
+ pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cco_cmd_data );
+
+ if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
+ {
+ /* Success responses */
+
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+ }
+ else
+ {
+ /* Error responses */
+
+ /* Add data */
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+ } /* end of if-else( CI_CRC_SUCCESS ) */
+
+ } /* End of if command-specific data present */
+
+ } /* End of if reply */
+ else
+ {
+ /* Request message */
+
+ /* Add service to info column */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_str( pinfo->cinfo, COL_INFO,
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals_cco , "Unknown Service (%x)") );
+ }
+ req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+
+ /* If there is any command specific data creat a sub-tree for it */
+ if( (item_length-req_path_size-2) != 0 )
+ {
+
+ pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+ cmd_data_tree = proto_item_add_subtree( pi, ett_cco_cmd_data );
+
+ /* Check what service code that recived */
+
+ if( tvb_get_guint8( tvb, offset ) == SC_CCO_AUDIT_CHANGE )
+ {
+ /* Audit Change */
- temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
- temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
+ temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
- if (temp_data == 0)
+ if (temp_data == 0)
proto_item_append_text(temp_item, "Full" );
- else if (temp_data == 1)
+ else if (temp_data == 1)
proto_item_append_text(temp_item, "Incremental" );
- else
+ else
proto_item_append_text(temp_item, "Reserved" );
- }
+ }
+ else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
+ {
+ /* Get attribute list request */
+
+ int att_count;
+
+ /* Add number of services */
+ att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+ proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
+
+ /* Add Attribute List */
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
+
+ for( i=0; i < att_count; i++ )
+ {
+ if( i == (att_count-1) )
+ proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ else
+ proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+ }
+
+ } /* End of Get attribute list request */
+ else if ( tvb_get_guint8( tvb, offset ) == SC_CCO_CHANGE_COMPLETE )
+ {
+ /* Change complete request */
+
+ temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
+ temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
+
+ if (temp_data == 0)
+ proto_item_append_text(temp_item, "Full" );
+ else if (temp_data == 1)
+ proto_item_append_text(temp_item, "Incremental" );
+ else
+ proto_item_append_text(temp_item, "Reserved" );
+ }
else
{
/* Add data */
- proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, FALSE);
-
+ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
} /* End of check service code */
} /* End of if command-specific data present */
} /* End of if-else( request ) */
+} /* End of dissect_cip_cco_data() */
+
+static int
+dissect_cip_class_cco(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ proto_item *ti;
+ proto_tree *class_tree;
+
+ if( tree )
+ {
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip_class_cco, tvb, 0, -1, FALSE);
+ class_tree = proto_item_add_subtree( ti, ett_cip_class_cco );
+
+ dissect_cip_cco_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+ }
+
+ return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Request/Response
+ * - matches requests/responses
+ * - calls class specific dissector
+ *
+ ************************************************/
+
+static void
+dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length _U_, packet_info *pinfo, cip_req_info_t* preq_info )
+{
+ proto_item *ti;
+ proto_tree *cip_tree;
+ proto_item *pi, *rrsc_item, *status_item;
+ proto_tree *rrsc_tree, *status_tree;
+ int req_path_size;
+ unsigned char gen_status;
+ unsigned char add_stat_size;
+ unsigned char i;
+ guint32 classid;
+ unsigned char service,ioilen,segment;
+ void *p_save_proto_data;
+ dissector_handle_t dissector;
+
+ p_save_proto_data = p_get_proto_data(pinfo->fd, proto_cip);
+ p_remove_proto_data(pinfo->fd, proto_cip);
+ p_add_proto_data(pinfo->fd, proto_cip, preq_info);
+
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(item_tree, proto_cip, tvb, 0, -1, FALSE);
+ cip_tree = proto_item_add_subtree( ti, ett_cip );
+
+ /* Add Service code & Request/Response tree */
+ rrsc_item = proto_tree_add_text( cip_tree, tvb, offset, 1, "Service: " );
+ rrsc_tree = proto_item_add_subtree( rrsc_item, ett_rrsc );
+
+ /* Add Request/Response */
+ proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+ /* watch for service collisions */
+ service = tvb_get_guint8( tvb, offset );
+ proto_item_append_text( rrsc_item, "%s (%s)",
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+ cip_sc_vals , "Unknown Service (%x)"),
+ val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+ cip_sc_rr, "") );
+
+ /* Add Service code */
+ proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+ if( service & 0x80 )
+ {
+ /* Response message */
+ status_item = proto_tree_add_text( cip_tree, tvb, offset+2, 1, "Status: " );
+ status_tree = proto_item_add_subtree( status_item, ett_status_item );
+
+ /* Add general status */
+ gen_status = tvb_get_guint8( tvb, offset+2 );
+ proto_tree_add_item(status_tree, hf_cip_genstat, tvb, offset+2, 1, TRUE );
+ proto_item_append_text( status_item, "%s", val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
+ cip_gs_vals , "Unknown Response (%x)") );
+
+ /* Add reply status to info column */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_str( pinfo->cinfo, COL_INFO,
+ val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
+ cip_gs_vals , "Unknown Response (%x)") );
+ }
+
+ /* Add additional status size */
+ proto_tree_add_text( status_tree, tvb, offset+3, 1, "Additional Status Size: %d (word)",
+ tvb_get_guint8( tvb, offset+3 ) );
+
+ add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+
+ if( add_stat_size )
+ {
+ proto_item_append_text( status_item, ", Extended:" );
+
+ /* Add additional status */
+ pi = proto_tree_add_text( status_tree, tvb, offset+4, add_stat_size, "Additional Status:" );
+
+ for( i=0; i < add_stat_size/2; i ++ )
+ {
+ proto_item_append_text( pi, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
+ proto_item_append_text( status_item, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
+ }
+ }
+
+ proto_item_set_len( status_item, 2 + add_stat_size );
+
+
+ if( preq_info
+ && !( preq_info->bService == ( service & 0x7F )
+ || ( preq_info->bService == SC_CM_UNCON_SEND && preq_info->dissector == cip_class_cm_handle )
+ )
+ )
+ preq_info = NULL;
+
+ if ( preq_info )
+ {
+ if ( preq_info->IOILen && preq_info->pIOI )
+ {
+ tvbuff_t* tvbIOI;
+
+ tvbIOI = tvb_new_real_data( preq_info->pIOI, preq_info->IOILen * 2, preq_info->IOILen * 2);
+ if ( tvbIOI )
+ {
+ /* pi = add_byte_array_text_to_proto_tree( cip_tree, tvbIOI, 0, req_path_size+1, "IOI: " );
+ PROTO_ITEM_SET_GENERATED(pi); */
+
+ pi = proto_tree_add_text( cip_tree, NULL, 0, 0, "Request Path Size: %d (words)", preq_info->IOILen );
+ PROTO_ITEM_SET_GENERATED(pi);
+
+ /* Add the epath */
+ pi = proto_tree_add_text(cip_tree, NULL, 0, 0, "Request Path: ");
+ PROTO_ITEM_SET_GENERATED(pi);
+ dissect_epath( tvbIOI, pi, 0, preq_info->IOILen, TRUE );
+ tvb_free(tvbIOI);
+ }
+ }
+ }
+
+ if ( preq_info && preq_info->dissector )
+ {
+ call_dissector( preq_info->dissector, tvb, pinfo, item_tree );
+ }
+ else
+ {
+ call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
+ }
+ } /* End of if reply */
+ else
+ {
+ /* Request message */
+
+ /* Add path size to tree */
+ req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+ proto_tree_add_text( cip_tree, tvb, offset+1, 1, "Request Path Size: %d (words)", req_path_size/2 );
+
+ /* Add the epath */
+ pi = proto_tree_add_text(cip_tree, tvb, offset+2, req_path_size, "Request Path: ");
+ dissect_epath( tvb, pi, offset+2, req_path_size, FALSE );
+
+ /* parse IOI; extract class ID */
+ ioilen = tvb_get_guint8( tvb, offset + 1 );
+ if ( preq_info )
+ preq_info->dissector = NULL;
+ dissector = NULL;
+ if ( ioilen >= 1 )
+ {
+ segment = tvb_get_guint8( tvb, offset + 2 );
+ switch ( segment & CI_SEGMENT_TYPE_MASK )
+ {
+ case CI_LOGICAL_SEGMENT:
+ /* Logical segment, determin the logical type */
+ switch( segment & CI_LOGICAL_SEG_TYPE_MASK )
+ {
+ case CI_LOGICAL_SEG_CLASS_ID:
+
+ /* Logical Class ID, do a format check */
+ classid = 0;
+ switch ( segment & CI_LOGICAL_SEG_FORMAT_MASK )
+ {
+ case CI_LOGICAL_SEG_8_BIT:
+ classid = tvb_get_guint8( tvb, offset + 3 );
+ break;
+ case CI_LOGICAL_SEG_16_BIT:
+ if ( ioilen >= 2 )
+ classid = tvb_get_letohs( tvb, offset + 4 );
+ break;
+ case CI_LOGICAL_SEG_32_BIT:
+ if ( ioilen >= 3 )
+ classid = tvb_get_letohl( tvb, offset + 4 );
+ break;
+ }
+ dissector = dissector_get_port_handle( subdissector_class_table, classid );
+ if ( preq_info )
+ preq_info->dissector = dissector;
+ break;
+ }
+ break;
+
+ case CI_DATA_SEGMENT:
+ dissector = dissector_get_port_handle( subdissector_symbol_table, segment );
+ if ( preq_info )
+ preq_info->dissector = dissector;
+ break;
+ }
+ if ( preq_info )
+ {
+ /* copy IOI for access by response packet */
+ preq_info->pIOI = se_alloc( ioilen*2);
+ if ( preq_info->pIOI )
+ {
+ preq_info->IOILen = ioilen;
+ tvb_memcpy(tvb, preq_info->pIOI, offset+2, ioilen*2);
+ }
+ }
+ }
+
+ if( preq_info )
+ preq_info->bService = service;
+
+ if ( dissector )
+ {
+ call_dissector( dissector, tvb, pinfo, item_tree );
+ }
+ else
+ {
+ call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
+ }
+ } /* End of if-else( request ) */
+
+ p_remove_proto_data(pinfo->fd, proto_cip);
+ p_add_proto_data(pinfo->fd, proto_cip, p_save_proto_data);
+
} /* End of dissect_cip_data() */
static int
dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- proto_item *ti;
- proto_tree *cip_tree = NULL;
+ enip_request_info_t *enip_info;
+ cip_req_info_t *preq_info;
/* Make entries in Protocol column and Info column on summary display */
if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
@@ -1642,18 +2770,44 @@ dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
if (check_col( pinfo->cinfo, COL_INFO ) )
col_clear( pinfo->cinfo, COL_INFO );
- if( tree )
+ /* Each CIP request received by ENIP gets a unique ID */
+ enip_info = (enip_request_info_t*)p_get_proto_data(pinfo->fd, proto_enip);
+
+ if ( enip_info )
{
- /* Create display subtree for the protocol */
- ti = proto_tree_add_item(tree, proto_cip, tvb, 0, -1, FALSE);
- cip_tree = proto_item_add_subtree( ti, ett_cip );
+ preq_info = (cip_req_info_t*)enip_info->cip_info;
+ if ( preq_info == NULL )
+ {
+ preq_info = se_alloc( sizeof( cip_req_info_t ) );
+ if ( preq_info )
+ {
+ preq_info->bService = 0;
+ preq_info->dissector = NULL;
+ preq_info->IOILen = 0;
+ preq_info->pIOI = NULL;
+ preq_info->pData = NULL;
+ enip_info->cip_info = preq_info;
+ }
+ }
+ dissect_cip_data( tree, tvb, 0, tvb_length(tvb), pinfo, enip_info->cip_info );
+ }
+ else
+ {
+ dissect_cip_data( tree, tvb, 0, tvb_length(tvb), pinfo, NULL );
}
-
- dissect_cip_data( cip_tree, tvb, 0, tvb_length(tvb), pinfo );
return tvb_length(tvb);
}
+/*
+ * Protocol initialization
+ */
+
+static void
+cip_init_protocol(void)
+{
+ proto_enip = proto_get_id_by_filter_name( "enip" );
+}
void
proto_register_cip(void)
@@ -1661,215 +2815,328 @@ proto_register_cip(void)
/* Setup list of header fields */
static hf_register_info hf[] = {
- { &hf_cip_rr,
- { "Request/Response", "cip.rr",
- FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
- "Request or Response message", HFILL }
- },
- { &hf_cip_sc,
- { "Service", "cip.sc",
- FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
- "Service Code", HFILL }
- },
- { &hf_cip_epath,
- { "EPath", "cip.epath",
- FT_BYTES, BASE_NONE, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_genstat,
- { "General Status", "cip.genstat",
- FT_UINT8, BASE_HEX, VALS(cip_gs_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_port,
- { "Port", "cip.port",
- FT_UINT8, BASE_DEC, NULL, 0,
- "Port Identifier", HFILL }
- },
- { &hf_cip_link_address_byte,
- { "Link Address", "cip.linkaddress",
- FT_UINT8, BASE_DEC, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_link_address_string,
- { "Link Address", "cip.linkaddress",
- FT_STRING, BASE_NONE, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_class8,
- { "Class", "cip.class",
- FT_UINT8, BASE_HEX, VALS(cip_class_names_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_class16,
- { "Class", "cip.class",
- FT_UINT16, BASE_HEX, VALS(cip_class_names_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_class32,
- { "Class", "cip.class",
- FT_UINT32, BASE_HEX, VALS(cip_class_names_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_instance8,
- { "Instance", "cip.instance",
- FT_UINT8, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_instance16,
- { "Instance", "cip.instance",
- FT_UINT16, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_instance32,
- { "Instance", "cip.instance",
- FT_UINT32, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_attribute8,
- { "Attribute", "cip.attribute",
- FT_UINT8, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_attribute16,
- { "Attribute", "cip.attribute",
- FT_UINT16, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_attribute32,
- { "Attribute", "cip.attribute",
- FT_UINT32, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_conpoint8,
- { "Connection Point", "cip.connpoint",
- FT_UINT8, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_conpoint16,
- { "Connection Point", "cip.connpoint",
- FT_UINT16, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_conpoint32,
- { "Connection Point", "cip.connpoint",
- FT_UINT16, BASE_HEX, NULL, 0,
- NULL, HFILL }
- },
- { &hf_cip_symbol,
- { "Symbol", "cip.symbol",
- FT_STRING, BASE_NONE, NULL, 0,
- "ANSI Extended Symbol Segment", HFILL }
- },
- { &hf_cip_vendor,
- { "Vendor ID", "cip.vendor",
- FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_devtype,
- { "Device Type", "cip.devtype",
- FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
- NULL, HFILL }
- },
- { &hf_cip_fwo_comp,
- { "Compatibility", "cip.fwo.cmp",
- FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
- "Fwd Open: Compatibility bit", HFILL }
- },
- { &hf_cip_fwo_mrev,
- { "Major Revision", "cip.fwo.major",
- FT_UINT8, BASE_DEC, NULL, 0x7F,
- "Fwd Open: Major Revision", HFILL }
- },
- { &hf_cip_fwo_con_size,
- { "Connection Size", "cip.fwo.consize",
- FT_UINT16, BASE_DEC, NULL, 0x01FF,
- "Fwd Open: Connection size", HFILL }
- },
- { &hf_cip_fwo_fixed_var,
- { "Connection Size Type", "cip.fwo.f_v",
- FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200,
- "Fwd Open: Fixed or variable connection size", HFILL }
- },
- { &hf_cip_fwo_prio,
- { "Priority", "cip.fwo.prio",
- FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00,
- "Fwd Open: Connection priority", HFILL }
- },
- { &hf_cip_fwo_typ,
- { "Connection Type", "cip.fwo.type",
- FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000,
- "Fwd Open: Connection type", HFILL }
- },
- { &hf_cip_fwo_own,
- { "Owner", "cip.fwo.owner",
- FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000,
- "Fwd Open: Redundant owner bit", HFILL }
- },
- { &hf_cip_fwo_dir,
- { "Direction", "cip.fwo.dir",
- FT_UINT8, BASE_DEC, VALS(cip_con_dir_vals), 0x80,
- "Fwd Open: Direction", HFILL }
- },
- { &hf_cip_fwo_trigg,
- { "Trigger", "cip.fwo.trigger",
- FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), 0x70,
- "Fwd Open: Production trigger", HFILL }
- },
- { &hf_cip_fwo_class,
- { "Class", "cip.fwo.transport",
- FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), 0x0F,
- "Fwd Open: Transport Class", HFILL }
- },
- { &hf_cip_data,
- { "Data", "cip.data",
- FT_BYTES, BASE_NONE, NULL, 0,
- NULL, HFILL }
- }
+ { &hf_cip_rr,
+ { "Request/Response", "cip.rr",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+ "Request or Response message", HFILL }
+ },
+ { &hf_cip_sc,
+ { "Service", "cip.sc",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+ "Service Code", HFILL }
+ },
+ { &hf_cip_epath,
+ { "EPath", "cip.epath",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ "EPath", HFILL }
+ },
+ { &hf_cip_genstat,
+ { "General Status", "cip.genstat",
+ FT_UINT8, BASE_HEX, VALS(cip_gs_vals), 0,
+ "General Status", HFILL }
+ },
+ { &hf_cip_port,
+ { "Port", "cip.port",
+ FT_UINT8, BASE_DEC, NULL, 0,
+ "Port Identifier", HFILL }
+ },
+ { &hf_cip_link_address_byte,
+ { "Link Address", "cip.linkaddress",
+ FT_UINT8, BASE_DEC, NULL, 0,
+ "Link Address", HFILL }
+ },
+ { &hf_cip_link_address_string,
+ { "Link Address", "cip.linkaddress",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "Link Address", HFILL }
+ },
+ { &hf_cip_class8,
+ { "Class", "cip.class",
+ FT_UINT8, BASE_HEX, VALS(cip_class_names_vals), 0,
+ "Class", HFILL }
+ },
+ { &hf_cip_class16,
+ { "Class", "cip.class",
+ FT_UINT16, BASE_HEX, VALS(cip_class_names_vals), 0,
+ "Class", HFILL }
+ },
+ { &hf_cip_class32,
+ { "Class", "cip.class",
+ FT_UINT32, BASE_HEX, VALS(cip_class_names_vals), 0,
+ "Class", HFILL }
+ },
+ { &hf_cip_instance8,
+ { "Instance", "cip.instance",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "Instance", HFILL }
+ },
+ { &hf_cip_instance16,
+ { "Instance", "cip.instance",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "Instance", HFILL }
+ },
+ { &hf_cip_instance32,
+ { "Instance", "cip.instance",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Instance", HFILL }
+ },
+ { &hf_cip_member8,
+ { "Member", "cip.member",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "Member", HFILL }
+ },
+ { &hf_cip_member16,
+ { "Member", "cip.member",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "Member", HFILL }
+ },
+ { &hf_cip_member32,
+ { "Member", "cip.member",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Member", HFILL }
+ },
+ { &hf_cip_attribute8,
+ { "Attribute", "cip.attribute",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "Attribute", HFILL }
+ },
+ { &hf_cip_attribute16,
+ { "Attribute", "cip.attribute",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "Attribute", HFILL }
+ },
+ { &hf_cip_attribute32,
+ { "Attribute", "cip.attribute",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Attribute", HFILL }
+ },
+ { &hf_cip_conpoint8,
+ { "Connection Point", "cip.connpoint",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "Connection Point", HFILL }
+ },
+ { &hf_cip_conpoint16,
+ { "Connection Point", "cip.connpoint",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "Connection Point", HFILL }
+ },
+ { &hf_cip_conpoint32,
+ { "Connection Point", "cip.connpoint",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "Connection Point", HFILL }
+ },
+ { &hf_cip_symbol,
+ { "Symbol", "cip.symbol",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "ANSI Extended Symbol Segment", HFILL }
+ },
+ { &hf_cip_vendor,
+ { "Vendor ID", "cip.vendor",
+ FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
+ "Vendor ID", HFILL }
+ },
+ { &hf_cip_devtype,
+ { "Device Type", "cip.devtype",
+ FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
+ "Device Type", HFILL }
+ },
+ { &hf_cip_fwo_comp,
+ { "Compatibility", "cip.fwo.cmp",
+ FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
+ "EKey: Compatibility bit", HFILL }
+ },
+ { &hf_cip_fwo_mrev,
+ { "Major Revision", "cip.fwo.major",
+ FT_UINT8, BASE_DEC, NULL, 0x7F,
+ "EKey: Major Revision", HFILL }
+ }
+ };
+
+ static hf_register_info hf_cm[] = {
+ { &hf_cip_cm_rr,
+ { "Request/Response", "cip.rr",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+ "Request or Response message", HFILL }
+ },
+ { &hf_cip_cm_sc,
+ { "Service", "cip.sc",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+ "Service Code", HFILL }
+ },
+ { &hf_cip_cm_fwo_comp,
+ { "Compatibility", "cip.cm.fwo.cmp",
+ FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
+ "Fwd Open: Compatibility bit", HFILL }
+ },
+ { &hf_cip_cm_fwo_mrev,
+ { "Major Revision", "cip.cm.fwo.major",
+ FT_UINT8, BASE_DEC, NULL, 0x7F,
+ "Fwd Open: Major Revision", HFILL }
+ },
+ { &hf_cip_cm_fwo_con_size,
+ { "Connection Size", "cip.cm.fwo.consize",
+ FT_UINT16, BASE_DEC, NULL, 0x01FF,
+ "Fwd Open: Connection size", HFILL }
+ },
+ { &hf_cip_cm_fwo_fixed_var,
+ { "Connection Size Type", "cip.cm.fwo.f_v",
+ FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200,
+ "Fwd Open: Fixed or variable connection size", HFILL }
+ },
+ { &hf_cip_cm_fwo_prio,
+ { "Priority", "cip.cm.fwo.prio",
+ FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00,
+ "Fwd Open: Connection priority", HFILL }
+ },
+ { &hf_cip_cm_fwo_typ,
+ { "Connection Type", "cip.cm.fwo.type",
+ FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000,
+ "Fwd Open: Connection type", HFILL }
+ },
+ { &hf_cip_cm_fwo_own,
+ { "Owner", "cip.cm.fwo.owner",
+ FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000,
+ "Fwd Open: Redundant owner bit", HFILL }
+ },
+ { &hf_cip_cm_fwo_dir,
+ { "Direction", "cip.cm.fwo.dir",
+ FT_UINT8, BASE_DEC, VALS(cip_con_dir_vals), 0x80,
+ "Fwd Open: Direction", HFILL }
+ },
+ { &hf_cip_cm_fwo_trigg,
+ { "Trigger", "cip.cm.fwo.trigger",
+ FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), 0x70,
+ "Fwd Open: Production trigger", HFILL }
+ },
+ { &hf_cip_cm_fwo_class,
+ { "Class", "cip.cm.fwo.transport",
+ FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), 0x0F,
+ "Fwd Open: Transport Class", HFILL }
+ }
+ };
+
+ static hf_register_info hf_mr[] = {
+ { &hf_cip_mr_rr,
+ { "Request/Response", "cip.rr",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+ "Request or Response message", HFILL }
+ },
+ { &hf_cip_mr_sc,
+ { "Service", "cip.sc",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+ "Service Code", HFILL }
+ }
+ };
+
+ static hf_register_info hf_cco[] = {
+ { &hf_cip_cco_rr,
+ { "Request/Response", "cip.rr",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+ "Request or Response message", HFILL }
+ },
+ { &hf_cip_cco_sc,
+ { "Service", "cip.sc",
+ FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+ "Service Code", HFILL }
+ }
};
/* Setup protocol subtree array */
- static gint *ett[] = {
- &ett_cip,
- &ett_path,
- &ett_ekey_path,
- &ett_rrsc,
- &ett_mcsc,
- &ett_ncp,
- &ett_cia_path,
- &ett_data_seg,
- &ett_lsrcf,
- &ett_mes_req,
- &ett_cmd_data,
- &ett_port_path,
- &ett_mult_ser,
- &ett_path,
- &ett_status_item
- };
+ static gint *ett[] = {
+ &ett_cip_class_generic,
+ &ett_cip,
+ &ett_path,
+ &ett_ekey_path,
+ &ett_rrsc,
+ &ett_mcsc,
+ &ett_cia_path,
+ &ett_data_seg,
+ &ett_cmd_data,
+ &ett_port_path,
+ &ett_status_item
+ };
+
+ static gint *ett_mr[] = {
+ &ett_cip_class_mr,
+ &ett_mr_rrsc,
+ &ett_mr_mult_ser,
+ &ett_mr_cmd_data
+ };
+
+ static gint *ett_cm[] = {
+ &ett_cip_class_cm,
+ &ett_cm_rrsc,
+ &ett_cm_mes_req,
+ &ett_cm_ncp,
+ &ett_cm_cmd_data
+ };
+
+ static gint *ett_cco[] = {
+ &ett_cip_class_cco,
+ &ett_cco_rrsc,
+ &ett_cco_cmd_data
+ };
/* Register the protocol name and description */
proto_cip = proto_register_protocol("Common Industrial Protocol",
- "CIP", "cip");
+ "CIP", "cip");
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_cip, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
+ subdissector_class_table = register_dissector_table("cip.class.iface",
+ "CIP Class Interface Handle", FT_UINT32, BASE_HEX);
+ subdissector_symbol_table = register_dissector_table("cip.data_segment.iface",
+ "CIP Data Segment Interface Handle", FT_UINT32, BASE_HEX);
+ /* Register the protocol name and description */
+ proto_cip_class_generic = proto_register_protocol("CIP Class Generic",
+ "CIPCLS", "cipcls");
+
+ /* Register the protocol name and description */
+ proto_cip_class_mr = proto_register_protocol("CIP Message Router",
+ "CIPMR", "cipmr");
+ proto_register_field_array(proto_cip_class_mr, hf_mr, array_length(hf_mr));
+ proto_register_subtree_array(ett_mr, array_length(ett_mr));
+
+ proto_cip_class_cm = proto_register_protocol("CIP Connection Manager",
+ "CIPCM", "cipcm");
+ proto_register_field_array(proto_cip_class_cm, hf_cm, array_length(hf_cm));
+ proto_register_subtree_array(ett_cm, array_length(ett_cm));
+
+ proto_cip_class_cco = proto_register_protocol("CIP Connection Configuration Object",
+ "CIPCCO", "cipcco");
+ proto_register_field_array(proto_cip_class_cco, hf_cco, array_length(hf_cco));
+ proto_register_subtree_array(ett_cco, array_length(ett_cco));
+
+ register_init_routine(&cip_init_protocol);
} /* end of proto_register_cip() */
void
proto_reg_handoff_cip(void)
{
- dissector_handle_t cip_handle;
-
/* Create dissector handles */
- cip_handle = new_create_dissector_handle( dissect_cip, proto_cip );
-
/* Register for UCMM CIP data, using EtherNet/IP SendRRData service*/
- dissector_add( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle );
-
/* Register for Connected CIP data, using EtherNet/IP SendUnitData service*/
+ cip_handle = new_create_dissector_handle( dissect_cip, proto_cip );
+ dissector_add( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle );
dissector_add( "enip.sud.iface", ENIP_CIP_INTERFACE, cip_handle );
+ /* Create and register dissector handle for generic class */
+ cip_class_generic_handle = new_create_dissector_handle( dissect_cip_class_generic, proto_cip_class_generic );
+ dissector_add( "cip.class.iface", 0, cip_class_generic_handle );
+
+ /* Create and register dissector handle for Message Router */
+ cip_class_mr_handle = new_create_dissector_handle( dissect_cip_class_mr, proto_cip_class_mr );
+ dissector_add( "cip.class.iface", CI_CLS_MR, cip_class_mr_handle );
+
+ /* Create and register dissector handle for Connection Manager */
+ cip_class_cm_handle = new_create_dissector_handle( dissect_cip_class_cm, proto_cip_class_cm );
+ dissector_add( "cip.class.iface", CI_CLS_CM, cip_class_cm_handle );
+
+ /* Create and register dissector handle for Connection Configuration Object */
+ cip_class_cco_handle = new_create_dissector_handle( dissect_cip_class_cco, proto_cip_class_cco );
+ dissector_add( "cip.class.iface", CI_CLS_CCO, cip_class_cco_handle );
+
} /* end of proto_reg_handoff_cip() */
diff --git a/epan/dissectors/packet-cip.h b/epan/dissectors/packet-cip.h
index a900362c69..e36c797e23 100644
--- a/epan/dissectors/packet-cip.h
+++ b/epan/dissectors/packet-cip.h
@@ -8,8 +8,6 @@
*
* Added support for Connection Configuration Object
* ryan wamsley * Copyright 2007
- * Added Additional Status text in Forward Open Response
- * ryan wamsley * Copyright 2008
*
* $Id$
*
@@ -53,19 +51,19 @@
#define SC_GET_MEMBER 0x18
#define SC_SET_MEMBER 0x19
/* Class specific services */
-#define SC_FWD_CLOSE 0x4E
-#define SC_UNCON_SEND 0x52
-#define SC_FWD_OPEN 0x54
+/* Connection Manager */
+#define SC_CM_FWD_CLOSE 0x4E
+#define SC_CM_UNCON_SEND 0x52
+#define SC_CM_FWD_OPEN 0x54
/* Connection Configuration Object services */
-#define SC_KICK_TIMER 0x4B
-#define SC_OPEN_CONN 0x4C
-#define SC_CLOSE_CONN 0x4D
-#define SC_STOP_CONN 0x4E /* collision with SC_FWD_CLOSE */
-#define SC_CHANGE_START 0x4F
-#define SC_GET_STATUS 0x50
-#define SC_CHANGE_COMPLETE 0x51
-#define SC_AUDIT_CHANGE 0x52 /* collision with SC_UNCON_SEND */
-
+#define SC_CCO_KICK_TIMER 0x4B
+#define SC_CCO_OPEN_CONN 0x4C
+#define SC_CCO_CLOSE_CONN 0x4D
+#define SC_CCO_STOP_CONN 0x4E
+#define SC_CCO_CHANGE_START 0x4F
+#define SC_CCO_GET_STATUS 0x50
+#define SC_CCO_CHANGE_COMPLETE 0x51
+#define SC_CCO_AUDIT_CHANGE 0x52
/* CIP Genral status codes */
#define CI_GRC_SUCCESS 0x00
@@ -111,47 +109,6 @@
#define CI_GRC_STILL_PROCESSING 0xFF
-/* Extended Status Error Codes */
-#define CI_SREC_CONNECTION_IN_USE 0x0100
-#define CI_SREC_TCLASS_TRIGGER_ERR 0x0103
-#define CI_SREC_OWNERSHIP_CONFLICT 0x0106
-#define CI_SREC_CONN_NOT_FOUND 0x0107
-#define CI_SREC_INVALID_CONN_TYPE 0x0108
-#define CI_SREC_INVALID_CONN_SIZE 0x0109
-#define CI_SREC_DEV_NOT_CONFIGURED 0x0110
-#define CI_SREC_UNSUPPORTED_RPI 0x0111
-#define CI_SREC_NO_MORE_CONNS 0x0113
-#define CI_SREC_VEN_OR_PCODE_MISMATCH 0x0114
-#define CI_SREC_PRODTYPE_MISMATCH 0x0115
-#define CI_SREC_REVISION_MISMATCH 0x0116
-#define CI_SREC_BAD_CONN_POINT 0x0117
-#define CI_SREC_INVAL_CONFIG_FRMT 0x0118
-#define CI_SREC_NO_CONTROL_CONN 0x0119
-#define CI_SREC_NO_MORE_CONN_SUPPORT 0x011A
-#define CI_SREC_RPI_SMALLERTHAN_PIT 0x011B
-#define CI_SREC_CONN_ALREADY_CLOSED 0x0203
-#define CI_SREC_UNCONN_SND_TIMEOUT 0x0204
-#define CI_SREC_UNCONN_PARM_ERR 0x0205
-#define CI_SREC_UCONN_TOO_LARGE 0x0206
-#define CI_SREC_UCONN_ACK_NO_REP 0x0207
-#define CI_SREC_NO_MEMORY 0x0301
-#define CI_SREC_NO_NET_BANDWIDTH 0x0302
-#define CI_SREC_NO_SCREENERS 0x0303
-#define CI_SREC_NO_REALTIME_CONFIG 0x0304
-#define CI_SREC_INVALID_PORT 0x0311
-#define CI_SREC_LINKADDR_NOT_AVAIL 0x0312
-#define CI_SREC_INVALID_SEGMENT_TYP 0x0315
-#define CI_SREC_CLOSE_PATH_ERR 0x0316
-#define CI_SREC_NO_SCHED 0x0317
-#define CI_SREC_INVALID_LINK_ADDR 0x0318
-#define CI_SREC_UNAVAIL_RESOURCE 0x0319
-#define CI_SREC_CONN_ALREADY_ESTAB 0x031A
-#define CI_SREC_DCONN_ALREADY_ESTAB 0x031B
-#define CI_SREC_MISC 0x031C
-#define CI_SREC_REDUNDANT_MISMATCH 0x031D
-#define CI_SREC_NO_CONSUME_RESRC 0x031E
-#define CI_SREC_NO_CONN_RESRC 0x031F
-
/* IOI Path types */
#define CI_SEGMENT_TYPE_MASK 0xE0
@@ -191,7 +148,7 @@
/* Device Profile:s */
#define DP_GEN_DEV 0x00
-#define DP_AC_DRIVE 0x02
+#define DP_AC_DRIVE 0x02
#define DP_MOTOR_OVERLOAD 0x03
#define DP_LIMIT_SWITCH 0x04
#define DP_IND_PROX_SWITCH 0x05
@@ -1005,8 +962,8 @@
{ 899, "Practicon Ltd" }, \
{ 900, "Schunk GmbH & Co. KG" }, \
{ 902, "Defontaine Groupe" }, \
- { 903, "Emerson Process Management Power & Water Solutions" },
-
+ { 903, "Emerson Process Management Power & Water Solutions" }, \
+ { 981, "Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG" },
/*
** Exported variables
diff --git a/epan/dissectors/packet-enip.c b/epan/dissectors/packet-enip.c
index 31ce1ce1a9..02cd23dbf9 100644
--- a/epan/dissectors/packet-enip.c
+++ b/epan/dissectors/packet-enip.c
@@ -6,6 +6,10 @@
* Magnus Hansson <mah@hms.se>
* Joakim Wiberg <jow@hms.se>
*
+ * Conversation data support for CIP
+ * Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
+ * Copyright 2007
+ *
* $Id$
*
* Wireshark - Network traffic analyzer
@@ -38,19 +42,18 @@
#include <glib.h>
#include <epan/packet.h>
-#include <prefs.h>
+#include <epan/emem.h>
+#include <epan/conversation.h>
+#include <epan/prefs.h>
#include "packet-tcp.h"
+#include "packet-enip.h"
#include "packet-cip.h"
+#define se_new(type) ((type*)se_alloc(sizeof(type)))
/* Communication Ports */
-#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */
-#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */
-
-/* Return codes of function classifying packets as query/response */
-#define REQUEST_PACKET 0
-#define RESPONSE_PACKET 1
-#define CANNOT_CLASSIFY 2
+#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */
+#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */
/* EtherNet/IP function codes */
#define NOP 0x0000
@@ -118,6 +121,10 @@ static int hf_enip_cpf_typeid = -1;
static int hf_enip_cpf_sai_connid = -1;
static int hf_enip_cpf_sai_seqnum = -1;
+static int hf_enip_response_in = -1;
+static int hf_enip_response_to = -1;
+static int hf_enip_time = -1;
+
/* Initialize the subtree pointers */
static gint ett_enip = -1;
static gint ett_count_tree = -1;
@@ -135,322 +142,759 @@ static gboolean enip_desegment = TRUE;
/* Translate function to string - Encapsulation commands */
static const value_string encap_cmd_vals[] = {
- { NOP, "NOP" },
- { LIST_SERVICES, "List Services" },
- { LIST_IDENTITY, "List Identity" },
- { LIST_INTERFACES, "List Interfaces" },
- { REGISTER_SESSION, "Register Session" },
- { UNREGISTER_SESSION,"Unregister Session" },
- { SEND_RR_DATA, "Send RR Data" },
- { SEND_UNIT_DATA, "Send Unit Data" },
- { INDICATE_STATUS, "Indicate Status" },
- { CANCEL, "Cancel" },
-
- { 0, NULL }
+ { NOP, "NOP" },
+ { LIST_SERVICES, "List Services" },
+ { LIST_IDENTITY, "List Identity" },
+ { LIST_INTERFACES, "List Interfaces" },
+ { REGISTER_SESSION, "Register Session" },
+ { UNREGISTER_SESSION,"Unregister Session" },
+ { SEND_RR_DATA, "Send RR Data" },
+ { SEND_UNIT_DATA, "Send Unit Data" },
+ { INDICATE_STATUS, "Indicate Status" },
+ { CANCEL, "Cancel" },
+
+ { 0, NULL }
};
/* Translate function to string - Encapsulation status */
static const value_string encap_status_vals[] = {
- { SUCCESS, "Success" },
- { INVALID_CMD, "Invalid Command" },
- { NO_RESOURCES, "No Memory Resources" },
- { INCORRECT_DATA, "Incorrect Data" },
- { INVALID_SESSION, "Invalid Session Handle" },
- { INVALID_LENGTH, "Invalid Length" },
- { UNSUPPORTED_PROT_REV, "Unsupported Protocol Revision" },
-
- { 0, NULL }
+ { SUCCESS, "Success" },
+ { INVALID_CMD, "Invalid Command" },
+ { NO_RESOURCES, "No Memory Resources" },
+ { INCORRECT_DATA, "Incorrect Data" },
+ { INVALID_SESSION, "Invalid Session Handle" },
+ { INVALID_LENGTH, "Invalid Length" },
+ { UNSUPPORTED_PROT_REV, "Unsupported Protocol Revision" },
+
+ { 0, NULL }
};
/* Translate function to Common data format values */
static const value_string cdf_type_vals[] = {
- { CDF_NULL, "Null Address Item" },
- { LIST_IDENTITY_RESP, "List Identity Response" },
- { CONNECTION_BASED, "Connected Address Item" },
- { CONNECTION_TRANSPORT, "Connected Data Item" },
- { UNCONNECTED_MSG, "Unconnected Data Item" },
- { LIST_SERVICES_RESP, "List Services Response" },
- { SOCK_ADR_INFO_OT, "Socket Address Info O->T" },
- { SOCK_ADR_INFO_TO, "Socket Address Info T->O" },
- { SEQ_ADDRESS, "Sequenced Address Item" },
-
- { 0, NULL }
+ { CDF_NULL, "Null Address Item" },
+ { LIST_IDENTITY_RESP, "List Identity Response" },
+ { CONNECTION_BASED, "Connected Address Item" },
+ { CONNECTION_TRANSPORT, "Connected Data Item" },
+ { UNCONNECTED_MSG, "Unconnected Data Item" },
+ { LIST_SERVICES_RESP, "List Services Response" },
+ { SOCK_ADR_INFO_OT, "Socket Address Info O->T" },
+ { SOCK_ADR_INFO_TO, "Socket Address Info T->O" },
+ { SEQ_ADDRESS, "Sequenced Address Item" },
+
+ { 0, NULL }
};
/* Translate function to string - True/False */
static const value_string enip_true_false_vals[] = {
- { 0, "False" },
- { 1, "True" },
+ { 0, "False" },
+ { 1, "True" },
- { 0, NULL }
+ { 0, NULL }
};
/* Translate interface handle to string */
static const value_string enip_interface_handle_vals[] = {
- { 0, "CIP" },
+ { 0, "CIP" },
- { 0, NULL }
+ { 0, NULL }
};
+static GHashTable *enip_request_hashtable = NULL;
+
+/* Return codes of function classifying packets as query/response */
+#define ENIP_REQUEST_PACKET 0
+#define ENIP_RESPONSE_PACKET 1
+#define ENIP_CANNOT_CLASSIFY 2
+
+enum enip_packet_data_type { EPDT_UNKNOWN, EPDT_CONNECTED_TRANSPORT, EPDT_UNCONNECTED };
+
+typedef struct enip_request_key {
+ gint requesttype;
+ enum enip_packet_data_type type;
+ guint32 session_handle;
+ guint64 sender_context;
+ guint32 conversation;
+ union {
+ struct {
+ guint32 connid;
+ guint16 sequence;
+ } connected_transport;
+ } data;
+} enip_request_key_t;
+
+typedef struct enip_request_val {
+ emem_tree_t *frames;
+} enip_request_val_t;
+
+/*
+ * Hash Functions
+ */
+static gint
+enip_request_equal(gconstpointer v, gconstpointer w)
+{
+ const enip_request_key_t *v1 = (const enip_request_key_t *)v;
+ const enip_request_key_t *v2 = (const enip_request_key_t *)w;
+
+ if ( v1->conversation == v2->conversation &&
+ v1->session_handle == v2->session_handle &&
+ v1->type == v2->type &&
+ ((v1->sender_context == v2->sender_context && /* heuristic approach */
+ v1->type == EPDT_UNCONNECTED)
+ ||
+ (v1->data.connected_transport.connid == v2->data.connected_transport.connid &&
+ v1->data.connected_transport.sequence == v2->data.connected_transport.sequence &&
+ v1->type == EPDT_CONNECTED_TRANSPORT))
+ )
+ return 1;
+
+ return 0;
+}
+
+static guint
+enip_request_hash (gconstpointer v)
+{
+ const enip_request_key_t *key = (const enip_request_key_t *)v;
+ guint val;
+
+ val = (guint)( key->conversation * 37 + key->session_handle * 93 + key->type * 765
+ + key->sender_context * 23
+ + key->data.connected_transport.connid * 87 + key->data.connected_transport.sequence * 834 );
+
+ return val;
+}
+
+static enip_request_info_t *
+enip_match_request( packet_info *pinfo, proto_tree *tree, enip_request_key_t *prequest_key )
+{
+enip_request_key_t *new_request_key;
+enip_request_val_t *request_val;
+enip_request_info_t *request_info = NULL;
+
+ request_info = NULL;
+ request_val = g_hash_table_lookup( enip_request_hashtable, prequest_key );
+ if(!pinfo->fd->flags.visited)
+ {
+ if ( prequest_key && prequest_key->requesttype == ENIP_REQUEST_PACKET )
+ {
+ if ( request_val == NULL )
+ {
+ new_request_key = se_alloc(sizeof(enip_request_key_t));
+ memcpy( new_request_key, prequest_key, sizeof(enip_request_key_t) );
+
+ request_val = se_alloc(sizeof(enip_request_val_t));
+ request_val->frames = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "enip_frames");
+
+ g_hash_table_insert(enip_request_hashtable, new_request_key, request_val );
+ }
+
+ request_info = se_alloc(sizeof(enip_request_info_t));
+ request_info->req_num = pinfo->fd->num;
+ request_info->rep_num = 0;
+ request_info->req_time = pinfo->fd->abs_ts;
+ request_info->cip_info = NULL;
+ se_tree_insert32(request_val->frames, pinfo->fd->num, (void *)request_info);
+ }
+ if( request_val && prequest_key && prequest_key->requesttype == ENIP_RESPONSE_PACKET )
+ {
+ request_info = (enip_request_info_t*)se_tree_lookup32_le( request_val->frames, pinfo->fd->num );
+ if ( request_info )
+ {
+ request_info->rep_num = pinfo->fd->num;
+ }
+ }
+ }
+ else
+ {
+ if ( request_val )
+ request_info = (enip_request_info_t*)se_tree_lookup32_le( request_val->frames, pinfo->fd->num );
+ }
+
+ if ( tree && request_info )
+ {
+ /* print state tracking in the tree */
+ if ( prequest_key && prequest_key->requesttype == ENIP_REQUEST_PACKET )
+ {
+ /* This is a request */
+ if (request_info->rep_num)
+ {
+ proto_item *it;
+
+ it = proto_tree_add_uint(tree, hf_enip_response_in,
+ NULL, 0, 0, request_info->rep_num);
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ }
+ else
+ {
+ if ( prequest_key && prequest_key->requesttype == ENIP_RESPONSE_PACKET )
+ {
+ /* This is a reply */
+ if (request_info->req_num)
+ {
+ proto_item *it;
+ nstime_t ns;
+
+ it = proto_tree_add_uint(tree, hf_enip_response_to,
+ NULL, 0, 0, request_info->req_num);
+ PROTO_ITEM_SET_GENERATED(it);
+
+ nstime_delta(&ns, &pinfo->fd->abs_ts, &request_info->req_time);
+ it = proto_tree_add_time(tree, hf_enip_time, NULL, 0, 0, &ns);
+ PROTO_ITEM_SET_GENERATED(it);
+ }
+ }
+ }
+ }
+ return request_info;
+}
+
+/*
+ * Connection management
+ */
+
+typedef struct enip_conn_key {
+ guint16 ConnSerialNumber;
+ guint16 VendorID;
+ guint32 DeviceSerialNumber;
+} enip_conn_key_t;
+
+typedef struct enip_conn_val {
+ guint16 ConnSerialNumber;
+ guint16 VendorID;
+ guint32 DeviceSerialNumber;
+ guint32 O2TConnID;
+ guint32 T2OConnID;
+ guint32 openframe;
+ guint32 closeframe;
+ guint32 connid;
+} enip_conn_val_t;
+
+typedef struct _enip_conv_info_t {
+ emem_tree_t *O2TConnIDs;
+ emem_tree_t *T2OConnIDs;
+} enip_conv_info_t;
+
+static GHashTable *enip_conn_hashtable = NULL;
+static guint32 enip_unique_connid = 1;
+
+static gint
+enip_conn_equal(gconstpointer v, gconstpointer w)
+{
+ const enip_conn_key_t *v1 = (const enip_conn_key_t *)v;
+ const enip_conn_key_t *v2 = (const enip_conn_key_t *)w;
+
+ if ( v1->ConnSerialNumber == v2->ConnSerialNumber
+ && v1->VendorID == v2->VendorID
+ && v1->DeviceSerialNumber == v2->DeviceSerialNumber
+ )
+ return 1;
+
+ return 0;
+}
+
+static guint
+enip_conn_hash (gconstpointer v)
+{
+ const enip_conn_key_t *key = (const enip_conn_key_t *)v;
+ guint val;
+
+ val = (guint)( key->ConnSerialNumber + key->VendorID + key->DeviceSerialNumber );
+
+ return val;
+}
+
+void enip_open_cip_connection( packet_info *pinfo,
+ guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber,
+ guint32 O2TConnID, guint32 T2OConnID )
+{
+enip_conn_key_t *conn_key;
+enip_conn_val_t *conn_val;
+conversation_t *conversation;
+enip_conv_info_t *enip_info;
+
+ if (pinfo->fd->flags.visited)
+ return;
+
+ conn_key = se_alloc(sizeof(enip_conn_key_t));
+ conn_key->ConnSerialNumber = ConnSerialNumber;
+ conn_key->VendorID = VendorID;
+ conn_key->DeviceSerialNumber = DeviceSerialNumber;
+
+ conn_val = g_hash_table_lookup( enip_conn_hashtable, conn_key );
+ if ( conn_val == NULL )
+ {
+ conn_val = se_alloc(sizeof(enip_conn_val_t));
+
+ conn_val->ConnSerialNumber = ConnSerialNumber;
+ conn_val->VendorID = VendorID;
+ conn_val->DeviceSerialNumber = DeviceSerialNumber;
+ conn_val->O2TConnID = O2TConnID;
+ conn_val->T2OConnID = T2OConnID;
+ conn_val->openframe = pinfo->fd->num;
+ conn_val->closeframe = 0;
+ conn_val->connid = enip_unique_connid++;
+
+ g_hash_table_insert(enip_conn_hashtable, conn_key, conn_val );
+
+ /*
+ * Do we have a conversation for this connection?
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst,
+ pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ if (conversation == NULL)
+ {
+ /* We don't yet have a conversation, so create one. */
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst,
+ pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ }
+ /*
+ * Do we already have a state structure for this conv
+ */
+ enip_info = conversation_get_proto_data(conversation, proto_enip);
+ if (!enip_info)
+ {
+ /*
+ * No. Attach that information to the conversation, and add
+ * it to the list of information structures.
+ */
+ enip_info = se_alloc(sizeof(enip_conv_info_t));
+ enip_info->O2TConnIDs = se_tree_create_non_persistent(
+ EMEM_TREE_TYPE_RED_BLACK, "enip_O2T");
+ enip_info->T2OConnIDs = se_tree_create_non_persistent(
+ EMEM_TREE_TYPE_RED_BLACK, "enip_T2O");
+
+ conversation_add_proto_data(conversation, proto_enip, enip_info);
+ }
+ se_tree_insert32(enip_info->O2TConnIDs, O2TConnID, (void *)conn_val);
+ se_tree_insert32(enip_info->O2TConnIDs, T2OConnID, (void *)conn_val);
+ }
+}
+
+void enip_close_cip_connection( packet_info *pinfo,
+ guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber )
+{
+enip_conn_key_t conn_key;
+enip_conn_val_t *conn_val;
+
+ if (pinfo->fd->flags.visited)
+ return;
+
+ conn_key.ConnSerialNumber = ConnSerialNumber;
+ conn_key.VendorID = VendorID;
+ conn_key.DeviceSerialNumber = DeviceSerialNumber;
+
+ conn_val = g_hash_table_lookup( enip_conn_hashtable, &conn_key );
+ if ( conn_val )
+ {
+ conn_val->closeframe = pinfo->fd->num;
+ }
+}
+
+static guint32 enip_get_connid( packet_info *pinfo, enip_request_key_t *prequest_key, guint32 connid )
+{
+conversation_t *conversation;
+enip_conv_info_t *enip_info;
+enip_conn_val_t *conn_val;
+
+ if ( prequest_key == NULL
+ || ( prequest_key->requesttype != ENIP_REQUEST_PACKET && prequest_key->requesttype != ENIP_RESPONSE_PACKET )
+ )
+ return 0;
+
+ /*
+ * Do we have a conversation for this connection?
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst,
+ pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ if (conversation == NULL)
+ return 0;
+
+ /*
+ * Do we already have a state structure for this conv
+ */
+ enip_info = conversation_get_proto_data(conversation, proto_enip);
+ if (!enip_info)
+ return 0;
+
+ conn_val = NULL;
+ switch ( prequest_key->requesttype )
+ {
+ case ENIP_REQUEST_PACKET:
+ conn_val = se_tree_lookup32( enip_info->O2TConnIDs, connid );
+ if ( conn_val == NULL )
+ conn_val = se_tree_lookup32( enip_info->T2OConnIDs, connid );
+ break;
+
+ case ENIP_RESPONSE_PACKET:
+ conn_val = se_tree_lookup32( enip_info->T2OConnIDs, connid );
+ if ( conn_val == NULL )
+ conn_val = se_tree_lookup32( enip_info->O2TConnIDs, connid );
+ break;
+ }
+
+ if ( conn_val == NULL )
+ return 0;
+
+ if ( conn_val->openframe > pinfo->fd->num )
+ return 0;
+
+ return conn_val->connid;
+}
+
+/*
+ * Protocol initialization
+ */
+static void
+enip_init_protocol(void)
+{
+ if (enip_request_hashtable)
+ g_hash_table_destroy(enip_request_hashtable);
+ enip_request_hashtable = g_hash_table_new(enip_request_hash, enip_request_equal);
+
+ if (enip_conn_hashtable)
+ g_hash_table_destroy(enip_conn_hashtable);
+ enip_conn_hashtable = g_hash_table_new(enip_conn_hash, enip_conn_equal);
+}
+
+static proto_item*
+add_byte_array_text_to_proto_tree( proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char* str )
+{
+ const char *tmp;
+ char *tmp2, *tmp2start;
+ proto_item *pi;
+ int i,tmp_length,tmp2_length;
+ guint32 octet;
+ /* At least one version of Apple's C compiler/linker is buggy, causing
+ a complaint from the linker about the "literal C string section"
+ not ending with '\0' if we initialize a 16-element "char" array with
+ a 16-character string, the fact that initializing such an array with
+ such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
+ '\0' byte in the string nonwithstanding. */
+ static const char my_hex_digits[16] =
+ { '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+
+ if( ( length * 2 ) > 32 )
+ {
+ tmp_length = 16;
+ tmp2_length = 36;
+ }
+ else
+ {
+ tmp_length = length;
+ tmp2_length = ( length * 2 ) + 1;
+ }
+
+ tmp = (const char *)tvb_get_ptr( tvb, start, tmp_length );
+ tmp2 = (char *)ep_alloc( tmp2_length );
+
+ tmp2start = tmp2;
+
+ for( i = 0; i < tmp_length; i++ )
+ {
+ octet = tmp[i];
+ octet >>= 4;
+ *tmp2++ = my_hex_digits[octet&0xF];
+ octet = tmp[i];
+ *tmp2++ = my_hex_digits[octet&0xF];
+ }
+
+ if( tmp_length != length )
+ {
+ *tmp2++ = '.';
+ *tmp2++ = '.';
+ *tmp2++ = '.';
+ }
+
+ *tmp2 = '\0';
+
+ pi = proto_tree_add_text( tree, tvb, start, length, "%s%s", str, tmp2start );
+
+ return( pi );
+
+} /* end of add_byte_array_text_to_proto_tree() */
/* Disssect Common Packet Format */
static void
-dissect_cpf( int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl )
+dissect_cpf( enip_request_key_t *request_key, int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl )
{
proto_item *temp_item, *count_item, *type_item, *sockaddr_item;
- proto_tree *temp_tree, *count_tree, *item_tree, *sockaddr_tree;
- int temp_data, item_count, item_length, item;
- unsigned char name_length;
- tvbuff_t *next_tvb;
-
- /* Create item count tree */
- item_count = tvb_get_letohs( tvb, offset );
- count_item = proto_tree_add_text( tree, tvb, offset, 2, "Item Count: %d", item_count );
- count_tree = proto_item_add_subtree( count_item, ett_count_tree );
-
- while( item_count-- )
- {
- /* Add item type tree to item count tree*/
- type_item = proto_tree_add_item( count_tree, hf_enip_cpf_typeid, tvb, offset+2, 2, TRUE );
- item_tree = proto_item_add_subtree( type_item, ett_type_tree );
-
- /* Add length field to item type tree*/
- proto_tree_add_text( item_tree, tvb, offset+4, 2, "Length: %d", tvb_get_letohs( tvb, offset+4 ) );
+ proto_tree *temp_tree, *count_tree, *item_tree, *sockaddr_tree;
+ int temp_data, item_count, item_length, item;
+ unsigned char name_length;
+ tvbuff_t *next_tvb;
+ enip_request_info_t *request_info;
- item = tvb_get_letohs( tvb, offset+2 );
- item_length = tvb_get_letohs( tvb, offset+4 );
-
- if( item_length )
- {
- /* Add item data field */
-
- switch( item )
- {
- case CONNECTION_BASED:
-
- /* Add Connection identifier */
- proto_tree_add_text( item_tree, tvb, offset+6, 4, "Connection Identifier: 0x%08X", tvb_get_letohl( tvb, offset + 6 ) );
-
- /* Add Connection ID to Info col */
- if(check_col(pinfo->cinfo, COL_INFO))
- {
- col_append_fstr(pinfo->cinfo, COL_INFO,
- ", CONID: 0x%08X",
- tvb_get_letohl( tvb, offset+6 ) );
- }
-
- break;
+ /* Create item count tree */
+ item_count = tvb_get_letohs( tvb, offset );
+ count_item = proto_tree_add_text( tree, tvb, offset, 2, "Item Count: %d", item_count );
+ count_tree = proto_item_add_subtree( count_item, ett_count_tree );
- case UNCONNECTED_MSG:
-
- /* Call dissector for interface */
- next_tvb = tvb_new_subset( tvb, offset+6, item_length, item_length );
-
- if( tvb_length(next_tvb) == 0 || !dissector_try_port(subdissector_srrd_table, ifacehndl, next_tvb, pinfo, g_tree) )
- {
- /* Show the undissected payload */
- if( tvb_length_remaining(tvb, offset) > 0 )
- call_dissector( data_handle, next_tvb, pinfo, g_tree );
- }
-
- break;
-
- case CONNECTION_TRANSPORT:
-
- if( command == SEND_UNIT_DATA )
- {
- /*
- ** If the encapsulation service is SendUnit Data, this is a
- ** encapsulated connected message
- */
-
- /* Add sequence count ( Transport Class 1,2,3 )*/
- proto_tree_add_text( item_tree, tvb, offset+6, 2, "Sequence Count: 0x%04X", tvb_get_letohs( tvb, offset+6 ) );
-
- /* Call dissector for interface */
- next_tvb = tvb_new_subset (tvb, offset+8, item_length-2, item_length-2);
-
- if( tvb_length(next_tvb) == 0 || !dissector_try_port(subdissector_sud_table, ifacehndl, next_tvb, pinfo, g_tree) )
- {
- /* Show the undissected payload */
- if( tvb_length_remaining(tvb, offset) > 0 )
- call_dissector( data_handle, next_tvb, pinfo, g_tree );
- }
+ while( item_count-- )
+ {
+ /* Add item type tree to item count tree*/
+ type_item = proto_tree_add_item( count_tree, hf_enip_cpf_typeid, tvb, offset+2, 2, TRUE );
+ item_tree = proto_item_add_subtree( type_item, ett_type_tree );
- }
- else
- {
- /* Display data */
- if (tvb_length_remaining(tvb, offset+6) > 0)
- {
- next_tvb = tvb_new_subset(tvb, offset+6, item_length, item_length);
- call_dissector(data_handle, next_tvb, pinfo, item_tree);
- }
- } /* End of if send unit data */
+ /* Add length field to item type tree*/
+ proto_tree_add_text( item_tree, tvb, offset+4, 2, "Length: %d", tvb_get_letohs( tvb, offset+4 ) );
- break;
+ item = tvb_get_letohs( tvb, offset+2 );
+ item_length = tvb_get_letohs( tvb, offset+4 );
+ if( item_length )
+ {
+ /* Add item data field */
- case LIST_IDENTITY_RESP:
+ switch( item )
+ {
+ case CONNECTION_BASED:
- /* Encapsulation version */
- temp_data = tvb_get_letohs( tvb, offset+6 );
- proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
+ if ( request_key )
+ {
+ request_key->type = EPDT_CONNECTED_TRANSPORT;
+ request_key->data.connected_transport.connid = enip_get_connid( pinfo, request_key, tvb_get_letohl( tvb, offset+6 ) );
+ }
+ /* Add Connection identifier */
+ proto_tree_add_text( item_tree, tvb, offset+6, 4, "Connection Identifier: 0x%08X", tvb_get_letohl( tvb, offset + 6 ) );
- /* Socket Address */
- sockaddr_item = proto_tree_add_text( item_tree, tvb, offset+8, 16, "Socket Address");
- sockaddr_tree = proto_item_add_subtree( sockaddr_item, ett_sockadd );
+ /* Add Connection ID to Info col */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ ", CONID: 0x%08X",
+ tvb_get_letohl( tvb, offset+6 ) );
+ }
- /* Socket address struct - sin_family */
- proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily,
- tvb, offset+8, 2, FALSE );
+ break;
- /* Socket address struct - sin_port */
- proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport,
- tvb, offset+10, 2, FALSE );
+ case UNCONNECTED_MSG:
+ request_info = NULL;
+ if ( request_key )
+ {
+ request_key->type = EPDT_UNCONNECTED;
+ request_info = enip_match_request( pinfo, tree, request_key );
+ }
- /* Socket address struct - sin_address */
- proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr,
- tvb, offset+12, 4, FALSE );
+ /* Call dissector for interface */
+ next_tvb = tvb_new_subset( tvb, offset+6, item_length, item_length );
+ p_add_proto_data(pinfo->fd, proto_enip, request_info);
+ if( tvb_length_remaining(next_tvb, 0) == 0 || !dissector_try_port(subdissector_srrd_table, ifacehndl, next_tvb, pinfo, g_tree) )
+ {
+ /* Show the undissected payload */
+ if( tvb_length_remaining(tvb, offset) > 0 )
+ call_dissector( data_handle, next_tvb, pinfo, g_tree );
+ }
+ p_remove_proto_data(pinfo->fd, proto_enip);
- /* Socket address struct - sin_zero */
- proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero,
- tvb, offset+16, 8, FALSE );
+ break;
- /* Vendor ID */
- proto_tree_add_item(item_tree, hf_enip_lir_vendor,
- tvb, offset+24, 2, TRUE );
+ case CONNECTION_TRANSPORT:
- /* Device Type */
- proto_tree_add_item(item_tree, hf_enip_lir_devtype,
- tvb, offset+26, 2, TRUE );
+ if( command == SEND_UNIT_DATA )
+ {
+ request_info = NULL;
+
+ if ( request_key )
+ {
+ request_key->type = EPDT_CONNECTED_TRANSPORT;
+ request_key->data.connected_transport.sequence = tvb_get_letohs( tvb, offset+6 );
+ request_info = enip_match_request( pinfo, tree, request_key );
+ }
+
+ /*
+ ** If the encapsulation service is SendUnit Data, this is a
+ ** encapsulated connected message
+ */
+
+ /* Add sequence count ( Transport Class 1,2,3 )*/
+ proto_tree_add_text( item_tree, tvb, offset+6, 2, "Sequence Count: 0x%04X", tvb_get_letohs( tvb, offset+6 ) );
+
+ /* Call dissector for interface */
+ next_tvb = tvb_new_subset (tvb, offset+8, item_length-2, item_length-2);
+ p_add_proto_data(pinfo->fd, proto_enip, request_info);
+ if( tvb_length_remaining(next_tvb, 0) == 0 || !dissector_try_port(subdissector_sud_table, ifacehndl, next_tvb, pinfo, g_tree) )
+ {
+ /* Show the undissected payload */
+ if( tvb_length_remaining(tvb, offset) > 0 )
+ call_dissector( data_handle, next_tvb, pinfo, g_tree );
+ }
+ p_remove_proto_data(pinfo->fd, proto_enip);
+ }
+ else
+ {
+ /* Display data */
+ add_byte_array_text_to_proto_tree( item_tree, tvb, offset+6, item_length, "Data: " );
- /* Product Code */
- proto_tree_add_item(item_tree, hf_enip_lir_prodcode,
- tvb, offset+28, 2, TRUE );
+ } /* End of if send unit data */
- /* Revision */
- temp_data = tvb_get_letohs( tvb, offset+30 );
- proto_tree_add_text( item_tree, tvb, offset+30, 2, "Revision: %d.%02d", temp_data & 0xFF, ( temp_data & 0xFF00 ) >> 8 );
+ break;
- /* Status */
- proto_tree_add_item(item_tree, hf_enip_lir_status,
- tvb, offset+32, 2, TRUE );
- /* Serial Number */
- proto_tree_add_item(item_tree, hf_enip_lir_serial,
- tvb, offset+34, 4, TRUE );
+ case LIST_IDENTITY_RESP:
- /* Product Name Length */
- name_length = tvb_get_guint8( tvb, offset+38 );
- proto_tree_add_text( item_tree, tvb, offset+38, 1, "Product Name Length: %d", name_length );
+ /* Encapsulation version */
+ temp_data = tvb_get_letohs( tvb, offset+6 );
+ proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
- /* Product Name */
- proto_tree_add_item(item_tree, hf_enip_lir_name,
- tvb, offset+39, name_length, TRUE );
+ /* Socket Address */
+ sockaddr_item = proto_tree_add_text( item_tree, tvb, offset+8, 16, "Socket Address");
+ sockaddr_tree = proto_item_add_subtree( sockaddr_item, ett_sockadd );
- /* Append product name to info column */
- if(check_col(pinfo->cinfo, COL_INFO))
- {
- col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
- tvb_format_text(tvb, offset+39, name_length));
- }
+ /* Socket address struct - sin_family */
+ proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily,
+ tvb, offset+8, 2, FALSE );
- /* State */
- proto_tree_add_item(item_tree, hf_enip_lir_state,
- tvb, offset+name_length+39, 1, TRUE );
- break;
+ /* Socket address struct - sin_port */
+ proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport,
+ tvb, offset+10, 2, FALSE );
+ /* Socket address struct - sin_address */
+ proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr,
+ tvb, offset+12, 4, FALSE );
- case SOCK_ADR_INFO_OT:
- case SOCK_ADR_INFO_TO:
+ /* Socket address struct - sin_zero */
+ proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero,
+ tvb, offset+16, 8, FALSE );
- /* Socket address struct - sin_family */
- proto_tree_add_item(item_tree, hf_enip_lir_sinfamily,
- tvb, offset+6, 2, FALSE );
+ /* Vendor ID */
+ proto_tree_add_item(item_tree, hf_enip_lir_vendor,
+ tvb, offset+24, 2, TRUE );
- /* Socket address struct - sin_port */
- proto_tree_add_item(item_tree, hf_enip_lir_sinport,
- tvb, offset+8, 2, FALSE );
+ /* Device Type */
+ proto_tree_add_item(item_tree, hf_enip_lir_devtype,
+ tvb, offset+26, 2, TRUE );
- /* Socket address struct - sin_address */
- proto_tree_add_item(item_tree, hf_enip_lir_sinaddr,
- tvb, offset+10, 4, FALSE );
+ /* Product Code */
+ proto_tree_add_item(item_tree, hf_enip_lir_prodcode,
+ tvb, offset+28, 2, TRUE );
- /* Socket address struct - sin_zero */
- proto_tree_add_item( item_tree, hf_enip_lir_sinzero,
- tvb, offset+14, 8, FALSE );
- break;
+ /* Revision */
+ temp_data = tvb_get_letohs( tvb, offset+30 );
+ proto_tree_add_text( item_tree, tvb, offset+30, 2, "Revision: %d.%02d", temp_data & 0xFF, ( temp_data & 0xFF00 ) >> 8 );
+ /* Status */
+ proto_tree_add_item(item_tree, hf_enip_lir_status,
+ tvb, offset+32, 2, TRUE );
- case SEQ_ADDRESS:
- proto_tree_add_item(item_tree, hf_enip_cpf_sai_connid,
- tvb, offset+6, 4, TRUE );
+ /* Serial Number */
+ proto_tree_add_item(item_tree, hf_enip_lir_serial,
+ tvb, offset+34, 4, TRUE );
- proto_tree_add_item(item_tree, hf_enip_cpf_sai_seqnum,
- tvb, offset+10, 4, TRUE );
+ /* Product Name Length */
+ name_length = tvb_get_guint8( tvb, offset+38 );
+ proto_tree_add_text( item_tree, tvb, offset+38, 1, "Product Name Length: %d", name_length );
- /* Add info to column */
+ /* Product Name */
+ proto_tree_add_item(item_tree, hf_enip_lir_name,
+ tvb, offset+39, name_length, TRUE );
- if(check_col(pinfo->cinfo, COL_INFO))
- {
- col_clear(pinfo->cinfo, COL_INFO);
+ /* Append product name to info column */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
+ tvb_format_text(tvb, offset+39, name_length));
+ }
- col_add_fstr(pinfo->cinfo, COL_INFO,
- "Connection: ID=0x%08X, SEQ=%010d",
- tvb_get_letohl( tvb, offset+6 ),
- tvb_get_letohl( tvb, offset+10 ) );
- }
+ /* State */
+ proto_tree_add_item(item_tree, hf_enip_lir_state,
+ tvb, offset+name_length+39, 1, TRUE );
+ break;
- break;
- case LIST_SERVICES_RESP:
+ case SOCK_ADR_INFO_OT:
+ case SOCK_ADR_INFO_TO:
- /* Encapsulation version */
- temp_data = tvb_get_letohs( tvb, offset+6 );
- proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
+ /* Socket address struct - sin_family */
+ proto_tree_add_item(item_tree, hf_enip_lir_sinfamily,
+ tvb, offset+6, 2, FALSE );
- /* Capability flags */
- temp_data = tvb_get_letohs( tvb, offset+8 );
- temp_item = proto_tree_add_text(item_tree, tvb, offset+8, 2, "Capability Flags: 0x%04X", temp_data );
- temp_tree = proto_item_add_subtree(temp_item, ett_lsrcf);
+ /* Socket address struct - sin_port */
+ proto_tree_add_item(item_tree, hf_enip_lir_sinport,
+ tvb, offset+8, 2, FALSE );
- proto_tree_add_item(temp_tree, hf_enip_lsr_tcp,
- tvb, offset+8, 2, TRUE );
- proto_tree_add_item(temp_tree, hf_enip_lsr_udp,
- tvb, offset+8, 2, TRUE );
+ /* Socket address struct - sin_address */
+ proto_tree_add_item(item_tree, hf_enip_lir_sinaddr,
+ tvb, offset+10, 4, FALSE );
- /* Name of service */
- temp_item = proto_tree_add_text( item_tree, tvb, offset+10, 16, "Name of Service: %s",
- tvb_format_stringzpad(tvb, offset+10, 16) );
+ /* Socket address struct - sin_zero */
+ proto_tree_add_item( item_tree, hf_enip_lir_sinzero,
+ tvb, offset+14, 8, FALSE );
+ break;
+
+
+ case SEQ_ADDRESS:
+ proto_tree_add_item(item_tree, hf_enip_cpf_sai_connid,
+ tvb, offset+6, 4, TRUE );
+
+ proto_tree_add_item(item_tree, hf_enip_cpf_sai_seqnum,
+ tvb, offset+10, 4, TRUE );
+
+ /* Add info to column */
+
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_clear(pinfo->cinfo, COL_INFO);
- /* Append service name to info column */
- if(check_col(pinfo->cinfo, COL_INFO))
- {
- col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
- tvb_format_stringzpad(tvb, offset+10, 16) );
- }
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "Connection: ID=0x%08X, SEQ=%010d",
+ tvb_get_letohl( tvb, offset+6 ),
+ tvb_get_letohl( tvb, offset+10 ) );
+ }
+
+ break;
- break;
+ case LIST_SERVICES_RESP:
+ /* Encapsulation version */
+ temp_data = tvb_get_letohs( tvb, offset+6 );
+ proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
- default:
- if (tvb_length_remaining(tvb, offset+6) > 0)
- {
- next_tvb = tvb_new_subset(tvb, offset+6, item_length, item_length);
- call_dissector(data_handle, next_tvb, pinfo, item_tree);
- }
- break;
+ /* Capability flags */
+ temp_data = tvb_get_letohs( tvb, offset+8 );
+ temp_item = proto_tree_add_text(item_tree, tvb, offset+8, 2, "Capability Flags: 0x%04X", temp_data );
+ temp_tree = proto_item_add_subtree(temp_item, ett_lsrcf);
- } /* end of switch( item type ) */
+ proto_tree_add_item(temp_tree, hf_enip_lsr_tcp,
+ tvb, offset+8, 2, TRUE );
+ proto_tree_add_item(temp_tree, hf_enip_lsr_udp,
+ tvb, offset+8, 2, TRUE );
- } /* end of if( item length ) */
+ /* Name of service */
+ temp_item = proto_tree_add_text( item_tree, tvb, offset+10, 16, "Name of Service: %s",
+ tvb_format_stringzpad(tvb, offset+10, 16) );
- offset = offset + item_length + 4;
+ /* Append service name to info column */
+ if(check_col(pinfo->cinfo, COL_INFO))
+ {
+ col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
+ tvb_format_stringzpad(tvb, offset+10, 16) );
+ }
+
+ break;
- } /* end of while( item count ) */
+
+ default:
+
+ add_byte_array_text_to_proto_tree( item_tree, tvb, offset+6, item_length, "Data: " );
+ break;
+
+ } /* end of switch( item type ) */
+
+ } /* end of if( item length ) */
+
+ offset = offset + item_length + 4;
+
+ } /* end of while( item count ) */
} /* end of dissect_cpf() */
@@ -459,17 +903,17 @@ dissect_cpf( int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i
static int
classify_packet(packet_info *pinfo)
{
- /* see if nature of packets can be derived from src/dst ports */
- /* if so, return as found */
- if ( ( ENIP_ENCAP_PORT == pinfo->srcport && ENIP_ENCAP_PORT != pinfo->destport ) ||
- ( ENIP_ENCAP_PORT != pinfo->srcport && ENIP_ENCAP_PORT == pinfo->destport ) ) {
- if ( ENIP_ENCAP_PORT == pinfo->srcport )
- return RESPONSE_PACKET;
- else if ( ENIP_ENCAP_PORT == pinfo->destport )
- return REQUEST_PACKET;
- }
- /* else, cannot classify */
- return CANNOT_CLASSIFY;
+ /* see if nature of packets can be derived from src/dst ports */
+ /* if so, return as found */
+ if ( ( ENIP_ENCAP_PORT == pinfo->srcport && ENIP_ENCAP_PORT != pinfo->destport ) ||
+ ( ENIP_ENCAP_PORT != pinfo->srcport && ENIP_ENCAP_PORT == pinfo->destport ) ) {
+ if ( ENIP_ENCAP_PORT == pinfo->srcport )
+ return ENIP_RESPONSE_PACKET;
+ else if ( ENIP_ENCAP_PORT == pinfo->destport )
+ return ENIP_REQUEST_PACKET;
+ }
+ /* else, cannot classify */
+ return ENIP_CANNOT_CLASSIFY;
}
static guint
@@ -493,17 +937,16 @@ get_enip_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
static void
dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- int packet_type;
+ int packet_type;
guint16 encap_cmd, encap_data_length;
const char *pkt_type_str = "";
guint32 ifacehndl;
- tvbuff_t *next_tvb;
+ enip_request_key_t request_key;
+ conversation_t *conversation;
/* Set up structures needed to add the protocol subtree and manage it */
proto_item *ti, *encaph, *csf;
- proto_tree *enip_tree = NULL;
- proto_tree *header_tree = NULL;
- proto_tree *csftree;
+ proto_tree *enip_tree, *header_tree = NULL, *csftree;
/* Make entries in Protocol column and Info column on summary display */
if (check_col(pinfo->cinfo, COL_PROTOCOL))
@@ -512,19 +955,18 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
col_clear(pinfo->cinfo, COL_INFO);
encap_cmd = tvb_get_letohs( tvb, 0 );
- encap_data_length = tvb_get_letohs( tvb, 2 );
+
+ packet_type = classify_packet(pinfo);
if( check_col(pinfo->cinfo, COL_INFO) )
{
- packet_type = classify_packet(pinfo);
-
switch ( packet_type )
{
- case REQUEST_PACKET:
+ case ENIP_REQUEST_PACKET:
pkt_type_str="Req";
break;
- case RESPONSE_PACKET:
+ case ENIP_RESPONSE_PACKET:
pkt_type_str="Rsp";
break;
@@ -534,16 +976,48 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* Add service and request/response to info column */
col_add_fstr(pinfo->cinfo, COL_INFO,
- "%s (%s)",
+ "%s (%s)",
val_to_str(encap_cmd, encap_cmd_vals, "Unknown (0x%04x)"),
pkt_type_str );
+
} /* end of if( col exists ) */
+
+ /*
+ * We need to track some state for this protocol on a per conversation
+ * basis so we can do neat things like request/response tracking
+ */
+ /*
+ * Do we have a conversation for this connection?
+ */
+ conversation = find_conversation(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst,
+ pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ if (conversation == NULL) {
+ /* We don't yet have a conversation, so create one. */
+ conversation = conversation_new(pinfo->fd->num,
+ &pinfo->src, &pinfo->dst,
+ pinfo->ptype,
+ pinfo->srcport, pinfo->destport, 0);
+ }
+ /*
+ * No. Attach that information to the conversation, and add
+ * it to the list of information structures later before dissection.
+ */
+ memset( &request_key, 0, sizeof(enip_request_key_t) );
+ request_key.requesttype = packet_type;
+ request_key.type = EPDT_UNKNOWN;
+ request_key.session_handle = tvb_get_letohl( tvb, 4 );
+ request_key.sender_context = tvb_get_letoh64( tvb, 12 );
+ request_key.conversation = conversation->index;
+
+ encap_data_length = tvb_get_letohs( tvb, 2 );
+ enip_tree = NULL;
/* In the interest of speed, if "tree" is NULL, don't do any work not
necessary to generate protocol tree items. */
if (tree) {
-
/* create display subtree for the protocol */
ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE);
@@ -556,6 +1030,7 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
/* Add EtherNet/IP encapsulation header */
proto_tree_add_item( header_tree, hf_enip_command, tvb, 0, 2, TRUE );
+ encap_data_length = tvb_get_letohs( tvb, 2 );
proto_tree_add_text( header_tree, tvb, 2, 2, "Length: %u", encap_data_length );
proto_tree_add_item( header_tree, hf_enip_session, tvb, 4, 4, TRUE );
@@ -567,100 +1042,95 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
proto_item_append_text( ti, ", Session: 0x%08X, %s", tvb_get_letohl( tvb, 4 ),
val_to_str( encap_cmd, encap_cmd_vals, "Unknown (0x%04x)" ) );
- } /* end of if (tree) */
-
- /*
- ** For some commands we want to add some info to the info column
- */
+ /*
+ ** For some commands we want to add some info to the info column
+ */
- if( check_col( pinfo->cinfo, COL_INFO ) )
- {
-
- switch( encap_cmd )
+ if( check_col( pinfo->cinfo, COL_INFO ) )
{
- case REGISTER_SESSION:
- case UNREGISTER_SESSION:
- col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X",
- tvb_get_letohl( tvb, 4 ) );
- } /* end of switch() */
+ switch( encap_cmd )
+ {
+ case REGISTER_SESSION:
+ case UNREGISTER_SESSION:
+ col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X",
+ tvb_get_letohl( tvb, 4 ) );
- } /* end of id info column */
+ } /* end of switch() */
- /* Command specific data - create tree */
- if( encap_data_length )
- {
- /* The packet have some command specific data, buid a sub tree for it */
+ } /* end of id info column */
+ } /* end of tree */
- csf = proto_tree_add_text( enip_tree, tvb, 24, encap_data_length,
- "Command Specific Data");
+ /* Command specific data - create tree */
+ if( encap_data_length )
+ {
+ /* The packet have some command specific data, buid a sub tree for it */
- csftree = proto_item_add_subtree(csf, ett_command_tree);
+ csf = proto_tree_add_text( enip_tree, tvb, 24, encap_data_length,
+ "Command Specific Data");
- switch( encap_cmd )
- {
- case NOP:
- break;
+ csftree = proto_item_add_subtree(csf, ett_command_tree);
- case LIST_SERVICES:
- dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 );
- break;
+ switch( encap_cmd )
+ {
+ case NOP:
+ break;
- case LIST_IDENTITY:
- dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 );
- break;
+ case LIST_SERVICES:
+ dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
+ break;
- case LIST_INTERFACES:
- dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 );
- break;
+ case LIST_IDENTITY:
+ dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
+ break;
- case REGISTER_SESSION:
- proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X",
- tvb_get_letohs( tvb, 24 ) );
+ case LIST_INTERFACES:
+ dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
+ break;
- proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X",
- tvb_get_letohs( tvb, 26 ) );
+ case REGISTER_SESSION:
+ proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X",
+ tvb_get_letohs( tvb, 24 ) );
- break;
+ proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X",
+ tvb_get_letohs( tvb, 26 ) );
- case UNREGISTER_SESSION:
- break;
+ break;
- case SEND_RR_DATA:
- proto_tree_add_item(csftree, hf_enip_srrd_ifacehnd, tvb, 24, 4, TRUE);
+ case UNREGISTER_SESSION:
+ break;
- proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
- tvb_get_letohs( tvb, 28 ) );
+ case SEND_RR_DATA:
+ proto_tree_add_item(csftree, hf_enip_srrd_ifacehnd, tvb, 24, 4, TRUE);
- ifacehndl = tvb_get_letohl( tvb, 24 );
- dissect_cpf( encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
- break;
+ proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
+ tvb_get_letohs( tvb, 28 ) );
- case SEND_UNIT_DATA:
- proto_tree_add_item(csftree, hf_enip_sud_ifacehnd, tvb, 24, 4, TRUE);
+ ifacehndl = tvb_get_letohl( tvb, 24 );
+ dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
+ break;
- proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
- tvb_get_letohs( tvb, 28 ) );
+ case SEND_UNIT_DATA:
+ proto_tree_add_item(csftree, hf_enip_sud_ifacehnd, tvb, 24, 4, TRUE);
- ifacehndl = tvb_get_letohl( tvb, 24 );
- dissect_cpf( encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
- break;
+ proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
+ tvb_get_letohs( tvb, 28 ) );
- case INDICATE_STATUS:
- case CANCEL:
- default:
- /* Can not decode - Just show the data */
- if (tvb_length_remaining(tvb, 24) > 0)
- {
- next_tvb = tvb_new_subset(tvb, 24, encap_data_length, encap_data_length);
- call_dissector(data_handle, next_tvb, pinfo, header_tree);
- }
- break;
+ ifacehndl = tvb_get_letohl( tvb, 24 );
+ dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
+ break;
- } /* end of switch() */
+ case INDICATE_STATUS:
+ case CANCEL:
+ default:
+
+ /* Can not decode - Just show the data */
+ add_byte_array_text_to_proto_tree( header_tree, tvb, 24, encap_data_length, "Encap Data: " );
+ break;
- } /* end of if( encapsulated data ) */
+ } /* end of switch() */
+ } /* end of if( encapsulated data ) */
} /* end of dissect_enip_pdu() */
static int
@@ -671,13 +1141,13 @@ dissect_enip_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
g_tree = tree;
/* An ENIP packet is at least 4 bytes long - we need the command type. */
- if (tvb_length(tvb) < 4)
+ if (!tvb_bytes_exist(tvb, 0, 4))
return 0;
/* Get the command type and see if it's valid. */
encap_cmd = tvb_get_letohs( tvb, 0 );
if (match_strval(encap_cmd, encap_cmd_vals) == NULL)
- return 0; /* not a known command */
+ return 0; /* not a known command */
dissect_enip_pdu(tvb, pinfo, tree);
return tvb_length(tvb);
@@ -691,55 +1161,45 @@ dissect_enip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
g_tree = tree;
/* An ENIP packet is at least 4 bytes long - we need the command type. */
- if (tvb_length(tvb) < 4)
+ if (!tvb_bytes_exist(tvb, 0, 4))
return 0;
/* Get the command type and see if it's valid. */
encap_cmd = tvb_get_letohs( tvb, 0 );
if (match_strval(encap_cmd, encap_cmd_vals) == NULL)
- return 0; /* not a known command */
+ return 0; /* not a known command */
tcp_dissect_pdus(tvb, pinfo, tree, enip_desegment, 4,
- get_enip_pdu_len, dissect_enip_pdu);
+ get_enip_pdu_len, dissect_enip_pdu);
return tvb_length(tvb);
}
/* Code to actually dissect the io packets*/
-static int
+static void
dissect_enipio(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
/* Set up structures needed to add the protocol subtree and manage it */
- proto_item *ti;
- proto_tree *enip_tree = NULL;
- guint16 type_id;
-
- g_tree = tree;
+ proto_item *ti;
+ proto_tree *enip_tree;
- /* Verify that the packet belongs to this dissector */
- if (tvb_length(tvb) < 4)
- return 0;
-
- type_id = tvb_get_letohs( tvb, 2 );
- if (match_strval(type_id, cdf_type_vals) == NULL)
- return 0; /* not a known type id */
+ g_tree = tree;
/* Make entries in Protocol column and Info column on summary display */
- if (check_col(pinfo->cinfo, COL_PROTOCOL))
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "ENIP");
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "ENIP");
/* In the interest of speed, if "tree" is NULL, don't do any work not
necessary to generate protocol tree items. */
- if (tree)
- {
- /* create display subtree for the protocol */
- ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE);
+ if (tree)
+ {
+ /* create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE);
- enip_tree = proto_item_add_subtree(ti, ett_enip);
- }
+ enip_tree = proto_item_add_subtree(ti, ett_enip);
- dissect_cpf( 0xFFFF, tvb, pinfo, enip_tree, 0, 0 );
+ dissect_cpf( NULL, 0xFFFF, tvb, pinfo, enip_tree, 0, 0 );
+ }
- return tvb_length(tvb);
} /* end of dissect_enipio() */
@@ -753,161 +1213,178 @@ void
proto_register_enip(void)
{
/* Setup list of header fields */
- static hf_register_info hf[] = {
- { &hf_enip_command,
- { "Command", "enip.command",
- FT_UINT16, BASE_HEX, VALS(encap_cmd_vals), 0,
- "Encapsulation command", HFILL }
- },
- { &hf_enip_session,
- { "Session Handle", "enip.session",
- FT_UINT32, BASE_HEX, NULL, 0,
- "Session identification", HFILL }
- },
- { &hf_enip_status,
- { "Status", "enip.status",
- FT_UINT32, BASE_HEX, VALS(encap_status_vals), 0,
- "Status code", HFILL }
- },
- { &hf_enip_sendercontex,
- { "Sender Context", "enip.context",
- FT_BYTES, BASE_NONE, NULL, 0,
- "Information pertinent to the sender", HFILL }
- },
- { &hf_enip_options,
- { "Options", "enip.options",
- FT_UINT32, BASE_HEX, NULL, 0,
- "Options flags", HFILL }
- },
- { &hf_enip_lsr_tcp,
- { "Supports CIP Encapsulation via TCP", "enip.lsr.capaflags.tcp",
- FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0020,
- "ListServices Reply: Supports CIP Encapsulation via TCP", HFILL }
- },
- { &hf_enip_lsr_udp,
- { "Supports CIP Class 0 or 1 via UDP", "enip.lsr.capaflags.udp",
- FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0100,
- "ListServices Reply: Supports CIP Class 0 or 1 via UDP", HFILL }
- },
- /* Send Request/Reply Data */
- { &hf_enip_srrd_ifacehnd,
- { "Interface Handle", "enip.srrd.iface",
- FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
- "SendRRData: Interface handle", HFILL }
- },
- /* Send Unit Data */
- { &hf_enip_sud_ifacehnd,
- { "Interface Handle", "enip.sud.iface",
- FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
- "SendUnitData: Interface handle", HFILL }
- },
- /* List identity reply */
+ static hf_register_info hf[] = {
+ { &hf_enip_command,
+ { "Command", "enip.command",
+ FT_UINT16, BASE_HEX, VALS(encap_cmd_vals), 0,
+ "Encapsulation command", HFILL }
+ },
+ { &hf_enip_session,
+ { "Session Handle", "enip.session",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Session identification", HFILL }
+ },
+ { &hf_enip_status,
+ { "Status", "enip.status",
+ FT_UINT32, BASE_HEX, VALS(encap_status_vals), 0,
+ "Status code", HFILL }
+ },
+ { &hf_enip_sendercontex,
+ { "Sender Context", "enip.context",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ "Information pertient to the sender", HFILL }
+ },
+ { &hf_enip_options,
+ { "Options", "enip.options",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Options flags", HFILL }
+ },
+ { &hf_enip_lsr_tcp,
+ { "Supports CIP Encapsulation via TCP", "enip.lsr.capaflags.tcp",
+ FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0020,
+ "ListServices Reply: Supports CIP Encapsulation via TCP", HFILL }
+ },
+ { &hf_enip_lsr_udp,
+ { "Supports CIP Class 0 or 1 via UDP", "enip.lsr.capaflags.udp",
+ FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0100,
+ "ListServices Reply: Supports CIP Class 0 or 1 via UDP", HFILL }
+ },
+ /* Send Request/Reply Data */
+ { &hf_enip_srrd_ifacehnd,
+ { "Interface Handle", "enip.srrd.iface",
+ FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
+ "SendRRData: Interface handle", HFILL }
+ },
+ /* Send Unit Data */
+ { &hf_enip_sud_ifacehnd,
+ { "Interface Handle", "enip.sud.iface",
+ FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
+ "SendUnitData: Interface handle", HFILL }
+ },
+ /* List identity reply */
{ &hf_enip_lir_sinfamily,
- { "sin_family", "enip.lir.sa.sinfamily",
- FT_UINT16, BASE_DEC, NULL, 0,
- "ListIdentity Reply: Socket Address.Sin Family", HFILL }
- },
+ { "sin_family", "enip.lir.sa.sinfamily",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ "ListIdentity Reply: Socket Address.Sin Family", HFILL }
+ },
{ &hf_enip_lir_sinport,
- { "sin_port", "enip.lir.sa.sinport",
- FT_UINT16, BASE_DEC, NULL, 0,
- "ListIdentity Reply: Socket Address.Sin Port", HFILL }
- },
+ { "sin_port", "enip.lir.sa.sinport",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ "ListIdentity Reply: Socket Address.Sin Port", HFILL }
+ },
{ &hf_enip_lir_sinaddr,
- { "sin_addr", "enip.lir.sa.sinaddr",
- FT_IPv4, BASE_NONE, NULL, 0,
- "ListIdentity Reply: Socket Address.Sin Addr", HFILL }
- },
+ { "sin_addr", "enip.lir.sa.sinaddr",
+ FT_IPv4, BASE_HEX, NULL, 0,
+ "ListIdentity Reply: Socket Address.Sin Addr", HFILL }
+ },
{ &hf_enip_lir_sinzero,
- { "sin_zero", "enip.lir.sa.sinzero",
- FT_BYTES, BASE_NONE, NULL, 0,
- "ListIdentity Reply: Socket Address.Sin Zero", HFILL }
- },
- { &hf_enip_lir_vendor,
- { "Vendor ID", "enip.lir.vendor",
- FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
- "ListIdentity Reply: Vendor ID", HFILL }
- },
+ { "sin_zero", "enip.lir.sa.sinzero",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ "ListIdentity Reply: Socket Address.Sin Zero", HFILL }
+ },
+ { &hf_enip_lir_vendor,
+ { "Vendor ID", "enip.lir.vendor",
+ FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
+ "ListIdentity Reply: Vendor ID", HFILL }
+ },
{ &hf_enip_lir_devtype,
- { "Device Type", "enip.lir.devtype",
- FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
- "ListIdentity Reply: Device Type", HFILL }
- },
+ { "Device Type", "enip.lir.devtype",
+ FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
+ "ListIdentity Reply: Device Type", HFILL }
+ },
{ &hf_enip_lir_prodcode,
- { "Product Code", "enip.lir.prodcode",
- FT_UINT16, BASE_DEC, NULL, 0,
- "ListIdentity Reply: Product Code", HFILL }
- },
+ { "Product Code", "enip.lir.prodcode",
+ FT_UINT16, BASE_DEC, NULL, 0,
+ "ListIdentity Reply: Product Code", HFILL }
+ },
{ &hf_enip_lir_status,
- { "Status", "enip.lir.status",
- FT_UINT16, BASE_HEX, NULL, 0,
- "ListIdentity Reply: Status", HFILL }
- },
+ { "Status", "enip.lir.status",
+ FT_UINT16, BASE_HEX, NULL, 0,
+ "ListIdentity Reply: Status", HFILL }
+ },
{ &hf_enip_lir_serial,
- { "Serial Number", "enip.lir.serial",
- FT_UINT32, BASE_HEX, NULL, 0,
- "ListIdentity Reply: Serial Number", HFILL }
- },
+ { "Serial Number", "enip.lir.serial",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "ListIdentity Reply: Serial Number", HFILL }
+ },
{ &hf_enip_lir_name,
- { "Product Name", "enip.lir.name",
- FT_STRING, BASE_NONE, NULL, 0,
- "ListIdentity Reply: Product Name", HFILL }
- },
+ { "Product Name", "enip.lir.name",
+ FT_STRING, BASE_NONE, NULL, 0,
+ "ListIdentity Reply: Product Name", HFILL }
+ },
{ &hf_enip_lir_state,
- { "State", "enip.lir.state",
- FT_UINT8, BASE_HEX, NULL, 0,
- "ListIdentity Reply: State", HFILL }
- },
- /* Common Packet Format */
- { &hf_enip_cpf_typeid,
- { "Type ID", "enip.cpf.typeid",
- FT_UINT16, BASE_HEX, VALS(cdf_type_vals), 0,
- "Common Packet Format: Type of encapsulated item", HFILL }
- },
- /* Sequenced Address Type */
+ { "State", "enip.lir.state",
+ FT_UINT8, BASE_HEX, NULL, 0,
+ "ListIdentity Reply: State", HFILL }
+ },
+ /* Common Packet Format */
+ { &hf_enip_cpf_typeid,
+ { "Type ID", "enip.cpf.typeid",
+ FT_UINT16, BASE_HEX, VALS(cdf_type_vals), 0,
+ "Common Packet Format: Type of encapsulated item", HFILL }
+ },
+ /* Sequenced Address Type */
{ &hf_enip_cpf_sai_connid,
- { "Connection ID", "enip.cpf.sai.connid",
- FT_UINT32, BASE_HEX, NULL, 0,
- "Common Packet Format: Sequenced Address Item, Connection Identifier", HFILL }
- },
+ { "Connection ID", "enip.cpf.sai.connid",
+ FT_UINT32, BASE_HEX, NULL, 0,
+ "Common Packet Format: Sequenced Address Item, Connection Identifier", HFILL }
+ },
{ &hf_enip_cpf_sai_seqnum,
- { "Sequence Number", "enip.cpf.sai.seq",
- FT_UINT32, BASE_DEC, NULL, 0,
- "Common Packet Format: Sequenced Address Item, Sequence Number", HFILL }
- }
+ { "Sequence Number", "enip.cpf.sai.seq",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ "Common Packet Format: Sequenced Address Item, Sequence Number", HFILL }
+ },
+ /* Request/Response Matching */
+ { &hf_enip_response_in,
+ { "Response In", "enip.response_in",
+ FT_FRAMENUM, BASE_DEC, NULL, 0x0,
+ "The response to this ENIP request is in this frame", HFILL }
+ },
+ { &hf_enip_response_to,
+ { "Request In", "enip.response_to",
+ FT_FRAMENUM, BASE_DEC, NULL, 0x0,
+ "This is a response to the ENIP request in this frame", HFILL }
+ },
+ { &hf_enip_time,
+ { "Time", "enip.time",
+ FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+ "The time between the Call and the Reply", HFILL }
+ }
};
/* Setup protocol subtree array */
- static gint *ett[] = {
- &ett_enip,
- &ett_count_tree,
- &ett_type_tree,
- &ett_command_tree,
- &ett_sockadd,
- &ett_lsrcf,
- };
- module_t *enip_module;
+ static gint *ett[] = {
+ &ett_enip,
+ &ett_count_tree,
+ &ett_type_tree,
+ &ett_command_tree,
+ &ett_sockadd,
+ &ett_lsrcf,
+ };
+ module_t *enip_module;
/* Register the protocol name and description */
- proto_enip = proto_register_protocol("EtherNet/IP (Industrial Protocol)",
- "ENIP", "enip");
+ proto_enip = proto_register_protocol("EtherNet/IP (Industrial Protocol)",
+ "ENIP", "enip");
/* Required function calls to register the header fields and subtrees used */
- proto_register_field_array(proto_enip, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
+ proto_register_field_array(proto_enip, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
- enip_module = prefs_register_protocol(proto_enip, NULL);
- prefs_register_bool_preference(enip_module, "desegment",
- "Desegment all EtherNet/IP messages spanning multiple TCP segments",
- "Whether the EtherNet/IP dissector should desegment all messages spanning multiple TCP segments",
- &enip_desegment);
+ enip_module = prefs_register_protocol(proto_enip, NULL);
+ prefs_register_bool_preference(enip_module, "desegment",
+ "Desegment all EtherNet/IP messages spanning multiple TCP segments",
+ "Whether the EtherNet/IP dissector should desegment all messages spanning multiple TCP segments",
+ &enip_desegment);
- subdissector_sud_table = register_dissector_table("enip.sud.iface",
- "SendUnitData.Interface Handle", FT_UINT32, BASE_HEX);
+ subdissector_sud_table = register_dissector_table("enip.sud.iface",
+ "SendUnitData.Interface Handle", FT_UINT32, BASE_HEX);
- subdissector_srrd_table = register_dissector_table("enip.srrd.iface",
- "SendRequestReplyData.Interface Handle", FT_UINT32, BASE_HEX);
+ subdissector_srrd_table = register_dissector_table("enip.srrd.iface",
+ "SendRequestReplyData.Interface Handle", FT_UINT32, BASE_HEX);
+ register_init_routine(&enip_init_protocol);
} /* end of proto_register_enip() */
@@ -918,22 +1395,22 @@ proto_register_enip(void)
void
proto_reg_handoff_enip(void)
{
- dissector_handle_t enip_udp_handle, enip_tcp_handle;
- dissector_handle_t enipio_handle;
+ dissector_handle_t enip_udp_handle, enip_tcp_handle;
+ dissector_handle_t enipio_handle;
- /* Register for EtherNet/IP, using TCP */
- enip_tcp_handle = new_create_dissector_handle(dissect_enip_tcp, proto_enip);
- dissector_add("tcp.port", ENIP_ENCAP_PORT, enip_tcp_handle);
+ /* Register for EtherNet/IP, using TCP */
+ enip_tcp_handle = new_create_dissector_handle(dissect_enip_tcp, proto_enip);
+ dissector_add("tcp.port", ENIP_ENCAP_PORT, enip_tcp_handle);
- /* Register for EtherNet/IP, using UDP */
- enip_udp_handle = new_create_dissector_handle(dissect_enip_udp, proto_enip);
- dissector_add("udp.port", ENIP_ENCAP_PORT, enip_udp_handle);
+ /* Register for EtherNet/IP, using UDP */
+ enip_udp_handle = new_create_dissector_handle(dissect_enip_udp, proto_enip);
+ dissector_add("udp.port", ENIP_ENCAP_PORT, enip_udp_handle);
- /* Register for EtherNet/IP IO data (UDP) */
- enipio_handle = new_create_dissector_handle(dissect_enipio, proto_enip);
- dissector_add("udp.port", ENIP_IO_PORT, enipio_handle);
+ /* Register for EtherNet/IP IO data (UDP) */
+ enipio_handle = create_dissector_handle(dissect_enipio, proto_enip);
+ dissector_add("udp.port", ENIP_IO_PORT, enipio_handle);
- /* Find dissector for data packet */
- data_handle = find_dissector("data");
+ /* Find dissector for data packet */
+ data_handle = find_dissector("data");
} /* end of proto_reg_handoff_enip() */
diff --git a/epan/dissectors/packet-enip.h b/epan/dissectors/packet-enip.h
new file mode 100644
index 0000000000..018ca297ff
--- /dev/null
+++ b/epan/dissectors/packet-enip.h
@@ -0,0 +1,40 @@
+/* packet-enip.h
+ * Routines for EtherNet/IP (Industrial Protocol) dissection
+ * EtherNet/IP Home: www.odva.org
+ *
+ * Conversation data support for CIP
+ * Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
+ * Copyright 2007
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+typedef struct {
+ guint32 req_num, rep_num;
+ nstime_t req_time;
+ void *cip_info;
+} enip_request_info_t;
+
+void enip_open_cip_connection( packet_info *pinfo,
+ guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber,
+ guint32 O2TConnID, guint32 T2OConnID );
+void enip_close_cip_connection( packet_info *pinfo,
+ guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber );
diff --git a/epan/dissectors/packet-glbp.c b/epan/dissectors/packet-glbp.c
index 59e5b92525..4535639527 100644
--- a/epan/dissectors/packet-glbp.c
+++ b/epan/dissectors/packet-glbp.c
@@ -152,6 +152,8 @@ dissect_glbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
col_add_str(pinfo->cinfo, COL_INFO, val_to_str(type2,
glbp_type2_vals, "Type 0x%02x"));
+ length2 = tvb_get_guint8(tvb, 13);
+
if (tree) {
ti = proto_tree_add_item(tree, proto_glbp, tvb, 0, -1, FALSE);
glbp_tree = proto_item_add_subtree(ti, ett_glbp);
@@ -165,7 +167,6 @@ dissect_glbp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
offset += 12;
proto_tree_add_item(glbp_tree, hf_glbp_type2, tvb, 12, 1, FALSE);
proto_tree_add_item(glbp_tree, hf_glbp_length2, tvb, 13, 1, FALSE);
- length2 = tvb_get_guint8(tvb, 13);
switch(type2) {
case 1: /* Hello */