diff options
author | Stig Bjørlykke <stig@bjorlykke.org> | 2009-06-19 05:45:40 +0000 |
---|---|---|
committer | Stig Bjørlykke <stig@bjorlykke.org> | 2009-06-19 05:45:40 +0000 |
commit | b3e10e559aba9f14711ac221255ed59047ba9b01 (patch) | |
tree | 85a4442848d7e6ab7b3cc2c51d98ab86c110522d | |
parent | ea64b282678b562695741d3d51e629aebee0891e (diff) | |
download | wireshark-b3e10e559aba9f14711ac221255ed59047ba9b01.tar.gz |
Moved setting of length2 to make it always set.
svn path=/trunk/; revision=28777
-rw-r--r-- | epan/dissectors/packet-cip.c | 2787 | ||||
-rw-r--r-- | epan/dissectors/packet-cip.h | 73 | ||||
-rw-r--r-- | epan/dissectors/packet-enip.c | 1503 | ||||
-rw-r--r-- | epan/dissectors/packet-enip.h | 40 | ||||
-rw-r--r-- | epan/dissectors/packet-glbp.c | 3 |
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 */ |