aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-cip.c
diff options
context:
space:
mode:
authorJared Rittle <jrittle@cisco.com>2018-04-02 15:28:04 -0400
committerAnders Broman <a.broman58@gmail.com>2018-04-05 05:24:08 +0000
commit49eeba22c59090e911f0b2910eec8dd6477ff466 (patch)
treee12fca4894d224e1581c6c6e59cd5ecee951016d /epan/dissectors/packet-cip.c
parent62af7654388645a2479193bd1a280b65c1e714e0 (diff)
ENIP-CIP-PCCC: add support for the CIP PCCC Object
Support has been added to the EtherNet/IP (ENIP) CIP dissector (packet-cip.c) to allow for the dissection of Programmable Controller Communication Commands (PCCC). These commands are often seen in the traffic of Programmable Logic Controllers (PLCs) communicating over the EtherNet/IP or DF1 protocols. Dissection has been implemented directly in the packet-cip dissector, following the structure put in place by other existing CIP classes such as the Connection Manager. Change-Id: Ia1e614fb7fd9fd36e79c0dac06cd5f1a9c1cea78 Reviewed-on: https://code.wireshark.org/review/26713 Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-cip.c')
-rw-r--r--epan/dissectors/packet-cip.c477
1 files changed, 471 insertions, 6 deletions
diff --git a/epan/dissectors/packet-cip.c b/epan/dissectors/packet-cip.c
index acf72ac50d..68b4cb79ad 100644
--- a/epan/dissectors/packet-cip.c
+++ b/epan/dissectors/packet-cip.c
@@ -17,6 +17,10 @@
* Heuristic object support for common services
* Michael Mann * Copyright 2011
*
+ * Added support for PCCC Objects
+ * Jared Rittle - Cisco Talos
+ * Copyright 2017
+ *
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
@@ -52,6 +56,7 @@ typedef struct mr_mult_req_info {
static dissector_handle_t cip_class_generic_handle;
static dissector_handle_t cip_class_cm_handle;
+static dissector_handle_t cip_class_pccc_handle;
static dissector_handle_t modbus_handle;
static dissector_handle_t cip_class_cco_handle;
static heur_dissector_list_t heur_subdissector_service;
@@ -62,6 +67,7 @@ static gboolean cip_enhanced_info_column = TRUE;
static int proto_cip = -1;
static int proto_cip_class_generic = -1;
static int proto_cip_class_cm = -1;
+static int proto_cip_class_pccc = -1;
static int proto_cip_class_mb = -1;
static int proto_cip_class_cco = -1;
static int proto_enip = -1;
@@ -147,6 +153,27 @@ static int hf_cip_cm_ext126_size = -1;
static int hf_cip_cm_ext127_size = -1;
static int hf_cip_cm_ext128_size = -1;
+static int hf_cip_pccc_sc = -1;
+static int hf_cip_pccc_req_id_len = -1;
+static int hf_cip_pccc_cip_vend_id = -1;
+static int hf_cip_pccc_cip_serial_num = -1;
+static int hf_cip_pccc_cmd_code = -1;
+static int hf_cip_pccc_sts_code = -1;
+static int hf_cip_pccc_ext_sts_code = -1;
+static int hf_cip_pccc_tns_code = -1;
+static int hf_cip_pccc_fnc_code = -1;
+static int hf_cip_pccc_byte_size = -1;
+static int hf_cip_pccc_file_num = -1;
+static int hf_cip_pccc_file_type = -1;
+static int hf_cip_pccc_element_num = -1;
+static int hf_cip_pccc_subelement_num = -1;
+static int hf_cip_pccc_cpu_mode = -1;
+static int hf_cip_pccc_resp_code = -1;
+static int hf_cip_pccc_execute_multi_count = -1;
+static int hf_cip_pccc_execute_multi_len = -1;
+static int hf_cip_pccc_execute_multi_fnc = -1;
+static int hf_cip_pccc_data = -1;
+
static int hf_cip_mb_sc = -1;
static int hf_cip_mb_read_coils_start_addr = -1;
static int hf_cip_mb_read_coils_num_coils = -1;
@@ -491,6 +518,7 @@ static int hf_32bitheader_run_idle = -1;
static gint ett_cip = -1;
static gint ett_cip_class_generic = -1;
static gint ett_cip_class_cm = -1;
+static gint ett_cip_class_pccc = -1;
static gint ett_cip_class_mb = -1;
static gint ett_cip_class_cco = -1;
@@ -532,6 +560,10 @@ static gint ett_cm_add_status_item = -1;
static gint ett_cip_cm_pid = -1;
static gint ett_cip_cm_safety = -1;
+static gint ett_pccc_rrsc = -1;
+static gint ett_pccc_req_id = -1;
+static gint ett_pccc_cmd_data = -1;
+
static gint ett_mb_rrsc = -1;
static gint ett_mb_cmd_data = -1;
@@ -640,6 +672,16 @@ static const value_string cip_sc_vals_cm[] = {
{ 0, NULL }
};
+/* Translate function to string - CIP Service codes for PCCC */
+static const value_string cip_sc_vals_pccc[] = {
+ GENERIC_SC_LIST
+
+ /* Some class specific services */
+ { SC_PCCC_EXECUTE_PCCC, "Execute PCCC" },
+
+ { 0, NULL }
+};
+
/* Translate function to string - CIP Service codes for MB */
static const value_string cip_sc_vals_mb[] = {
GENERIC_SC_LIST
@@ -887,8 +929,6 @@ static const value_string cip_security_state_vals[] = {
{ 0, NULL }
};
-
-
static const value_string cip_path_seg_vals[] = {
{ ((CI_PORT_SEGMENT>>5)&7), "Port Segment" },
{ ((CI_LOGICAL_SEGMENT>>5)&7), "Logical Segment" },
@@ -1255,6 +1295,184 @@ static const value_string cip_cm_ext_st_vals[] = {
value_string_ext cip_cm_ext_st_vals_ext = VALUE_STRING_EXT_INIT(cip_cm_ext_st_vals);
+/* Translate function to string - PCCC Status codes */
+static const value_string cip_pccc_gs_st_vals[] = {
+ { PCCC_GS_SUCCESS, "Success" },
+ { PCCC_GS_ILLEGAL_CMD, "Illegal command or format" },
+ { PCCC_GS_HOST_COMMS, "Host has a problem and will not communicate" },
+ { PCCC_GS_MISSING_REMOTE_NODE, "Remote node host is missing, disconnected, or shut down" },
+ { PCCC_GS_HARDWARE_FAULT, "Host could not complete function due to hardware fault" },
+ { PCCC_GS_ADDRESSING_ERROR, "Addressing problem or memory protect rungs" },
+ { PCCC_GS_CMD_PROTECTION, "Function not allowed due to command protection selection" },
+ { PCCC_GS_PROGRAM_MODE, "Processor is in Program mode" },
+ { PCCC_GS_MISSING_COMPATABILITY_FILE, "Compatibility mode file missing or communication zone problem" },
+ { PCCC_GS_BUFFER_FULL_1, "Remote node cannot buffer command" },
+ { PCCC_GS_WAIT_ACK, "Wait ACK (1775-KA buffer full)" },
+ { PCCC_GS_REMOTE_DOWNLOAD_ERROR, "Remote node problem due to download" },
+ { PCCC_GS_BUFFER_FULL_2, "Wait ACK (1775-KA buffer full)" },
+ { PCCC_GS_NOT_USED_1, "Not used" },
+ { PCCC_GS_NOT_USED_2, "Not used" },
+ { PCCC_GS_USE_EXTSTS, "Error code in the EXT STS byte" },
+
+ { 0, NULL }
+};
+
+value_string_ext cip_pccc_gs_st_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_gs_st_vals);
+
+/* Translate function to string - PCCC Extended Status codes */
+static const value_string cip_pccc_es_st_vals[] = {
+ { PCCC_ES_ILLEGAL_VALUE, "A field has an illegal value" },
+ { PCCC_ES_SHORT_ADDRESS, "Less levels specified in address than minimum for any address" },
+ { PCCC_ES_LONG_ADDRESS, "More levels specified in address than system supports" },
+ { PCCC_ES_NOT_FOUND, "Symbol not found" },
+ { PCCC_ES_BAD_FORMAT, "Symbol is of improper format" },
+ { PCCC_ES_BAD_POINTER, "Address doesn't point to something usable" },
+ { PCCC_ES_BAD_SIZE, "File is wrong size" },
+ { PCCC_ES_SITUATION_CHANGED, "Cannot complete request, situation has changed since the start of the command" },
+ { PCCC_ES_DATA_TOO_LARGE, "Data or file is too large" },
+ { PCCC_ES_TRANS_TOO_LARGE, "Transaction size plus word address is too large" },
+ { PCCC_ES_ACCESS_DENIED, "Access denied, improper privilege" },
+ { PCCC_ES_NOT_AVAILABLE, "Condition cannot be generated - resource is not available" },
+ { PCCC_ES_ALREADY_EXISTS, "Condition already exists - resource is already available" },
+ { PCCC_ES_NO_EXECUTION, "Command cannot be executed" },
+ { PCCC_ES_HIST_OVERFLOW, "Histogram overflow" },
+ { PCCC_ES_NO_ACCESS, "No access" },
+ { PCCC_ES_ILLEGAL_DATA_TYPE, "Illegal data type" },
+ { PCCC_ES_INVALID_DATA, "Invalid parameter or invalid data" },
+ { PCCC_ES_BAD_REFERENCE, "Address reference exists to deleted area" },
+ { PCCC_ES_EXECUTION_FAILURE, "Command execution failure for unknown reason; possible PLC-3 histogram overflow" },
+ { PCCC_ES_CONVERSION_ERROR, "Data conversion error" },
+ { PCCC_ES_NO_COMMS, "Scanner not able to communicate with 1771 rack adapter" },
+ { PCCC_ES_TYPE_MISMATCH, "Type mismatch" },
+ { PCCC_ES_BAD_RESPONSE, "1771 module response was not valid" },
+ { PCCC_ES_DUP_LABEL, "Duplicated label" },
+ { PCCC_ES_RACK_FAULT, "Remote rack fault" },
+ { PCCC_ES_TIMEOUT, "Timeout" },
+ { PCCC_ES_UNKNOWN, "Unknown error" },
+ { PCCC_ES_FILE_ALREADY_OPEN, "File is open; another node owns it" },
+ { PCCC_ES_PROGRAM_ALREADY_OWNED, "Another node is the program owner" },
+ { PCCC_ES_RESERVED_1, "Reserved" },
+ { PCCC_ES_RESERVED_2, "Reserved" },
+ { PCCC_ES_PROTECTION_VIOLATION, "Data table element protection violation" },
+ { PCCC_ES_TMP_INTERNAL_ERROR, "Temporary internal problem" },
+
+ { 0, NULL }
+};
+
+value_string_ext cip_pccc_es_st_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_es_st_vals);
+
+/* Translate PCCC Function Codes */
+static const value_string cip_pccc_fnc_vals[] = {
+ { PCCC_FNC_06_00, "Echo" },
+ { PCCC_FNC_06_01, "Read diagnostic counters" },
+ { PCCC_FNC_06_02, "Set variables" },
+ { PCCC_FNC_06_03, "Diagnostic status" },
+ { PCCC_FNC_06_04, "Set timeout" },
+ { PCCC_FNC_06_05, "Set NAKs" },
+ { PCCC_FNC_06_06, "Set ENQs" },
+ { PCCC_FNC_06_07, "Reset diagnostic counters" },
+ { PCCC_FNC_06_08, "Set data table size" },
+ { PCCC_FNC_06_09, "Read link parameters" },
+ { PCCC_FNC_06_0A, "Set link parameters" },
+ { PCCC_FNC_07_00, "Disable outputs" },
+ { PCCC_FNC_07_01, "Enable outputs" },
+ { PCCC_FNC_07_03, "Enable PLC scanning" },
+ { PCCC_FNC_07_04, "Enter download mode" },
+ { PCCC_FNC_07_05, "Exit download/upload mode" },
+ { PCCC_FNC_07_06, "Enter upload mode" },
+ { PCCC_FNC_0F_00, "Word range write" },
+ { PCCC_FNC_0F_01, "Word range read" },
+ { PCCC_FNC_0F_02, "Bit write" },
+ { PCCC_FNC_0F_03, "File write" },
+ { PCCC_FNC_0F_04, "File read" },
+ { PCCC_FNC_0F_05, "Download request" },
+ { PCCC_FNC_0F_06, "Upload" },
+ { PCCC_FNC_0F_07, "Shutdown" },
+ { PCCC_FNC_0F_08, "Physical write" },
+ { PCCC_FNC_0F_09, "Physical read" },
+ { PCCC_FNC_0F_0A, "Restart request" },
+ { PCCC_FNC_0F_11, "Get edit resource" },
+ { PCCC_FNC_0F_12, "Return edit resource" },
+ { PCCC_FNC_0F_17, "Read bytes physical" },
+ { PCCC_FNC_0F_18, "Write bytes physical" },
+ { PCCC_FNC_0F_26, "Read-modify-write" },
+ { PCCC_FNC_0F_29, "Read section size" },
+ { PCCC_FNC_0F_3A, "Set CPU mode" },
+ { PCCC_FNC_0F_41, "Disable forces" },
+ { PCCC_FNC_0F_50, "Download all request" },
+ { PCCC_FNC_0F_52, "Download completed" },
+ { PCCC_FNC_0F_53, "Upload all request (upload)" },
+ { PCCC_FNC_0F_55, "Upload completed" },
+ { PCCC_FNC_0F_57, "Initialize memory" },
+ { PCCC_FNC_0F_5E, "Modify PLC-2 compatibility file" },
+ { PCCC_FNC_0F_67, "Typed write" },
+ { PCCC_FNC_0F_68, "Typed read" },
+ { PCCC_FNC_0F_79, "Read-modify-write N" },
+ { PCCC_FNC_0F_80, "Change CPU mode" },
+ { PCCC_FNC_0F_81, "Open file" },
+ { PCCC_FNC_0F_82, "Close file" },
+ { PCCC_FNC_0F_88, "Execute Multiple Commands" },
+ { PCCC_FNC_0F_8F, "Apply port configuration" },
+ { PCCC_FNC_0F_A1, "Protected typed logical read with two address fields" },
+ { PCCC_FNC_0F_A2, "Protected typed logical read with three address fields" },
+ { PCCC_FNC_0F_A7, "Protected typed file read" },
+ { PCCC_FNC_0F_A9, "Protected typed logical write with two address fields" },
+ { PCCC_FNC_0F_AA, "Protected typed logical write with three address fields" },
+ { PCCC_FNC_0F_AB, "Protected typed logical masked-write with three address fields" },
+ { PCCC_FNC_0F_AF, "Protected typed file write" },
+
+ { 0, NULL }
+};
+
+value_string_ext cip_pccc_fnc_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_fnc_vals);
+
+/* Translate PCCC File Types */
+static const value_string cip_pccc_file_types_vals[] = {
+ { PCCC_FILE_TYPE_LOGIC, "Ladder Logic File" },
+ { PCCC_FILE_TYPE_CHANNEL_CONFIG, "Channel Configuration File" },
+ { PCCC_FILE_TYPE_FUNCTION_ES1, "EtherNet/IP Function File" },
+ { PCCC_FILE_TYPE_ONLINE_EDIT, "Online Editing File" },
+ { PCCC_FILE_TYPE_FUNCTION_IOS, "IOS Function File" },
+ { PCCC_FILE_TYPE_DATA_OUTPUT, "Output Data File" },
+ { PCCC_FILE_TYPE_DATA_INPUT, "Input Data File" },
+ { PCCC_FILE_TYPE_DATA_STATUS, "Status Data File" },
+ { PCCC_FILE_TYPE_DATA_BINARY, "Binary Data File" },
+ { PCCC_FILE_TYPE_DATA_TIMER, "Timer Data File" },
+ { PCCC_FILE_TYPE_DATA_COUNTER, "Counter Data File" },
+ { PCCC_FILE_TYPE_DATA_INTEGER, "Integer Data File" },
+ { PCCC_FILE_TYPE_DATA_FLOAT, "Float Data File" },
+ { PCCC_FILE_TYPE_FORCE_OUTPUT, "Output Force File" },
+ { PCCC_FILE_TYPE_FORCE_INPUT, "Input Force File" },
+ { PCCC_FILE_TYPE_FUNCTION_ES0, "ES0 Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_STI, "STI Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_EII, "EII Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_RTC, "RTC Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_BHI, "BHI Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_MMI, "Memory Module Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_LCD, "Built-in LCD Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_PTOX, "PTOX Function File" },
+ { PCCC_FILE_TYPE_FUNCTION_PWMX, "PWMX Function File" },
+
+ { 0, NULL }
+};
+
+value_string_ext cip_pccc_file_type_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_file_types_vals);
+
+/* Translate PCCC CPU Modes */
+static const value_string cip_pccc_cpu_mode_vals[] = {
+ { PCCC_CPU_3A_PROGRAM, "Remote Program" },
+ { PCCC_CPU_3A_RUN, "Remote Run" },
+ { PCCC_CPU_80_PROGRAM, "Remote Program" },
+ { PCCC_CPU_80_RUN, "Remote Run" },
+ { PCCC_CPU_80_TEST_CONT, "Remote Test Continuous" },
+ { PCCC_CPU_80_TEST_SINGLE, "Remote Test Single" },
+ { PCCC_CPU_80_TEST_DEBUG, "Remote Test Debug" },
+
+ { 0, NULL }
+};
+
+value_string_ext cip_pccc_cpu_mode_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_cpu_mode_vals);
+
/* Translate Vendor IDs */
static const value_string cip_vendor_vals[] = {
{ 0, "Reserved" },
@@ -2484,7 +2702,6 @@ static const value_string cip_vendor_vals[] = {
value_string_ext cip_vendor_vals_ext = VALUE_STRING_EXT_INIT(cip_vendor_vals);
-
/* Translate Device Profile's */
static const value_string cip_devtype_vals[] = {
{ 0x00, "Generic Device (deprecated)" },
@@ -2611,6 +2828,7 @@ static const value_string cip_class_names_vals[] = {
{ 0x5D, "CIP Security" },
{ 0x5E, "EtherNet/IP Security" },
{ 0x5F, "Certificate Management" },
+ { 0x67, "PCCC Class" },
{ 0xF0, "ControlNet" },
{ 0xF1, "ControlNet Keeper" },
{ 0xF2, "ControlNet Scheduling" },
@@ -2719,6 +2937,13 @@ void add_cip_service_to_info_column(packet_info *pinfo, guint8 service, const va
col_set_fence(pinfo->cinfo, COL_INFO);
}
+void add_cip_pccc_function_to_info_column(packet_info *pinfo, guint8 fnc, const value_string* fnc_vals)
+{
+ col_append_fstr( pinfo->cinfo, COL_INFO,
+ " - %s", val_to_str(fnc, fnc_vals, "Function (0x%02x)"));
+ col_set_fence(pinfo->cinfo, COL_INFO);
+}
+
static int dissect_id_revision(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
@@ -3418,7 +3643,6 @@ static int dissect_port_node_range(packet_info *pinfo _U_, proto_tree *tree, pro
return 4;
}
-
static attribute_info_t cip_attribute_vals[] = {
/* Identity Object (class attributes) */
{0x01, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
@@ -6712,6 +6936,209 @@ dissect_cip_class_cm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *
/************************************************
*
+ * Dissector for CIP PCCC Object
+ *
+ ************************************************/
+static void
+dissect_cip_pccc_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+ proto_item *rrsc_item;
+ proto_tree *rrsc_tree, *req_id_tree, *pccc_cmd_tree, *cmd_data_tree;
+ int req_path_size;
+ unsigned char service;
+ int add_status;
+
+ service = tvb_get_guint8( tvb, offset );
+
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP PCCC");
+
+ /* Add Service code & Request/Response tree */
+ rrsc_tree = proto_tree_add_subtree( item_tree, tvb, offset, 1, ett_pccc_rrsc, &rrsc_item, "Service: " );
+
+ /* Add Request/Response */
+ proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN );
+
+ /* watch for service collisions */
+ proto_item_append_text( rrsc_item, "%s (%s)",
+ val_to_str( ( service & CIP_SC_MASK ),
+ cip_sc_vals_pccc , "Unknown Service (0x%02x)"),
+ val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7,
+ cip_sc_rr, "") );
+
+ /* Add Service code */
+ proto_tree_add_item(rrsc_tree, hf_cip_pccc_sc, tvb, offset, 1, ENC_LITTLE_ENDIAN );
+ add_cip_service_to_info_column (pinfo, service, cip_sc_vals_pccc);
+
+ /* There is a minimum of two bytes different between the request and response request path */
+ /* Response message */
+ if ( service & CIP_SC_RESPONSE_MASK )
+ {
+ req_path_size = 2 + tvb_get_guint8( tvb, offset+2 )*2;
+ }
+ /* Request message */
+ else
+ {
+ req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+ }
+
+ int req_id_offset = offset+req_path_size+2;
+ int req_id_size = tvb_get_guint8( tvb, req_id_offset );
+ int pccc_cmd_offset = req_id_offset+req_id_size;
+
+ /* Add Requestor ID tree */
+ req_id_tree = proto_tree_add_subtree( item_tree, tvb, req_id_offset, req_id_size, ett_pccc_req_id, NULL, "Requestor ID" );
+ /* Add Length of Requestor ID code */
+ proto_tree_add_item(req_id_tree, hf_cip_pccc_req_id_len, tvb, req_id_offset, 1, ENC_LITTLE_ENDIAN );
+ /* Add CIP Vendor ID */
+ proto_tree_add_item(req_id_tree, hf_cip_pccc_cip_vend_id, tvb, req_id_offset+1, 2, ENC_LITTLE_ENDIAN );
+ /* Add CIP Serial Number */
+ proto_tree_add_item(req_id_tree, hf_cip_pccc_cip_serial_num, tvb, req_id_offset+3, 4, ENC_LITTLE_ENDIAN );
+
+ if( service & CIP_SC_RESPONSE_MASK )
+ {
+ /* Add PCCC Response Data tree */
+ pccc_cmd_tree = proto_tree_add_subtree( item_tree, tvb, pccc_cmd_offset, item_length-req_path_size-2-req_id_size, ett_pccc_req_id, NULL, "PCCC Response Data" );
+
+ /* Add Command Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_resp_code, tvb, pccc_cmd_offset, 1, ENC_LITTLE_ENDIAN );
+ /* Add Status Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_sts_code, tvb, pccc_cmd_offset+1, 1, ENC_LITTLE_ENDIAN );
+ /* Add Transaction Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_tns_code, tvb, pccc_cmd_offset+2, 2, ENC_LITTLE_ENDIAN );
+
+ /* Check the status byte for the EXT_STS signifier - 0xF0 */
+ add_status = tvb_get_guint8( tvb, pccc_cmd_offset+1 );
+ // TODO: still need to test this
+ if ( add_status == PCCC_GS_USE_EXTSTS )
+ {
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_ext_sts_code, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN );
+ }
+ // handle cases where data is returned in the response
+ else if (item_length-req_path_size-2-req_id_size-4 != 0)
+ {
+ /* Add the data tree */
+ cmd_data_tree = proto_tree_add_subtree( pccc_cmd_tree, tvb, pccc_cmd_offset+4, item_length-req_path_size-2-req_id_size-4, ett_pccc_cmd_data, NULL, "Function Specific Response Data" );
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+4, item_length-req_path_size-2-req_id_size-4, ENC_NA);
+ }
+
+ } /* end of if reply */
+
+ /* Request message */
+ else
+ {
+ /* If there is any command specific data create a sub-tree for it */
+ if( (item_length-req_path_size-2) != 0 )
+ {
+ /* Add PCCC CMD Data tree */
+ pccc_cmd_tree = proto_tree_add_subtree( item_tree, tvb, pccc_cmd_offset, item_length-req_path_size-2-req_id_size, ett_pccc_req_id, NULL, "PCCC Command Data" );
+
+ /* Add Command Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_cmd_code, tvb, pccc_cmd_offset, 1, ENC_LITTLE_ENDIAN );
+ /* Add Status Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_sts_code, tvb, pccc_cmd_offset+1, 1, ENC_LITTLE_ENDIAN );
+ /* Add Transaction Code */
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_tns_code, tvb, pccc_cmd_offset+2, 2, ENC_LITTLE_ENDIAN );
+ /* Add Function Code */
+ int pccc_cmd_value = tvb_get_guint8( tvb, pccc_cmd_offset);
+ if ((pccc_cmd_value != PCCC_CMD_00 ) && (pccc_cmd_value != PCCC_CMD_01 ) && (pccc_cmd_value != PCCC_CMD_02 ) && (pccc_cmd_value != PCCC_CMD_04 ) && (pccc_cmd_value != PCCC_CMD_05 ) && (pccc_cmd_value != PCCC_CMD_08 ))
+ {
+ proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_fnc_code, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN );
+ }
+
+ guint8 cmd_code, fnc_code;
+ cmd_code = tvb_get_guint8( tvb, pccc_cmd_offset );
+ fnc_code = tvb_get_guint8( tvb, pccc_cmd_offset+4 );
+ add_cip_pccc_function_to_info_column(pinfo, fnc_code, cip_pccc_fnc_vals);
+
+ if (item_length-req_path_size-2-req_id_size-5 != 0 )
+ {
+ /* Add the data tree */
+ cmd_data_tree = proto_tree_add_subtree( pccc_cmd_tree, tvb, pccc_cmd_offset+5, item_length-req_path_size-req_id_size-7,
+ ett_pccc_cmd_data, NULL, "Function Specific Data" );
+
+ int running_offset = pccc_cmd_offset+6;
+ int num_cmds;
+ int sub_fnc_len;
+ proto_tree *sub_fnc_tree;
+
+ /* Add in parsing of instructions that contain data beyond the FNC code */
+ /* Instructions that end at the FNC codes are already processed */
+ switch(cmd_code)
+ {
+ case PCCC_CMD_0F:
+ switch(fnc_code){
+ /* Change CPU Mode */
+ case PCCC_FNC_0F_80:
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_cpu_mode, tvb, pccc_cmd_offset+5, 1, ENC_NA);
+ break;
+ /* Execute Multiple Commands */
+ case PCCC_FNC_0F_88:
+ num_cmds = tvb_get_guint8( tvb, pccc_cmd_offset+5 );
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_execute_multi_count, tvb, pccc_cmd_offset+5, 1, ENC_NA);
+
+ /* iterate over each of the commands and break them out */
+ for( int i=0; i < num_cmds; i++ ){
+ sub_fnc_len = tvb_get_guint8( tvb, running_offset);
+ sub_fnc_tree = proto_tree_add_subtree_format(cmd_data_tree, tvb, running_offset, sub_fnc_len+1, ett_pccc_req_id, NULL, "Sub Function #%d", i+1);
+
+ proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_execute_multi_len, tvb, running_offset, 1, ENC_NA);
+ proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_execute_multi_fnc, tvb, running_offset+1, 1, ENC_NA);
+ if( sub_fnc_len > 2 ){
+ proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_data, tvb, running_offset+2, sub_fnc_len-1, ENC_NA);
+ }
+ running_offset = running_offset+sub_fnc_len+1;
+ }
+ break;
+ /* Protected Typed Logical Read with Three Address Fields */
+ case PCCC_FNC_0F_A2:
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_byte_size, tvb, pccc_cmd_offset+5, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_num, tvb, pccc_cmd_offset+6, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_type, tvb, pccc_cmd_offset+7, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_element_num, tvb, pccc_cmd_offset+8, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_subelement_num, tvb, pccc_cmd_offset+9, 1, ENC_NA);
+ break;
+ /* Protected Typed Logical Write with Three Address Fields */
+ case PCCC_FNC_0F_AA:
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_byte_size, tvb, pccc_cmd_offset+5, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_num, tvb, pccc_cmd_offset+6, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_type, tvb, pccc_cmd_offset+7, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_element_num, tvb, pccc_cmd_offset+8, 1, ENC_NA);
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_subelement_num, tvb, pccc_cmd_offset+9, 1, ENC_NA);
+ int byte_size;
+ byte_size = tvb_get_guint8( tvb, pccc_cmd_offset+5 );
+
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+10, byte_size, ENC_NA);
+ break;
+ default: /* just print the command data if no known command code is passed */
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+5, item_length-pccc_cmd_offset-5, ENC_NA);
+ }
+ break;
+ default: /* just print the command data if no known command code is passed */
+ proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+5, 1, ENC_NA);
+ }
+ }
+ } /* End of if-else( request ) */
+ }
+
+} /* End of dissect_cip_pccc_data() */
+
+static int
+dissect_cip_class_pccc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+ proto_item *ti;
+ proto_tree *class_tree;
+
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip_class_pccc, tvb, 0, -1, ENC_NA);
+ class_tree = proto_item_add_subtree( ti, ett_cip_class_pccc );
+
+ dissect_cip_pccc_data( class_tree, tvb, 0, tvb_reported_length(tvb), pinfo );
+
+ return tvb_reported_length(tvb);
+}
+
+/************************************************
+ *
* Dissector for CIP Modbus Object
*
************************************************/
@@ -7936,6 +8363,30 @@ proto_register_cip(void)
{ &hf_cip_cm_ext128_size, { "Maximum Size", "cip.cm.ext128_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }}
};
+ static hf_register_info hf_pccc[] = {
+ { &hf_cip_pccc_sc, { "Service", "cip.pccc.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_pccc), CIP_SC_MASK, NULL, HFILL }},
+ { &hf_cip_pccc_req_id_len, { "Requestor ID Length", "cip.pccc.req.id.len", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_cip_vend_id, { "CIP Vendor ID", "cip.pccc.cip.vend.id", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_cip_serial_num, { "CIP Serial Number", "cip.pccc.cip.serial.num", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_cmd_code, { "Command Code", "cip.pccc.cmd.code", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_sts_code, { "Status", "cip.pccc.gs.status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_gs_st_vals_ext, 0, NULL, HFILL }},
+ { &hf_cip_pccc_ext_sts_code, { "Extended Status", "cip.pccc.es.status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_es_st_vals_ext, 0, NULL, HFILL }},
+ { &hf_cip_pccc_tns_code, { "Transaction Code", "cip.pccc.tns.code", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_fnc_code, { "Function Code", "cip.pccc.fnc.code", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_fnc_vals_ext, 0, NULL, HFILL }},
+ { &hf_cip_pccc_byte_size, { "Byte Size", "cip.pccc.byte.size", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_file_num, { "File Number", "cip.pccc.file.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_file_type, { "File Type", "cip.pccc.file.type", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_file_type_vals_ext, 0, NULL, HFILL }},
+ { &hf_cip_pccc_element_num, { "Element Number", "cip.pccc.element.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_subelement_num, { "Sub-Element Number", "cip.pccc.subelement.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_cpu_mode, { "CPU Mode", "cip.pccc.cpu.mode", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_cpu_mode_vals_ext, 0, NULL, HFILL }},
+ { &hf_cip_pccc_resp_code, { "Response Code", "cip.pccc.resp.code", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_execute_multi_count, { "Execute Multiple Command - Number of Commands", "cip.pccc.execute.multi.count", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_execute_multi_len, { "Execute Multiple Command - Command Length", "cip.pccc.execute.multi.count", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+ { &hf_cip_pccc_execute_multi_fnc, { "Execute Multiple Command - Function Code", "cip.pccc.execute.multi.count", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
+
+ { &hf_cip_pccc_data, { "Data", "cip.pccc.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}
+ };
+
static hf_register_info hf_mb[] = {
{ &hf_cip_mb_sc, { "Service", "cip.mb.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_mb), CIP_SC_MASK, NULL, HFILL }},
{ &hf_cip_mb_read_coils_start_addr, { "Starting Address", "cip.mb.read_coils.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
@@ -8077,6 +8528,13 @@ proto_register_cip(void)
&ett_cip_cm_safety
};
+ static gint *ett_pccc[] = {
+ &ett_cip_class_pccc,
+ &ett_pccc_rrsc,
+ &ett_pccc_req_id,
+ &ett_pccc_cmd_data
+ };
+
static gint *ett_mb[] = {
&ett_cip_class_mb,
&ett_mb_rrsc,
@@ -8192,6 +8650,11 @@ proto_register_cip(void)
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_pccc = proto_register_protocol("CIP PCCC Object",
+ "CIPPCCC", "cippccc");
+ proto_register_field_array(proto_cip_class_pccc, hf_pccc, array_length(hf_pccc));
+ proto_register_subtree_array(ett_pccc, array_length(ett_pccc));
+
proto_cip_class_mb = proto_register_protocol("CIP Modbus Object",
"CIPMB", "cipmb");
proto_register_field_array(proto_cip_class_mb, hf_mb, array_length(hf_mb));
@@ -8209,7 +8672,6 @@ proto_register_cip(void)
build_get_attr_all_table();
} /* end of proto_register_cip() */
-
void
proto_reg_handoff_cip(void)
{
@@ -8231,6 +8693,10 @@ proto_reg_handoff_cip(void)
cip_class_cm_handle = 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 the PCCC class */
+ cip_class_pccc_handle = create_dissector_handle( dissect_cip_class_pccc, proto_cip_class_pccc );
+ dissector_add_uint( "cip.class.iface", CI_CLS_PCCC, cip_class_pccc_handle );
+
/* Create and register dissector handle for Modbus Object */
cip_class_mb_handle = create_dissector_handle( dissect_cip_class_mb, proto_cip_class_mb );
dissector_add_uint( "cip.class.iface", CI_CLS_MB, cip_class_mb_handle );
@@ -8246,7 +8712,6 @@ proto_reg_handoff_cip(void)
} /* end of proto_reg_handoff_cip() */
-
/*
* Editor modelines
*