/* packet-cip.c * Routines for Common Industrial Protocol (CIP) dissection * CIP Home: www.odva.org * * Copyright 2004 * Magnus Hansson * Joakim Wiberg * * Added support for Connection Configuration Object * ryan wamsley * Copyright 2007 * * Object dependend services based on IOI * Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG * Copyright 2007 * * Improved support for CoCo and CM objects * Michael Mann * Copyright 2011 * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * 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. */ /* &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 #include #include #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; typedef struct cip_simple_request_info { guint32 iClass; guint32 iInstance; guint32 iAttribute; guint32 iMember; } cip_simple_request_info_t; static dissector_handle_t cip_handle; static dissector_handle_t cip_class_generic_handle; static dissector_handle_t cip_class_cm_handle; static dissector_handle_t cip_class_mr_handle; static dissector_handle_t cip_class_cco_handle; /* Initialize the protocol and registered fields */ 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_ot_connid = -1; static int hf_cip_cm_to_connid = -1; static int hf_cip_cm_conn_serial_num = -1; static int hf_cip_cm_orig_serial_num = -1; static int hf_cip_cm_fwo_con_size = -1; static int hf_cip_cm_lfwo_con_size = -1; static int hf_cip_cm_fwo_fixed_var = -1; static int hf_cip_cm_lfwo_fixed_var = -1; static int hf_cip_cm_fwo_prio = -1; static int hf_cip_cm_lfwo_prio = -1; static int hf_cip_cm_fwo_typ = -1; static int hf_cip_cm_lfwo_typ = -1; static int hf_cip_cm_fwo_own = -1; static int hf_cip_cm_lfwo_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_cm_gco_conn = -1; static int hf_cip_cm_gco_coo_conn = -1; static int hf_cip_cm_gco_roo_conn = -1; static int hf_cip_cm_gco_la = -1; static int hf_cip_cco_con_type = -1; static int hf_cip_cco_ot_rtf = -1; static int hf_cip_cco_to_rtf = -1; static int hf_cip_vendor = -1; static int hf_cip_devtype = -1; static int hf_cip_port = -1; static int hf_cip_link_address_byte = -1; static int hf_cip_link_address_string = -1; static int hf_cip_class8 = -1; static int hf_cip_class16 = -1; 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; static int hf_cip_conpoint8 = -1; static int hf_cip_conpoint16 = -1; static int hf_cip_conpoint32 = -1; static int hf_cip_symbol = -1; static int hf_cip_class_rev = -1; static int hf_cip_class_max_inst32 = -1; static int hf_cip_class_num_inst32 = -1; static int hf_cip_reserved8 = -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_iomap = -1; static gint ett_cco_con_status = -1; static gint ett_cco_con_flag = -1; static gint ett_cco_tdi = -1; static gint ett_cco_ncp = -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[] = { GENERIC_SC_LIST { 0, NULL } }; /* Translate function to string - CIP Service codes for CM */ static const value_string cip_sc_vals_cm[] = { GENERIC_SC_LIST /* Some class specific services */ { SC_CM_FWD_CLOSE, "Forward Close" }, { SC_CM_FWD_OPEN, "Forward Open" }, { SC_CM_UNCON_SEND, "Unconnected Send" }, { SC_CM_LARGE_FWD_OPEN, "Large Forward Open" }, { SC_CM_GET_CONN_OWNER, "Get Connection Owner" }, { 0, NULL } }; /* Translate function to string - CIP Service codes for CCO */ static const value_string cip_sc_vals_cco[] = { GENERIC_SC_LIST /* Some class specific services */ { 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, NULL } }; /* Translate function to string - Compatibility */ static const value_string cip_com_bit_vals[] = { { 0, "Bit Cleared" }, { 1, "Bit Set" }, { 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, NULL } }; /* Translate function to string - Connection size fixed or variable */ static const value_string cip_con_fw_vals[] = { { 0, "Fixed" }, { 1, "Variable" }, { 0, NULL } }; /* Translate function to string - Connection owner */ static const value_string cip_con_owner_vals[] = { { 0, "Exclusive" }, { 1, "Redundant" }, { 0, NULL } }; /* Translate function to string - Connection direction */ static const value_string cip_con_dir_vals[] = { { 0, "Client" }, { 1, "Server" }, { 0, NULL } }; /* Translate function to string - Connection type*/ static const value_string cip_con_vals[] = { { 0, "Originator" }, { 1, "Target" }, { 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, NULL } }; /* Translate function to string - Transport class */ static const value_string cip_con_class_vals[] = { { 0, "0" }, { 1, "1" }, { 2, "2" }, { 3, "3" }, { 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 } }; /* 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, NULL } }; /* Translate function to string - Connection Last Action */ static const value_string cip_con_last_action_vals[] = { { 0, "No Owner" }, { 1, "Owner Is Idle Mode" }, { 2, "Owner Is Run Mode" }, { 255, "Implementation not supported" }, { 0, NULL } }; /* Translate function to string - real time transfer format type */ static const value_string cip_con_rtf_vals[] = { { 0, "32-bit Header" }, { 1, "Zero data length idle mode"}, { 2, "Modeless" }, { 3, "Heartbeat" }, { 5, "Safety" }, { 0, NULL } }; /* Translate function to string - CCO change type */ static const value_string cip_cco_change_type_vals[] = { { 0, "Full" }, { 1, "Incremental" }, { 0, NULL } }; /* Translate function to string - CIP General Status codes */ static const value_string cip_gs_vals[] = { { CI_GRC_SUCCESS, "Success" }, { CI_GRC_FAILURE, "Connection failure" }, { CI_GRC_NO_RESOURCE, "Resource unavailable" }, { CI_GRC_BAD_DATA, "Invalid parameter value" }, { CI_GRC_BAD_PATH, "Path segment error" }, { CI_GRC_BAD_CLASS_INSTANCE, "Path destination unknown" }, { CI_GRC_PARTIAL_DATA, "Partial transfer" }, { CI_GRC_CONN_LOST, "Connection lost" }, { CI_GRC_BAD_SERVICE, "Service not supported" }, { CI_GRC_BAD_ATTR_DATA, "Invalid attribute value" }, { CI_GRC_ATTR_LIST_ERROR, "Attribute list error" }, { CI_GRC_ALREADY_IN_MODE, "Already in requested mode/state" }, { CI_GRC_BAD_OBJ_MODE, "Object state conflict" }, { CI_GRC_OBJ_ALREADY_EXISTS, "Object already exists" }, { CI_GRC_ATTR_NOT_SETTABLE, "Attribute not settable" }, { CI_GRC_PERMISSION_DENIED, "Privilege violation" }, { CI_GRC_DEV_IN_WRONG_STATE, "Device state conflict" }, { CI_GRC_REPLY_DATA_TOO_LARGE,"Reply data too large" }, { CI_GRC_FRAGMENT_PRIMITIVE, "Fragmentation of a primitive value" }, { CI_GRC_CONFIG_TOO_SMALL, "Not enough data" }, { CI_GRC_UNDEFINED_ATTR, "Attribute not supported" }, { CI_GRC_CONFIG_TOO_BIG, "Too much data" }, { CI_GRC_OBJ_DOES_NOT_EXIST, "Object does not exist" }, { CI_GRC_NO_FRAGMENTATION, "Service fragmentation sequence not in progress" }, { CI_GRC_DATA_NOT_SAVED, "No stored attribute data" }, { CI_GRC_DATA_WRITE_FAILURE, "Store operation failure" }, { CI_GRC_REQUEST_TOO_LARGE, "Routing failure, request packet too large" }, { CI_GRC_RESPONSE_TOO_LARGE, "Routing failure, response packet too large" }, { CI_GRC_MISSING_LIST_DATA, "Missing attribute list entry data" }, { CI_GRC_INVALID_LIST_STATUS, "Invalid attribute value list" }, { CI_GRC_SERVICE_ERROR, "Embedded service error" }, { CI_GRC_CONN_RELATED_FAILURE,"Vendor specific error" }, { CI_GRC_INVALID_PARAMETER, "Invalid parameter" }, { CI_GRC_WRITE_ONCE_FAILURE, "Write-once value or medium already written" }, { CI_GRC_INVALID_REPLY, "Invalid reply received" }, { CI_GRC_BUFFER_OVERFLOW, "Buffer overflow" }, { CI_GRC_MESSAGE_FORMAT, "Invalid message format" }, { CI_GRC_BAD_KEY_IN_PATH, "Key failure in path" }, { CI_GRC_BAD_PATH_SIZE, "Path size invalid" }, { CI_GRC_UNEXPECTED_ATTR, "Unexpected attribute in list" }, { CI_GRC_INVALID_MEMBER, "Invalid Member ID" }, { CI_GRC_MEMBER_NOT_SETTABLE, "Member not settable" }, { CI_GRC_G2_SERVER_FAILURE, "Group 2 only server general failure" }, { CI_GRC_UNKNOWN_MB_ERROR, "Unknown Modbus error" }, { 0, NULL } }; /* Translate Vendor ID:s */ const value_string cip_vendor_vals[] = { VENDOR_ID_LIST { 0, NULL } }; /* Translate Device Profile:s */ const value_string cip_devtype_vals[] = { { DP_GEN_DEV, "Generic Device" }, { DP_AC_DRIVE, "AC Drive" }, { DP_MOTOR_OVERLOAD, "Motor Overload" }, { DP_LIMIT_SWITCH, "Limit Switch" }, { DP_IND_PROX_SWITCH, "Inductive Proximity Switch" }, { DP_PHOTO_SENSOR, "Photoelectric Sensor" }, { DP_GENP_DISC_IO, "General Purpose Discrete I/O"}, { DP_RESOLVER, "Resolver" }, { DP_COM_ADAPTER, "Communications Adapter" }, { DP_POS_CNT, "Position Controller", }, { DP_DC_DRIVE, "DC Drive" }, { DP_CONTACTOR, "Contactor", }, { DP_MOTOR_STARTER, "Motor Starter", }, { DP_SOFT_START, "Soft Start", }, { DP_HMI, "Human-Machine Interface" }, { DP_MASS_FLOW_CNT, "Mass Flow Controller" }, { DP_PNEUM_VALVE, "Pneumatic Valve" }, { DP_VACUUM_PRES_GAUGE, "Vacuum Pressure Gauge" }, { 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" }, { 0x39, "Safety Supervisor Object" }, { 0x3A, "Safety Validator Object" }, { 0x3B, "Safety Discrete Output Point Object" }, { 0x3C, "Safety Discrete Output Group Object" }, { 0x3D, "Safety Discrete Input Point Object" }, { 0x3E, "Safety Discrete Input Group Object" }, { 0x3F, "Safety Dual Channel Output Object" }, { 0x40, "S-Sensor Calibration Object" }, { 0x41, "Event Log Object" }, { 0x42, "Motion Axis Object" }, { 0x43, "Time Sync Object" }, { 0x44, "Modbus Object" }, { 0x45, "Originator Connection List Object" }, { 0x46, "Modbus Serial Link Object" }, { 0x47, "Device Level Ring (DLR) Object" }, { 0x48, "QoS 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" }, { 0xF7, "CompoNet Object" }, { 0xF8, "CompoNet Repeater Object" }, { 0, NULL } }; static void dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, 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, gboolean generate ) { int pathpos, temp_data, temp_data2, seg_size, i, temp_word; unsigned char segment_type, opt_link_size; proto_tree *path_tree, *port_tree, *net_tree; proto_item *qi, *cia_item, *ds_item; 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 ); 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; while( pathpos < path_length ) { /* Get segement type */ segment_type = tvb_get_guint8( tvb, offset + pathpos ); /* Determine the segment type */ switch( segment_type & CI_SEGMENT_TYPE_MASK ) { 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 */ 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 */ 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 ); 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 */ 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) ); /* Pad byte */ if( opt_link_size % 2 ) { proto_item_set_len( port_item, 3 + opt_link_size ); pathpos = pathpos + 3 + opt_link_size; } else { proto_item_set_len( port_item, 2 + opt_link_size ); pathpos = pathpos + 2 + opt_link_size; } } else { /* Add Extended Link Address flag */ 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 */ 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 ) ); proto_item_set_len( port_item, 2 ); pathpos += 2; } break; case CI_LOGICAL_SEGMENT: /* Logical segment, determin the logical type */ switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK ) { case CI_LOGICAL_SEG_CLASS_ID: /* 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 ); 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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_INST_ID: /* 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 ); 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 ); /* 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_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_uint( cia_tree, hf_cip_member8, NULL, 0, 0, temp_data ); 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_uint( cia_tree, hf_cip_member16, NULL, 0, 0, temp_data ); 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_uint( cia_tree, hf_cip_member32, NULL, 0, 0, temp_data ); 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 ); 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 */ if ( generate ) { it = proto_tree_add_uint( cia_tree, hf_cip_attribute8, NULL, 0, 0, temp_data ); 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 */ 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 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 */ if ( generate ) { it = proto_tree_add_uint( cia_tree, hf_cip_attribute16, NULL, 0, 0, temp_data ); 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 */ 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 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 */ if ( generate ) { it = proto_tree_add_uint( cia_tree, hf_cip_attribute32, NULL, 0, 0, temp_data ); 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 */ 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_CON_POINT: /* 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 ); 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 */ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path ); /* Display the 8-bit connection point number */ proto_tree_add_item( cia_tree, hf_cip_conpoint8, tvb, offset + pathpos + 1, 1, TRUE ); proto_item_append_text( epath_item, "Connection Point: 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 ); cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Connection Point Segment (0x%02X)", segment_type ); /* Create a sub tree for the connection point */ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path ); /* Display the 16-bit connection point number */ proto_tree_add_item( cia_tree, hf_cip_conpoint16, tvb, offset + pathpos + 2, 2, TRUE ); proto_item_append_text( epath_item, "Connection Point: 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 ); cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Connection Point Segment (0x%02X)", segment_type ); /* Create a sub tree for the connection point */ cia_tree = proto_item_add_subtree( cia_item, ett_cia_path ); /* Display the 32-bit connection point number */ proto_tree_add_item( cia_tree, hf_cip_conpoint32, tvb, offset + pathpos + 2, 4, TRUE ); proto_item_append_text( epath_item, "Connection Point: 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_SPECIAL: /* Logical Special ID, the only logical format sepcifyed is electronic key */ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_E_KEY ) { /* Get the Key Format */ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 ); if( temp_data == CI_E_KEY_FORMAT_VAL ) { qi = proto_tree_add_text( path_tree, tvb, offset + pathpos, 10, "Electronic Key Segment (0x%02X): ",segment_type ); /* Create a sub tree for the IOI */ e_key_tree = proto_item_add_subtree( qi, ett_ekey_path ); /* Print the key type */ 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 ); 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); proto_item_append_text( qi, ", DevTyp: 0x%04X", temp_data ); /* Product Code */ 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 ); /* 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 */ proto_tree_add_item(mc_tree, hf_cip_fwo_comp, 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 ); /* Minor revision */ temp_data2 = tvb_get_guint8( tvb, offset + pathpos + 9 ); proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 9, 1, "Minor Revision: %d", temp_data2 ); proto_item_append_text( qi, ", %d.%d", ( temp_data & 0x7F ), temp_data2 ); proto_item_append_text(epath_item, "[Key]" ); /* Increment the path pointer */ pathpos += 10; } else { /* Unsupported electronic key format */ proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Electronic Key Format" ); return; } } else { /* Unsupported special segment format */ proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Special Segment Format" ); return; } break; default: /* Unsupported logical segment type */ proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Logical Segment Type" ); return; } /* end of switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK ) */ break; case CI_DATA_SEGMENT: /* Data segment, determin the logical type */ switch( segment_type ) { case CI_DATA_SEG_SIMPLE: /* Simple data segment */ 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 ); /* Segment size */ seg_size = tvb_get_guint8( tvb, offset + pathpos+1 )*2; proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d (words)", seg_size/2 ); /* Segment data */ if( seg_size != 0 ) { qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos+2, 0, "Data: " ); for( i=0; i < seg_size/2; i ++ ) { temp_word = tvb_get_letohs( tvb, offset + pathpos+2+(i*2) ); proto_item_append_text(qi, " 0x%04X", temp_word ); } proto_item_set_len(qi, seg_size); } proto_item_set_len( ds_item, 2 + seg_size ); pathpos = pathpos + 2 + seg_size; proto_item_append_text(epath_item, "[Data]" ); break; case CI_DATA_SEG_SYMBOL: /* ANSI extended symbol segment */ 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 ); 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 ) { 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 */ 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++; } } if ( !generate ) proto_item_set_len( ds_item, 2 + seg_size ); pathpos = pathpos + 2 + seg_size; break; default: proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Sub-Segment Type" ); return; } /* End of switch sub-type */ break; case CI_NETWORK_SEGMENT: /* Network segment -Determine the segment sub-type */ switch( segment_type & CI_NETWORK_SEG_TYPE_MASK ) { case CI_NETWORK_SEG_SCHEDULE: net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Schedule" ); net_tree = proto_item_add_subtree( net_item, ett_port_path ); proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Multiplier/Phase: %02X", tvb_get_guint8( tvb, offset + pathpos + 1 ) ); /* 2 bytes of path used */ pathpos += 2; break; case CI_NETWORK_SEG_FIXED_TAG: net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Fixed Tag" ); net_tree = proto_item_add_subtree( net_item, ett_port_path ); proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Fixed Tag: %02X", tvb_get_guint8( tvb, offset + pathpos + 1 ) ); /* 2 bytes of path used */ pathpos += 2; break; case CI_NETWORK_SEG_PROD_INHI: net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Production Inhibit" ); net_tree = proto_item_add_subtree( net_item, ett_port_path ); proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Production Inhibit Time: %dms", tvb_get_guint8( tvb, offset + pathpos + 1 ) ); /* 2 bytes of path used */ pathpos += 2; break; default: proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Sub-Segment Type" ); return; } /* End of switch sub-type */ break; default: /* Unsupported segment type */ proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Segment Type" ); return; } /* end of switch( segment_type & CI_SEGMENT_TYPE_MASK ) */ /* Next path segment */ if( pathpos < path_length ) proto_item_append_text( epath_item, ", " ); } /* end of while( pathpos < path_length ) */ } /* end of dissect_epath() */ /* Dissect EPATH for class, instance, attribute, and/or member without creating a tree */ static void dissect_epath_request( tvbuff_t *tvb, cip_simple_request_info_t* req_data, int path_length) { int pathpos, offset, temp_data, seg_size; unsigned char segment_type, opt_link_size; /* can't populate req_data unless its there */ if (req_data == NULL) return; req_data->iClass = (guint32)-1; req_data->iInstance = (guint32)-1; req_data->iAttribute = (guint32)-1; req_data->iMember = (guint32)-1; pathpos = 0; offset = 0; while( pathpos < path_length ) { /* Get segement type */ segment_type = tvb_get_guint8( tvb, offset + pathpos ); /* Determine the segment type */ switch( segment_type & CI_SEGMENT_TYPE_MASK ) { case CI_PORT_SEGMENT: if( segment_type & 0x10 ) { /* Add size of extended link address */ opt_link_size = tvb_get_guint8( tvb, offset + pathpos + 1 ); /* Pad byte */ if( opt_link_size % 2 ) { pathpos = pathpos + 3 + opt_link_size; } else { pathpos = pathpos + 2 + opt_link_size; } } else { pathpos += 2; } break; case CI_LOGICAL_SEGMENT: /* Logical segment, determin the logical type */ switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK ) { case CI_LOGICAL_SEG_CLASS_ID: /* Logical Class ID, do a format check */ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT ) { req_data->iClass = tvb_get_guint8( tvb, offset + pathpos + 1 ); /* 2 bytes of path used */ pathpos += 2; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT ) { req_data->iClass = tvb_get_letohs( tvb, offset + pathpos + 2 ); /* 4 bytes of path used */ pathpos += 4; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT ) { req_data->iClass = tvb_get_letohl( tvb, offset + pathpos + 2 ); /* 6 bytes of path used */ pathpos += 6; } else { /* Unsupported logical segment format */ return; } break; case CI_LOGICAL_SEG_INST_ID: /* Logical Instance ID, do a format check */ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT ) { req_data->iInstance = tvb_get_guint8( tvb, offset + pathpos + 1 ); /* 2 bytes of path used */ pathpos += 2; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT ) { req_data->iInstance = tvb_get_letohs( tvb, offset + pathpos + 2 ); /* 4 bytes of path used */ pathpos += 4; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT ) { req_data->iInstance = tvb_get_letohl( tvb, offset + pathpos + 2 ); /* 6 bytes of path used */ pathpos += 6; } else { /* Unsupported logical segment format */ return; } 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 ) { req_data->iMember = tvb_get_guint8( tvb, offset + pathpos + 1 ); /* 2 bytes of path used */ pathpos += 2; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT ) { req_data->iMember = tvb_get_letohs( tvb, offset + pathpos + 2 ); /* 4 bytes of path used */ pathpos += 4; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT ) { req_data->iMember = tvb_get_letohl( tvb, offset + pathpos + 2 ); /* 6 bytes of path used */ pathpos += 6; } else { /* 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 ) { req_data->iAttribute = tvb_get_guint8( tvb, offset + pathpos + 1 ); /* 2 bytes of path used */ pathpos += 2; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT ) { req_data->iAttribute = tvb_get_letohs( tvb, offset + pathpos + 2 ); /* 4 bytes of path used */ pathpos += 4; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT ) { req_data->iAttribute = tvb_get_letohl( tvb, offset + pathpos + 2 ); /* 6 bytes of path used */ pathpos += 6; } else { /* Unsupported logical segment format */ return; } break; case CI_LOGICAL_SEG_CON_POINT: /* Logical Connection point , do a format check */ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT ) { /* 2 bytes of path used */ pathpos += 2; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT ) { /* 4 bytes of path used */ pathpos += 4; } else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT ) { /* 6 bytes of path used */ pathpos += 6; } else { /* Unsupported logical segment format */ return; } break; case CI_LOGICAL_SEG_SPECIAL: /* Logical Special ID, the only logical format specified is electronic key */ if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_E_KEY ) { /* Get the Key Format */ temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 ); if( temp_data == CI_E_KEY_FORMAT_VAL ) { /* Increment the path pointer */ pathpos += 10; } else { /* Unsupported electronic key format */ return; } } else { /* Unsupported special segment format */ return; } break; default: /* Unsupported logical segment type */ return; } /* end of switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK ) */ break; case CI_DATA_SEGMENT: /* Data segment, determin the logical type */ switch( segment_type ) { case CI_DATA_SEG_SIMPLE: /* Simple data segment */ /* Segment size */ seg_size = tvb_get_guint8( tvb, offset + pathpos+1 )*2; pathpos = pathpos + 2 + seg_size; break; case CI_DATA_SEG_SYMBOL: /* Segment size */ seg_size = tvb_get_guint8( tvb, offset + pathpos+1 ); /* Segment data */ if( seg_size != 0 ) { if( seg_size %2 ) { /* We have a PAD BYTE */ seg_size++; } } pathpos = pathpos + 2 + seg_size; break; default: return; } /* End of switch sub-type */ break; case CI_NETWORK_SEGMENT: /* Network segment -Determine the segment sub-type */ switch( segment_type & CI_NETWORK_SEG_TYPE_MASK ) { case CI_NETWORK_SEG_SCHEDULE: /* 2 bytes of path used */ pathpos += 2; break; case CI_NETWORK_SEG_FIXED_TAG: /* 2 bytes of path used */ pathpos += 2; break; case CI_NETWORK_SEG_PROD_INHI: /* 2 bytes of path used */ pathpos += 2; break; default: return; } /* End of switch sub-type */ break; default: /* Unsupported segment type */ return; } /* end of switch( segment_type & CI_SEGMENT_TYPE_MASK ) */ } /* end of while( pathpos < path_length ) */ } /* end of dissect_epath_request() */ /************************************************ * * Dissector for generic CIP object * ************************************************/ static void 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, *temp_item; proto_tree *cmd_data_tree; int req_path_size; unsigned char add_stat_size; unsigned char i; if( tvb_get_guint8( tvb, offset ) & 0x80 ) { /* 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 ) { 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 ); } } /* 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 , "Unknown Service (0x%02x)") ); } 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; 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( ( service & 0x7F ), cip_sc_vals, "Unknown Service (0x%02x)"), val_to_str( ( service & 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 ) { 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_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 */ 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, pinfo, mr_single_req_info ); } else { dissect_cip_data( temp_tree, next_tvb, 0, 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 ) & 0x7F ), cip_sc_vals, "Unknown Service (0x%02x)") ); } /* Add path size to tree */ 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_mr_cmd_data ); /* Check what service code that recived */ if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK ) { /* 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 */ 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, pinfo, mr_single_req_info ); } else { dissect_cip_data( temp_tree, next_tvb, 0, 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_timeout(proto_tree *cmd_tree, tvbuff_t *tvb, int offset) { unsigned char temp_byte; int temp_data; /* Display the priority/tick timer */ temp_byte = tvb_get_guint8( tvb, offset); proto_tree_add_text( cmd_tree, tvb, offset, 1, "Priority/Time_tick: 0x%02X", temp_byte ); /* Display the time-out ticks */ temp_data = tvb_get_guint8( tvb, offset+1 ); proto_tree_add_text( cmd_tree, tvb, offset+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_tree, tvb, offset, 2, "Actual Time Out: %dms", temp_data ); } static void dissect_cip_cm_fwd_open_req(proto_tree *cmd_tree, tvbuff_t *tvb, int offset, gboolean large_fwd_open) { proto_item *pi, *ncppi; proto_tree *ncp_tree; int conn_path_size, temp_data, net_param_offset = 0; /* Display timeout fields */ dissect_cip_cm_timeout(cmd_tree, tvb, offset); /* Display originator to taget connection ID */ proto_tree_add_item( cmd_tree, hf_cip_cm_ot_connid, tvb, offset+2, 4, TRUE); /* Display target to originator connection ID */ proto_tree_add_item( cmd_tree, hf_cip_cm_to_connid, tvb, offset+6, 4, TRUE); /* Display connection serial number */ proto_tree_add_item( cmd_tree, hf_cip_cm_conn_serial_num, tvb, offset+10, 2, TRUE); /* Display the originator vendor id */ proto_tree_add_item( cmd_tree, hf_cip_vendor, tvb, offset+12, 2, TRUE); /* Display the originator serial number */ proto_tree_add_item( cmd_tree, hf_cip_cm_orig_serial_num, tvb, offset+14, 4, TRUE); /* Display the timeout multiplier */ temp_data = tvb_get_guint8( tvb, offset+18 ); proto_tree_add_text( cmd_tree, tvb, offset+18, 1, "Connection Timeout Multiplier: %s (%d)", val_to_str( temp_data, cip_con_time_mult_vals , "Reserved" ), temp_data ); /* Put out an indicator for the reserved bytes */ proto_tree_add_text( cmd_tree, tvb, offset+19, 3, "Reserved Data" ); /* Display originator to target requested packet interval */ temp_data = tvb_get_letohl( tvb, offset+22 ); proto_tree_add_text( cmd_tree, tvb, offset+22, 4, "O->T RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display originator to target network connection parameters, in a tree */ if (large_fwd_open) { temp_data = tvb_get_letohl( tvb, offset+26 ); ncppi = proto_tree_add_text(cmd_tree, tvb, offset+26, 4, "O->T Network Connection Parameters: 0x%08X", temp_data ); ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_own, tvb, offset+26, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_typ, tvb, offset+26, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_prio, tvb, offset+26, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_fixed_var, tvb, offset+26, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_con_size, tvb, offset+26, 4, TRUE ); net_param_offset = 4; } else { temp_data = tvb_get_letohs( tvb, offset+26 ); ncppi = proto_tree_add_text(cmd_tree, tvb, offset+26, 2, "O->T Network Connection Parameters: 0x%04X", temp_data ); ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size, tvb, offset+26, 2, TRUE ); net_param_offset = 2; } /* Display target to originator requested packet interval */ temp_data = tvb_get_letohl( tvb, offset+26+net_param_offset ); proto_tree_add_text( cmd_tree, tvb, offset+26+net_param_offset, 4, "T->O RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display target to originator network connection parameters, in a tree */ if (large_fwd_open) { temp_data = tvb_get_letohl( tvb, offset+26+net_param_offset+4 ); ncppi = proto_tree_add_text(cmd_tree, tvb, offset+26+net_param_offset+4, 4, "T->O Network Connection Parameters: 0x%04X", temp_data ); ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_own, tvb, offset+26+net_param_offset+4, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_typ, tvb, offset+26+net_param_offset+4, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_prio, tvb, offset+26+net_param_offset+4, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_fixed_var, tvb, offset+26+net_param_offset+4, 4, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_lfwo_con_size, tvb, offset+26+net_param_offset+4, 4, TRUE ); net_param_offset += 4; } else { temp_data = tvb_get_letohs( tvb, offset+26+net_param_offset+4 ); ncppi = proto_tree_add_text(cmd_tree, tvb, offset+26+net_param_offset+4, 2, "T->O Network Connection Parameters: 0x%04X", temp_data ); ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own, tvb, offset+26+net_param_offset+4, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ, tvb, offset+26+net_param_offset+4, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio, tvb, offset+26+net_param_offset+4, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var, tvb, offset+26+net_param_offset+4, 2, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size, tvb, offset+26+net_param_offset+4, 2, TRUE ); net_param_offset += 2; } /* Transport type/trigger in tree */ temp_data = tvb_get_guint8( tvb, offset+26+net_param_offset+4 ); ncppi = proto_tree_add_text(cmd_tree, tvb, offset+26+net_param_offset+4, 1, "Transport Type/Trigger: 0x%02X", temp_data ); ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_dir, tvb, offset+26+net_param_offset+4, 1, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_trigg, tvb, offset+26+net_param_offset+4, 1, TRUE ); proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_class, tvb, offset+26+net_param_offset+4, 1, TRUE ); /* Add path size */ conn_path_size = tvb_get_guint8( tvb, offset+26+net_param_offset+5 )*2; proto_tree_add_text( cmd_tree, tvb, offset+26+net_param_offset+5, 1, "Connection Path Size: %d (words)", conn_path_size / 2 ); /* Add the epath */ pi = proto_tree_add_text(cmd_tree, tvb, offset+26+net_param_offset+6, conn_path_size, "Connection Path: "); dissect_epath( tvb, pi, offset+26+net_param_offset+6, conn_path_size, FALSE ); } 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, *ar_item, *temp_item; proto_tree *temp_tree, *rrsc_tree, *cmd_data_tree; int req_path_size, conn_path_size, temp_data; unsigned char service, gen_status, add_stat_size; unsigned short add_status; unsigned char temp_byte, route_path_size; unsigned char app_rep_size, i; int msg_req_siz; cip_req_info_t *preq_info; cip_req_info_t *pembedded_req_info; service = tvb_get_guint8( tvb, offset ); /* 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 != NULL && ( service & 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 ) */ || ( ( service & 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 ); 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; } } } 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 */ proto_item_append_text( rrsc_item, "%s (%s)", val_to_str( ( service & 0x7F ), cip_sc_vals_cm , "Unknown Service (0x%02x)"), val_to_str( ( service & 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 */ 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_cm_cmd_data ); if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR ) { /* Success responses */ switch (service & 0x7F) { case SC_CM_FWD_OPEN: case SC_CM_LARGE_FWD_OPEN: { /* Forward open Response (Success) */ guint32 O2TConnID; guint32 T2OConnID; guint16 ConnSerialNumber; guint32 DeviceSerialNumber; guint16 VendorID; /* Display originator to target connection ID */ O2TConnID = tvb_get_letohl( tvb, offset+4+add_stat_size ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_ot_connid, tvb, offset+4+add_stat_size, 4, TRUE); /* Display target to originator connection ID */ T2OConnID = tvb_get_letohl( tvb, offset+4+add_stat_size+4 ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_to_connid, tvb, offset+4+add_stat_size+4, 4, TRUE); /* Display connection serial number */ ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size+8 ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_conn_serial_num, tvb, offset+4+add_stat_size+8, 2, TRUE); /* 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 */ DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+12 ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_orig_serial_num, tvb, offset+4+add_stat_size+12, 4, TRUE); /* Display originator to target actual packet interval */ temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+16 ); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+16, 4, "O->T API: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display originator to target actual packet interval */ temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+20 ); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+20, 4, "T->O API: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display the application reply size */ app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+24 ) * 2; proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+24, 1, "Application Reply Size: %d (words)", app_rep_size / 2 ); /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+4+add_stat_size+25, 1, TRUE ); if( app_rep_size != 0 ) { /* Display application Reply data */ ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+26, app_rep_size, "Application Reply:" ); for( i=0; i < app_rep_size; i++ ) { temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+26+i ); proto_item_append_text(ar_item, " 0x%02X", temp_byte ); } } /* End of if reply data */ enip_open_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber, O2TConnID, T2OConnID ); } /* End of if forward open response */ break; case SC_CM_FWD_CLOSE: { /* Forward close response (Success) */ guint16 ConnSerialNumber; guint32 DeviceSerialNumber; guint16 VendorID; /* Display connection serial number */ ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_conn_serial_num, tvb, offset+4+add_stat_size, 2, TRUE); /* 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 */ DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+4 ); proto_tree_add_item( cmd_data_tree, hf_cip_cm_orig_serial_num, tvb, offset+4+add_stat_size+4, 4, TRUE); /* Display the application reply size */ app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+8 ) * 2; proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Application Reply Size: %d (words)", app_rep_size / 2 ); /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+4+add_stat_size+9, 1, TRUE ); if( app_rep_size != 0 ) { /* Display application Reply data */ ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+10, app_rep_size, "Application Reply:" ); 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 ); } } /* End of if reply data */ enip_close_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber ); } /* End of if forward close response */ break; case SC_CM_UNCON_SEND: { /* Unconnected send response (Success) */ /* Display service response data */ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " ); } break; case SC_CM_GET_CONN_OWNER: { /* Get Connection owner response (Success) */ /* Display number of connections */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_conn, tvb, offset+4+add_stat_size, 1, TRUE); /* Display number of COO connections */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_coo_conn, tvb, offset+4+add_stat_size+1, 1, TRUE); /* Display number of ROO connections */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_roo_conn, tvb, offset+4+add_stat_size+2, 1, TRUE); /* Display Last Action */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_la, tvb, offset+4+add_stat_size+3, 1, TRUE); /* Display connection serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_conn_serial_num, tvb, offset+4+add_stat_size+4, 2, TRUE); /* Display the originator vendor id */ proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+6, 2, TRUE); /* Display the originator serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_orig_serial_num, tvb, offset+4+add_stat_size+8, 4, TRUE); } break; case 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: " ); } /* Get Attribute List Reply */ break; default: /* 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: " ); break; } } else { /* Error responses */ switch (service & 0x7F) { case SC_CM_FWD_OPEN: case SC_CM_LARGE_FWD_OPEN: case SC_CM_FWD_CLOSE: /* Forward open and forward close error response look the same */ /* Display connection serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_conn_serial_num, tvb, offset+4+add_stat_size, 2, TRUE); /* Display the originator vendor id */ proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE); /* Display the originator serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_orig_serial_num, tvb, offset+4+add_stat_size+4, 4, TRUE); /* Display remaining path size */ temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+8 ); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Remaining Path Size: %d", temp_data ); /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+4+add_stat_size+9, 1, TRUE ); break; case SC_CM_UNCON_SEND: /* Unconnected send response (Unsuccess) */ /* Display remaining path size */ temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 1, "Remaining Path Size: %d", temp_data ); break; default: /* 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: " ); break; } } /* 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_cm , "Unknown Service (0x%02x)") ); } 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_cm_cmd_data ); /* Check what service code that received */ switch (service) { case SC_CM_FWD_OPEN: /* Forward open Request*/ dissect_cip_cm_fwd_open_req(cmd_data_tree, tvb, offset+2+req_path_size, FALSE); break; case SC_CM_LARGE_FWD_OPEN: /* Large Forward open Request*/ dissect_cip_cm_fwd_open_req(cmd_data_tree, tvb, offset+2+req_path_size, TRUE); break; case SC_CM_FWD_CLOSE: /* Forward Close Request */ /* Display timeout fields */ dissect_cip_cm_timeout( cmd_data_tree, tvb, offset+2+req_path_size); /* Display connection serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_conn_serial_num, tvb, offset+2+req_path_size+2, 2, TRUE); /* Display the originator vendor id */ proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+2+req_path_size+4, 2, TRUE); /* Display the originator serial number */ proto_tree_add_item( cmd_data_tree, hf_cip_cm_orig_serial_num, tvb, offset+2+req_path_size+6, 4, TRUE); /* Add the path size */ conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+10 )*2; proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+10, 1, "Connection Path Size: %d (words)", conn_path_size / 2 ); /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+2+req_path_size+11, 1, TRUE ); /* 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, FALSE ); break; case SC_CM_UNCON_SEND: { /* Unconnected send */ tvbuff_t *next_tvb; /* Display timeout fields */ dissect_cip_cm_timeout( cmd_data_tree, tvb, offset+2+req_path_size); /* 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_cm_mes_req ); /* ** We call our selves again to disect embedded packet */ 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 ) { 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 { pembedded_req_info = (cip_req_info_t*)preq_info->pData; } } dissect_cip_data( temp_tree, next_tvb, 0, pinfo, pembedded_req_info ); 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 ); /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+2+req_path_size+5+msg_req_siz, 1, TRUE ); /* 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 ); } break; case SC_CM_GET_CONN_OWNER: /* Get Connection Owner Request */ /* Display the Reserved byte */ proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+2+req_path_size, 1, TRUE ); /* Add path size */ conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+1 )*2; proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Connection Path Size: %d (words)", conn_path_size / 2 ); /* Add the epath */ pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+2, conn_path_size, "Connection Path: "); dissect_epath( tvb, pi, offset+2+req_path_size+2, conn_path_size, FALSE ); break; case 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 */ break; default: /* 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 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_all_attribute_common( proto_tree *cmd_tree, tvbuff_t *tvb, int offset, int item_length) { proto_item *pi, *tdii, *ncpi, *ncppi, *iomapi, *confgi; proto_tree *tdi_tree, *iomap_tree; proto_tree *ncp_tree, *ncpp_tree, *confg_tree; int conn_path_size, variable_data_size = 0, config_data_size; int connection_name_size, iomap_size, ot_rtf, to_rtf; int temp_data, temp_data2; char* str_connection_name; /* Connection flags */ temp_data = tvb_get_letohs( tvb, offset); ot_rtf = (temp_data >> 1) & 7; to_rtf = (temp_data >> 4) & 7; confgi = proto_tree_add_text( cmd_tree, tvb, offset, 2, "Connection Flags: 0x%04X", temp_data ); confg_tree = proto_item_add_subtree(confgi, ett_cco_con_flag); /* Add the data to the tree */ proto_tree_add_item(confg_tree, hf_cip_cco_con_type, tvb, offset, 2, TRUE ); proto_tree_add_item(confg_tree, hf_cip_cco_ot_rtf, tvb, offset, 2, TRUE ); proto_tree_add_item(confg_tree, hf_cip_cco_to_rtf, tvb, offset, 2, TRUE ); /* Target device id */ tdii = proto_tree_add_text( cmd_tree, tvb, offset+2, 10, "Target Device ID"); tdi_tree = proto_item_add_subtree(tdii, ett_cco_tdi); /* Target Vendor ID */ proto_tree_add_item(tdi_tree, hf_cip_vendor, tvb, offset+2, 2, TRUE); /* Target Device Type */ proto_tree_add_item(tdi_tree, hf_cip_devtype, tvb, offset+4, 2, TRUE); /* Target Product Code */ temp_data = tvb_get_letohs( tvb, offset+6); proto_tree_add_text(tdi_tree, tvb, offset+6, 2, "Product Code: 0x%04X", temp_data ); /* Target Major/Minor revision*/ temp_data = tvb_get_guint8( tvb, offset+8); temp_data2 = tvb_get_guint8( tvb, offset+9); proto_tree_add_text(tdi_tree, tvb, offset+8, 2, "Revision %d.%d", temp_data, temp_data2); /* CS Data Index Number */ temp_data = tvb_get_letohl( tvb, offset+10); proto_tree_add_text( cmd_tree, tvb, offset+10, 4, "CS Data Index Number: 0x%08X", temp_data ); /* Net Connection Parameters */ ncpi = proto_tree_add_text( cmd_tree, tvb, offset+14, 14, "Net Connection Parameters"); ncp_tree = proto_item_add_subtree(ncpi, ett_cco_ncp); /* Timeout multiplier */ temp_data = tvb_get_guint8( tvb, offset+14); proto_tree_add_text(ncp_tree, tvb, offset+14, 1, "Connection Timeout Multiplier: %s (%d)", val_to_str( temp_data, cip_con_time_mult_vals , "Reserved" ), temp_data ); /* Transport type/trigger in tree*/ temp_data = tvb_get_guint8( tvb, offset+15 ); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+15, 1, "Transport Type/Trigger: 0x%02X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_dir, tvb, offset+15, 1, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_trigg, tvb, offset+15, 1, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_class, tvb, offset+15, 1, TRUE ); temp_data = tvb_get_letohl( tvb, offset+16); proto_tree_add_text(ncp_tree, tvb, offset+16, 4, "O->T RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display originator to target network connection patameterts, in a tree */ temp_data = tvb_get_letohs( tvb, offset+20 ); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+20, 2, "O->T Network Connection Parameters: 0x%04X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_own, tvb, offset+20, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_typ, tvb, offset+20, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_prio, tvb, offset+20, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_fixed_var, tvb, offset+20, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_con_size, tvb, offset+20, 2, TRUE ); temp_data = tvb_get_letohl( tvb, offset+22); proto_tree_add_text(ncp_tree, tvb, offset+22, 4, "T->O RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display target to originator network connection patameters, in a tree */ temp_data = tvb_get_letohs( tvb, offset+26); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+26, 2, "T->O Network Connection Parameters: 0x%04X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_own, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_typ, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_prio, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_fixed_var, tvb, offset+26, 2, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_con_size, tvb, offset+26, 2, TRUE ); /* Connection Path */ conn_path_size = tvb_get_guint8( tvb, offset+28 )*2; proto_tree_add_text( cmd_tree, tvb, offset+28, 1, "Connection Path Size: %d (words)", conn_path_size / 2 ); /* Display the Reserved byte */ proto_tree_add_item(cmd_tree, hf_cip_reserved8, tvb, offset+29, 1, TRUE ); /* Add the epath */ pi = proto_tree_add_text(cmd_tree, tvb, offset+30, conn_path_size, "Connection Path: "); dissect_epath( tvb, pi, offset+30, conn_path_size, FALSE ); variable_data_size += (conn_path_size+30); /* Config #1 Data */ config_data_size = tvb_get_letohs( tvb, offset+variable_data_size); proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 2, "Proxy Config Data Size %d", config_data_size); if (config_data_size > 0) add_byte_array_text_to_proto_tree( cmd_tree, tvb, offset+variable_data_size+2, config_data_size, "Proxy Config Data: " ); variable_data_size += (config_data_size+2); /* Config #2 Data */ config_data_size = tvb_get_letohs( tvb, offset+variable_data_size); proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 2, "Target Config Data Size %d", config_data_size); if (config_data_size > 0) add_byte_array_text_to_proto_tree( cmd_tree, tvb, offset+variable_data_size+2, config_data_size, "Target Config Data: " ); variable_data_size += (config_data_size+2); /* Connection Name */ connection_name_size = tvb_get_guint8( tvb, offset+variable_data_size); str_connection_name = tvb_get_ephemeral_faked_unicode(tvb, offset+variable_data_size+2, connection_name_size, TRUE); proto_tree_add_text(cmd_tree, tvb, offset+variable_data_size, connection_name_size+2, "Connection Name: %s", str_connection_name); variable_data_size += ((connection_name_size*2)+2); /* I/O Mapping */ iomap_size = tvb_get_letohs( tvb, offset+variable_data_size+2); iomapi = proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, iomap_size+2, "I/O Mapping"); iomap_tree = proto_item_add_subtree(iomapi, ett_cco_iomap); /* Format number */ temp_data = tvb_get_guint8( tvb, offset+variable_data_size); proto_tree_add_text(iomap_tree, tvb, offset+variable_data_size, 2, "Format number: %d", temp_data ); /* Attribute size */ proto_tree_add_text(iomap_tree, tvb, offset+variable_data_size+2, 2, "Attribute size: %d (bytes)", iomap_size); /* Attribute data */ if (iomap_size > 0) add_byte_array_text_to_proto_tree( iomap_tree, tvb, offset+variable_data_size+4, iomap_size, "Attribute Data: " ); variable_data_size += (iomap_size+4); /* Proxy device id */ tdii = proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 10, "Proxy Device ID"); tdi_tree = proto_item_add_subtree(tdii, ett_cco_tdi); /* Proxy Vendor ID */ temp_data = tvb_get_letohs( tvb, offset+variable_data_size); proto_tree_add_item(tdi_tree, hf_cip_vendor, tvb, offset+variable_data_size, 2, TRUE); /* Proxy Device Type */ temp_data = tvb_get_letohs( tvb, offset+variable_data_size+2); proto_tree_add_item(tdi_tree, hf_cip_devtype, tvb, offset+variable_data_size+2, 2, TRUE); /* Proxy Product Code */ temp_data = tvb_get_letohs( tvb, offset+variable_data_size+4); proto_tree_add_text(tdi_tree, tvb, offset+variable_data_size+4, 2, "Product Code: 0x%04X", temp_data ); /* Proxy Major/Minor revision*/ temp_data = tvb_get_guint8( tvb, offset+variable_data_size+6); temp_data2 = tvb_get_guint8( tvb, offset+variable_data_size+7); proto_tree_add_text(tdi_tree, tvb, offset+variable_data_size+6, 2, "Revision %d.%d", temp_data, temp_data2); /* Add in proxy device id size */ variable_data_size += 8; if ((offset+variable_data_size < item_length) && ((ot_rtf == 5) || (to_rtf == 5))) { /* Safety parameters */ add_byte_array_text_to_proto_tree( cmd_tree, tvb, offset+variable_data_size, 55, "Safety Parameters: " ); variable_data_size += 55; } if (offset+variable_data_size < item_length) { /* Connection disable */ temp_data = tvb_get_guint8( tvb, offset+variable_data_size) & 1; proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 1, "Connection Disable: %d", temp_data ); variable_data_size++; } if (offset+variable_data_size < item_length) { /* Net Connection Parameter Attribute Selection */ temp_data = tvb_get_guint8( tvb, offset+variable_data_size); proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 1, "Net Connection Parameter Attribute Selection: %d", temp_data ); variable_data_size++; } if (offset+variable_data_size < item_length) { /* Large Net Connection Parameter */ ncpi = proto_tree_add_text( cmd_tree, tvb, offset+variable_data_size, 18, "Large Net Connection Parameters"); ncp_tree = proto_item_add_subtree(ncpi, ett_cco_ncp); /* Timeout multiplier */ temp_data = tvb_get_guint8( tvb, offset+variable_data_size); proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size, 1, "Connection Timeout Multiplier: %s (%d)", val_to_str( temp_data, cip_con_time_mult_vals , "Reserved" ), temp_data ); /* Transport type/trigger in tree*/ temp_data = tvb_get_guint8( tvb, offset+variable_data_size+1); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size+1, 1, "Transport Type/Trigger: 0x%02X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_dir, tvb, offset+variable_data_size+1, 1, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_trigg, tvb, offset+variable_data_size+1, 1, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_fwo_class, tvb, offset+variable_data_size+1, 1, TRUE ); temp_data = tvb_get_letohl( tvb, offset+variable_data_size+2); proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size+2, 4, "O->T RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display originator to target network connection patameterts, in a tree */ temp_data = tvb_get_letohl(tvb, offset+variable_data_size+6); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size+6, 4, "O->T Network Connection Parameters: 0x%08X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_own, tvb, offset+variable_data_size+6, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_typ, tvb, offset+variable_data_size+6, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_prio, tvb, offset+variable_data_size+6, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_fixed_var, tvb, offset+variable_data_size+6, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_con_size, tvb, offset+variable_data_size+6, 4, TRUE ); temp_data = tvb_get_letohl( tvb, offset+variable_data_size+10); proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size+10, 4, "T->O RPI: %dms (0x%08X)", temp_data / 1000, temp_data ); /* Display target to originator network connection patameterts, in a tree */ temp_data = tvb_get_letohl(tvb, offset+variable_data_size+14); ncppi = proto_tree_add_text(ncp_tree, tvb, offset+variable_data_size+14, 4, "T->0 Network Connection Parameters: 0x%08X", temp_data ); ncpp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp); /* Add the data to the tree */ proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_own, tvb, offset+variable_data_size+14, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_typ, tvb, offset+variable_data_size+14, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_prio, tvb, offset+variable_data_size+14, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_fixed_var, tvb, offset+variable_data_size+14, 4, TRUE ); proto_tree_add_item(ncpp_tree, hf_cip_cm_lfwo_con_size, tvb, offset+variable_data_size+14, 4, TRUE ); variable_data_size += 18; } } 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, *con_sti; proto_tree *rrsc_tree, *cmd_data_tree, *con_st_tree; int req_path_size; int temp_data; guint8 service; unsigned char gen_status; unsigned char add_stat_size; unsigned char i; cip_req_info_t* preq_info; cip_simple_request_info_t req_data; col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP CCO"); /* Add Service code & Request/Response tree */ service = tvb_get_guint8( tvb, offset ); 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( ( service & 0x7F ), cip_sc_vals_cco , "Unknown Service (0x%02x)"), val_to_str( ( service & 0x80 )>>7, cip_sc_rr, "") ); /* Add Service code */ proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE ); /* Get path information for further dissection */ req_data.iClass = (guint32)-1; req_data.iInstance = (guint32)-1; req_data.iAttribute = (guint32)-1; req_data.iMember = (guint32)-1; preq_info = p_get_proto_data(pinfo->fd, proto_cip); 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 ) { dissect_epath_request(tvbIOI, &req_data, preq_info->IOILen*2); tvb_free(tvbIOI); } } } if(service & 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 */ if (((service & 0x7F) == SC_GET_ATT_ALL) && (req_data.iInstance != (guint32)-1)) { if (req_data.iInstance == 0) { /* Get Attribute All (class) request */ /* Revision */ proto_tree_add_item(cmd_data_tree, hf_cip_class_rev, tvb, offset+4+add_stat_size, 2, TRUE ); /* Max Instance */ proto_tree_add_item(cmd_data_tree, hf_cip_class_max_inst32, tvb, offset+4+add_stat_size+2, 4, TRUE ); /* Num Instance */ proto_tree_add_item(cmd_data_tree, hf_cip_class_num_inst32, tvb, offset+4+add_stat_size+6, 4, TRUE ); /* Format Number */ temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+8); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 2, "Format Number: %d", temp_data ); /* Edit Signature */ temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+10); proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+10, 4, "Edit Signature: 0x%08X", temp_data ); } else { /* Get Attribute All (instance) request */ /* Connection status */ con_sti = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 4, "Connection Status"); con_st_tree = proto_item_add_subtree(con_sti, ett_cco_con_status); /* General Status */ proto_tree_add_item(con_st_tree, hf_cip_genstat, tvb, offset+4+add_stat_size, 1, TRUE ); /* Pad */ temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+1); proto_tree_add_text(con_st_tree, tvb, offset+4+add_stat_size+1, 1, "Pad: %d", temp_data ); /* Extended Status */ temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size+2); proto_tree_add_text(con_st_tree, tvb, offset+4+add_stat_size+2, 2, "Extended Status: 0x%04X", temp_data ); dissect_cip_cco_all_attribute_common( cmd_data_tree, tvb, offset+4+add_stat_size+4, item_length); } } 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: " ); } } 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( ( service & 0x7F ), cip_sc_vals_cco , "Unknown Service (0x%02x)") ); } req_path_size = tvb_get_guint8( tvb, offset+1 )*2; /* If there is any command specific data create 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 received */ switch (service) { case SC_CCO_AUDIT_CHANGE: /* Audit Change */ temp_data = tvb_get_letohs( tvb, offset+2+req_path_size); proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: %s (%d)", val_to_str( temp_data, cip_cco_change_type_vals , "Reserved" ), temp_data ); break; case 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) ) ); } } break; case SC_CCO_CHANGE_COMPLETE: /* Change complete request */ temp_data = tvb_get_letohs( tvb, offset+2+req_path_size); proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: %s (%d)", val_to_str( temp_data, cip_cco_change_type_vals , "Reserved" ), temp_data ); break; case SC_SET_ATT_ALL: if ((req_data.iInstance == 0) || (req_data.iInstance != (guint32)-1)) { /* Just add raw data */ add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " ); break; } /* Set Attribute All (instance) request */ dissect_cip_cco_all_attribute_common(cmd_data_tree, tvb, offset+2+req_path_size, item_length); break; default: /* 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_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, 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( ( service & 0x7F ), cip_sc_vals , "Unknown Service (0x%02x)"), val_to_str( ( service & 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( gen_status, 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 ) { #if 0 pi = add_byte_array_text_to_proto_tree( cip_tree, tvbIOI, 0, req_path_size+1, "IOI: " ); PROTO_ITEM_SET_GENERATED(pi); #endif 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*2, 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_uint_handle( subdissector_class_table, classid ); if ( preq_info ) preq_info->dissector = dissector; break; } break; case CI_DATA_SEGMENT: dissector = dissector_get_uint_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); 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) { enip_request_info_t *enip_info; cip_req_info_t *preq_info; /* Make entries in Protocol column and Info column on summary display */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP"); col_clear(pinfo->cinfo, COL_INFO); /* 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 ) { preq_info = (cip_req_info_t*)enip_info->cip_info; if ( preq_info == NULL ) { preq_info = se_alloc( sizeof( cip_req_info_t ) ); 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, pinfo, enip_info->cip_info ); } else { dissect_cip_data( tree, tvb, 0, pinfo, NULL ); } 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) { /* 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_member8, { "Member", "cip.member", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_cip_member16, { "Member", "cip.member", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_cip_member32, { "Member", "cip.member", 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_class_rev, { "Class Revision", "cip.class.rev", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_cip_class_max_inst32, { "Max Instance", "cip.class.max_inst", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_cip_class_num_inst32, { "Number of Instances", "cip.class.num_inst", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } }, { &hf_cip_reserved8, { "Reserved", "cip.reserved", FT_UINT8, BASE_HEX, NULL, 0, NULL, 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_ot_connid, { "O->T Network Connection ID", "cip.cm.ot_connid", FT_UINT32, BASE_HEX, NULL, 0, "O->T Network Connection ID", HFILL } }, { &hf_cip_cm_to_connid, { "T->O Network Connection ID", "cip.cm.to_connid", FT_UINT32, BASE_HEX, NULL, 0, "T->O Network Connection ID", HFILL } }, { &hf_cip_cm_conn_serial_num, { "Connection Serial Number", "cip.cm.conn_serial_num", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } }, { &hf_cip_cm_orig_serial_num, { "Originator Serial Number", "cip.cm.orig_serial_num", FT_UINT32, BASE_HEX, NULL, 0, NULL, 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_lfwo_con_size, { "Connection Size", "cip.cm.fwo.consize", FT_UINT32, BASE_DEC, NULL, 0xFFFF, "Large 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_lfwo_fixed_var, { "Connection Size Type", "cip.cm.fwo.f_v", FT_UINT32, BASE_DEC, VALS(cip_con_fw_vals), 0x02000000, "Large 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_lfwo_prio, { "Priority", "cip.cm.fwo.prio", FT_UINT32, BASE_DEC, VALS(cip_con_prio_vals), 0x0C000000, "Large 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_lfwo_typ, { "Connection Type", "cip.cm.fwo.type", FT_UINT32, BASE_DEC, VALS(cip_con_type_vals), 0x60000000, "Large 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_lfwo_own, { "Owner", "cip.cm.fwo.owner", FT_UINT32, BASE_DEC, VALS(cip_con_owner_vals), 0x80000000, "Large 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 } }, { &hf_cip_cm_gco_conn, { "Number of Connections", "cip.cm.gco.conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: Number of Connections", HFILL } }, { &hf_cip_cm_gco_coo_conn, { "COO Connections", "cip.cm.gco.coo_conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: COO Connections", HFILL } }, { &hf_cip_cm_gco_roo_conn, { "ROO Connections", "cip.cm.gco.roo_conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: ROO Connections", HFILL } }, { &hf_cip_cm_gco_la, { "Last Action", "cip.cm.gco.la", FT_UINT8, BASE_DEC, VALS(cip_con_last_action_vals), 0, "GetConnOwner: Last Action", HFILL } } }; static hf_register_info hf_cco[] = { { &hf_cip_cco_con_type, { "Connection O_T", "cip.cco.con", FT_UINT16, BASE_DEC, VALS(cip_con_vals), 0x001, "Connection", HFILL } }, { &hf_cip_cco_ot_rtf, { "O->T real time transfer format", "cip.cco.otrtf", FT_UINT16, BASE_DEC, VALS(cip_con_rtf_vals), 0x000E, "O->T real time transfer", HFILL } }, { &hf_cip_cco_to_rtf, { "T->O real time transfer format", "cip.cco.tortf", FT_UINT16, BASE_DEC, VALS(cip_con_rtf_vals), 0x0070, "T->O real time transfer", HFILL } }, }; /* Setup protocol subtree array */ 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_iomap, &ett_cco_con_status, &ett_cco_con_flag, &ett_cco_tdi, &ett_cco_ncp, &ett_cco_rrsc, &ett_cco_cmd_data }; /* Register the protocol name and description */ proto_cip = proto_register_protocol("Common Industrial Protocol", "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_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) { /* Create dissector handles */ /* Register for UCMM CIP data, using EtherNet/IP SendRRData service*/ /* Register for Connected CIP data, using EtherNet/IP SendUnitData service*/ cip_handle = new_create_dissector_handle( dissect_cip, proto_cip ); dissector_add_uint( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle ); dissector_add_uint( "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_uint( "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_uint( "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_uint( "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_uint( "cip.class.iface", CI_CLS_CCO, cip_class_cco_handle ); } /* end of proto_reg_handoff_cip() */ /* * Editor modelines * * Local Variables: * c-basic-offset: 3 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=3 tabstop=8 expandtab * :indentSize=3:tabSize=8:noTabs=true: */