From da8c28dc67ac3d319fa8f97a6f74682043eaf0f2 Mon Sep 17 00:00:00 2001 From: Dylan Ulis Date: Fri, 8 May 2020 13:00:52 -0700 Subject: [PATCH] CIP Motion: Connection Configuration Data 1. Dissect the Motion Configuration Block from the Forward Open 2. Add Motion Attributes related to #1 3. Save the first/last segment for certain segment types in an EPATH. Behavior changes based on the values in first segments for a given type, vs later segments. Change-Id: Id0552a585d158041c13adfa50f4bb164cada79b7 Reviewed-on: https://code.wireshark.org/review/37168 Petri-Dish: Anders Broman Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-cip.c | 132 +++++++++++++++++++++-------- epan/dissectors/packet-cip.h | 13 +++ epan/dissectors/packet-cipmotion.c | 85 ++++++++++++++++++- epan/dissectors/packet-cipmotion.h | 4 +- epan/dissectors/packet-cipsafety.c | 2 +- 5 files changed, 199 insertions(+), 37 deletions(-) diff --git a/epan/dissectors/packet-cip.c b/epan/dissectors/packet-cip.c index 7fb4e5873d..bf3609ada2 100644 --- a/epan/dissectors/packet-cip.c +++ b/epan/dissectors/packet-cip.c @@ -535,7 +535,6 @@ static gint ett_path_seg = -1; static gint ett_mcsc = -1; static gint ett_cia_path = -1; static gint ett_data_seg = -1; -static gint ett_data_seg_data = -1; static gint ett_port_path = -1; static gint ett_network_seg = -1; static gint ett_network_seg_safety = -1; @@ -3886,7 +3885,7 @@ attribute_info_t* cip_get_attribute(guint class_id, guint instance, guint attrib { pattr = &att_array->attrs[j]; if ((pattr->class_id == class_id) && - (instance != (guint)-1) && + (instance != SEGMENT_VALUE_NOT_SET) && (((instance == 0) && (pattr->class_instance == TRUE)) || ((instance != 0) && (pattr->class_instance == FALSE))) && (pattr->attribute == attribute)) { @@ -4701,15 +4700,15 @@ static int dissect_segment_safety(packet_info* pinfo, tvbuff_t* tvb, int offset, return segment_len; } -static int dissect_segment_data_simple(tvbuff_t* tvb, int offset, gboolean generate, - proto_tree* path_seg_tree, proto_item* path_seg_item) +static int dissect_segment_data_simple(packet_info* pinfo, tvbuff_t* tvb, int offset, gboolean generate, + proto_tree* path_seg_tree, proto_item* path_seg_item, cip_simple_request_info_t* req_data) { - /* Segment size */ guint16 seg_size = tvb_get_guint8(tvb, offset + 1) * 2; + int segment_len = seg_size + 2; if (generate) { - proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_data_seg_size_simple, tvb, 0, 0, seg_size); + proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_data_seg_size_simple, tvb, 0, 0, seg_size / 2); proto_item_set_generated(it); } else @@ -4717,24 +4716,32 @@ static int dissect_segment_data_simple(tvbuff_t* tvb, int offset, gboolean gener proto_tree_add_item(path_seg_tree, hf_cip_data_seg_size_simple, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN); } + if (generate) + { + return segment_len; + } + /* Segment data */ - if (seg_size != 0 && generate == FALSE) + if (seg_size != 0) { - proto_item* ds_data_item; - proto_tree* ds_data_tree = proto_tree_add_subtree(path_seg_tree, tvb, offset + 2, 0, ett_data_seg_data, &ds_data_item, "Data"); + int parsed_data_len = 0; + if (req_data && req_data->iClass == CI_CLS_MOTION + && req_data->iConnPointA != SEGMENT_VALUE_NOT_SET + && req_data->iConnPoint != SEGMENT_VALUE_NOT_SET) + { + parsed_data_len += dissect_motion_configuration_block(tvb, pinfo, path_seg_tree, path_seg_item, offset + 2); + } - for (int i = 0; i < seg_size / 2; i++) - proto_tree_add_item(ds_data_tree, hf_cip_data_seg_item, tvb, offset + 2 + (i * 2), 2, ENC_LITTLE_ENDIAN); - - proto_item_set_len(ds_data_item, seg_size); + int remaining_data_len = seg_size - parsed_data_len; + if (remaining_data_len > 0) + { + proto_tree_add_item(path_seg_tree, hf_cip_data_seg_item, tvb, offset + 2 + parsed_data_len, remaining_data_len, ENC_NA); + } } - if (generate == FALSE) - { - proto_item_set_len(path_seg_item, 2 + seg_size); - } + proto_item_set_len(path_seg_item, segment_len); - return 2 + seg_size; + return segment_len; } static int dissect_segment_ansi_extended_symbol(packet_info* pinfo, tvbuff_t* tvb, int offset, @@ -5077,15 +5084,28 @@ int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, pr switch (logical_seg_type) { case CI_LOGICAL_SEG_CLASS_ID: + { + guint32 ClassID; segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo, epath_item, path_seg_tree, path_seg_item, &cia_ret_item, - "Class", cip_class_names_vals, (req_data == NULL) ? NULL : &req_data->iClass, + "Class", cip_class_names_vals, &ClassID, hf_cip_class8, hf_cip_class16, hf_cip_class32); if (segment_len == 0) { return 0; } + if (req_data) + { + req_data->iClass = ClassID; + + // Save the first ClassID separately. + if (req_data->iClassA == SEGMENT_VALUE_NOT_SET) + { + req_data->iClassA = ClassID; + } + } + if (req_data != NULL) { if (cip_enhanced_info_column == TRUE && is_msp_item == FALSE) @@ -5100,13 +5120,33 @@ int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, pr } break; + } case CI_LOGICAL_SEG_INST_ID: + { + guint32 InstanceID; segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo, epath_item, path_seg_tree, path_seg_item, &cia_ret_item, - "Instance", NULL, (req_data == NULL) ? NULL : &req_data->iInstance, + "Instance", NULL, &InstanceID, hf_cip_instance8, hf_cip_instance16, hf_cip_instance32); + if (segment_len == 0) + { + return 0; + } + + if (req_data) + { + req_data->iInstance = InstanceID; + + // Save the first InstanceID separately. + if (req_data->iInstanceA == SEGMENT_VALUE_NOT_SET) + { + req_data->iInstanceA = InstanceID; + } + } + break; + } case CI_LOGICAL_SEG_MBR_ID: segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo, @@ -5138,11 +5178,30 @@ int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, pr break; case CI_LOGICAL_SEG_CON_POINT: + { + guint32 ConnPoint; segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo, - epath_item, path_seg_tree, path_seg_item, &cia_ret_item, - "Connection Point", NULL, (req_data == NULL) ? NULL : &req_data->iConnPoint, - hf_cip_conpoint8, hf_cip_conpoint16, hf_cip_conpoint32); + epath_item, path_seg_tree, path_seg_item, &cia_ret_item, + "Connection Point", NULL, &ConnPoint, + hf_cip_conpoint8, hf_cip_conpoint16, hf_cip_conpoint32); + if (segment_len == 0) + { + return 0; + } + + if (req_data) + { + req_data->iConnPoint = ConnPoint; + + // Save the first ConnPoint separately. + if (req_data->iConnPointA == SEGMENT_VALUE_NOT_SET) + { + req_data->iConnPointA = ConnPoint; + } + } + break; + } case CI_LOGICAL_SEG_SPECIAL: segment_len = dissect_segment_logical_special(pinfo, tvb, offset, generate, @@ -5187,7 +5246,7 @@ int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, pr switch( segment_type & CI_DATA_SEG_TYPE_MASK) { case CI_DATA_SEG_SIMPLE: - segment_len = dissect_segment_data_simple(tvb, offset, generate, path_seg_tree, path_seg_item); + segment_len = dissect_segment_data_simple(pinfo, tvb, offset, generate, path_seg_tree, path_seg_item, req_data); proto_item_append_text(epath_item, "[Data]" ); break; @@ -5241,12 +5300,17 @@ int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, pr void reset_cip_request_info(cip_simple_request_info_t* req_data) { - // All code relies on the fact that if something is (-1), then it's not defined yet. - req_data->iClass = (guint32)-1; - req_data->iInstance = (guint32)-1; - req_data->iAttribute = (guint32)-1; - req_data->iMember = (guint32)-1; - req_data->iConnPoint = (guint32)-1; + req_data->iClass = SEGMENT_VALUE_NOT_SET; + req_data->iClassA = SEGMENT_VALUE_NOT_SET; + + req_data->iInstance = SEGMENT_VALUE_NOT_SET; + req_data->iInstanceA = SEGMENT_VALUE_NOT_SET; + + req_data->iAttribute = SEGMENT_VALUE_NOT_SET; + req_data->iMember = SEGMENT_VALUE_NOT_SET; + + req_data->iConnPoint = SEGMENT_VALUE_NOT_SET; + req_data->iConnPointA = SEGMENT_VALUE_NOT_SET; } void dissect_epath(tvbuff_t *tvb, packet_info *pinfo, proto_tree *path_tree, proto_item *epath_item, int offset, int path_length, @@ -6455,6 +6519,7 @@ dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvb preq_info->connInfo->O2T.type = O2TType; preq_info->connInfo->safety = safety_fwdopen; preq_info->connInfo->ClassID = connection_path.iClass; + // The 2nd Connection Point defines the Motion I/O Format. preq_info->connInfo->ConnPoint = connection_path.iConnPoint; preq_info->connInfo->FwdOpenPathLenBytes = conn_path_size; @@ -7737,7 +7802,7 @@ dissect_cip_cco_data( proto_tree *item_tree, proto_item *ti, tvbuff_t *tvb, int { /* Success responses */ if (((service & CIP_SC_MASK) == SC_GET_ATT_ALL) && - (req_data.iInstance != (guint32)-1)) + (req_data.iInstance != SEGMENT_VALUE_NOT_SET)) { if (req_data.iInstance == 0) { @@ -7801,7 +7866,7 @@ dissect_cip_cco_data( proto_tree *item_tree, proto_item *ti, tvbuff_t *tvb, int break; case SC_SET_ATT_ALL: if ((req_data.iInstance == 0) || - (req_data.iInstance == (guint32)-1)) + (req_data.iInstance == SEGMENT_VALUE_NOT_SET)) { /* Just add raw data */ proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, ENC_NA); @@ -8248,7 +8313,7 @@ proto_register_cip(void) { &hf_cip_data_seg_type, { "Data Segment Type", "cip.data_segment.type", FT_UINT8, BASE_DEC, VALS(cip_data_segment_type_vals), CI_DATA_SEG_TYPE_MASK, NULL, HFILL }}, { &hf_cip_data_seg_size_simple, { "Data Size", "cip.data_segment.size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }}, { &hf_cip_data_seg_size_extended, { "Data Size", "cip.data_segment.size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0, NULL, HFILL } }, - { &hf_cip_data_seg_item, { "Data", "cip.data_segment.data", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }}, + { &hf_cip_data_seg_item, { "Data", "cip.data_segment.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, { &hf_cip_symbol, { "ANSI Symbol", "cip.symbol", FT_STRING, BASE_NONE, NULL, 0, "ANSI Extended Symbol Segment", HFILL }}, { &hf_cip_symbol_size, { "Symbolic Symbol Size", "cip.symbol.size", FT_UINT8, BASE_DEC, NULL, 0x1F, NULL, HFILL } }, { &hf_cip_symbol_ascii, { "ASCII Symbol", "cip.ascii_symbol", FT_STRING, BASE_NONE, NULL, 0, "ASCII Symbol Segment", HFILL } }, @@ -8660,7 +8725,6 @@ proto_register_cip(void) &ett_mcsc, &ett_cia_path, &ett_data_seg, - &ett_data_seg_data, &ett_cmd_data, &ett_port_path, &ett_network_seg, diff --git a/epan/dissectors/packet-cip.h b/epan/dissectors/packet-cip.h index 2bef4ad151..3fd8a9b9ca 100644 --- a/epan/dissectors/packet-cip.h +++ b/epan/dissectors/packet-cip.h @@ -397,11 +397,24 @@ { SC_REMOVE_MEMBER, "Remove Member" }, \ { SC_GROUP_SYNC, "Group Sync" }, \ +#define SEGMENT_VALUE_NOT_SET ((guint32)-1) typedef struct cip_simple_request_info { + // First Class ID + guint32 iClassA; + // Last Class ID guint32 iClass; + + // First Instance ID + guint32 iInstanceA; + // Last Instance ID guint32 iInstance; + guint32 iAttribute; guint32 iMember; + + // First Connection Point + guint32 iConnPointA; + // Last Connection Point guint32 iConnPoint; } cip_simple_request_info_t; diff --git a/epan/dissectors/packet-cipmotion.c b/epan/dissectors/packet-cipmotion.c index a793abd425..6d5fb42daf 100644 --- a/epan/dissectors/packet-cipmotion.c +++ b/epan/dissectors/packet-cipmotion.c @@ -39,6 +39,8 @@ static int proto_cipmotion3 = -1; static int hf_cip_format = -1; static int hf_cip_revision = -1; static int hf_cip_class1_seqnum = -1; +static int hf_configuration_block_format_rev = -1; +static int hf_configuration_block_drive_power_struct_id = -1; static int hf_cip_updateid = -1; static int hf_cip_instance_cnt = -1; static int hf_cip_last_update = -1; @@ -67,6 +69,12 @@ static int hf_cip_motor_cntrl = -1; static int hf_cip_feedback = -1; static int hf_cip_feedback_mode = -1; static int hf_cip_feedback_data_type = -1; + +static int hf_connection_configuration_bits = -1; +static int hf_connection_configuration_bits_power = -1; +static int hf_connection_configuration_bits_safety_bit_valid = -1; +static int hf_connection_configuration_bits_allow_network_safety = -1; + static int hf_cip_axis_control = -1; static int hf_cip_control_status = -1; static int hf_cip_control_status_complete = -1; @@ -271,6 +279,7 @@ static gint ett_time_data_set = -1; static gint ett_inst_data_header = -1; static gint ett_cyclic_data_block = -1; static gint ett_feedback_mode = -1; +static gint ett_connection_configuration_bits = -1; static gint ett_control_mode = -1; static gint ett_feedback_config = -1; static gint ett_command_data_set = -1; @@ -290,6 +299,7 @@ static gint ett_set_cyclic_list = -1; static gint ett_group_sync = -1; static gint ett_axis_status_set = -1; static gint ett_command_control = -1; +static gint ett_configuration_block = -1; static expert_field ei_format_rev_conn_pt = EI_INIT; @@ -768,10 +778,27 @@ static int dissect_feedback_mode(packet_info *pinfo _U_, proto_tree *tree, proto return 1; } +static int dissect_connection_configuration_bits(packet_info* pinfo _U_, proto_tree* tree, proto_item* item _U_, tvbuff_t* tvb, + int offset, int total_len _U_) +{ + static const int* bits[] = { + &hf_connection_configuration_bits_power, + &hf_connection_configuration_bits_safety_bit_valid, + &hf_connection_configuration_bits_allow_network_safety, + NULL + }; + + proto_tree_add_bitmask(tree, tvb, offset, hf_connection_configuration_bits, ett_connection_configuration_bits, bits, ENC_LITTLE_ENDIAN); + + return 1; +} + attribute_info_t cip_motion_attribute_vals[] = { { CI_CLS_MOTION, CIP_ATTR_CLASS, 14, -1, "Node Control", cip_dissector_func, NULL, dissect_node_control }, { CI_CLS_MOTION, CIP_ATTR_CLASS, 15, -1, "Node Status", cip_dissector_func, NULL, dissect_node_status }, { CI_CLS_MOTION, CIP_ATTR_CLASS, 31, -1, "Time Data Set", cip_dissector_func, NULL, dissect_time_data_set }, + { CI_CLS_MOTION, CIP_ATTR_CLASS, 34, -1, "Drive Power Structure Class ID", cip_udint, &hf_configuration_block_drive_power_struct_id, NULL }, + { CI_CLS_MOTION, CIP_ATTR_CLASS, 36, -1, "Connection Configuration Bits", cip_dissector_func, NULL, dissect_connection_configuration_bits }, { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 40, -1, "Control Mode", cip_usint, &hf_cip_motor_cntrl, NULL }, { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 42, -1, "Feedback Mode", cip_dissector_func, NULL, dissect_feedback_mode }, { CI_CLS_MOTION, CIP_ATTR_INSTANCE, 60, -1, "Event Checking Control", cip_dissector_func, NULL, dissect_event_checking_control }, @@ -2152,6 +2179,27 @@ static int dissect_cipmotion3(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tre return dissect_cipmotion(tvb, pinfo, tree, &io_data_input); } +int dissect_motion_configuration_block(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* item, int offset) +{ + proto_item* config_item; + proto_tree* config_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_configuration_block, &config_item, "Motion Configuration Block"); + + proto_tree_add_item(config_tree, hf_configuration_block_format_rev, tvb, offset, 1, ENC_LITTLE_ENDIAN); + int parsed_len = 1; + + parsed_len += dissect_connection_configuration_bits(pinfo, config_tree, item, tvb, offset + parsed_len, 1); + + // 2 reserved bytes + parsed_len += 2; + + proto_tree_add_item(config_tree, hf_configuration_block_drive_power_struct_id, tvb, offset + parsed_len, 4, ENC_LITTLE_ENDIAN); + parsed_len += 4; + + proto_item_set_len(config_item, parsed_len); + + return parsed_len; +} + /* * Function name: proto_register_cipmotion * @@ -2189,6 +2237,19 @@ proto_register_cipmotion(void) FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } }, + + { &hf_configuration_block_format_rev, + { "Format Revision", "cipm.config.format_rev", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + + { &hf_configuration_block_drive_power_struct_id, + { "Drive Power Structure Class ID", "cipm.config.drive_class_id", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_cip_updateid, { "Update Id", "cipm.updateid", FT_UINT8, BASE_DEC, NULL, 0, @@ -2316,6 +2377,7 @@ proto_register_cipmotion(void) FT_UINT8, BASE_DEC, VALS(cip_motor_control_vals), 0, "Cyclic Data Block: Motor Control Mode", HFILL } }, + { &hf_cip_feedback, { "Feedback Information", "cipm.feedback", FT_UINT8, BASE_HEX, NULL, 0, @@ -2331,6 +2393,25 @@ proto_register_cipmotion(void) FT_UINT8, BASE_DEC, VALS(cip_feedback_type_vals), FEEDBACK_DATA_TYPE_BITS, NULL, HFILL } }, + + { &hf_connection_configuration_bits, + { "Connection Configuration Bits", "cipm.ccb", + FT_UINT8, BASE_DEC, NULL, 0, + NULL, HFILL } + }, + { &hf_connection_configuration_bits_power, + { "Verify Power Ratings", "cipm.ccb.verify_power_ratings", + FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x01, + NULL, HFILL } }, + { &hf_connection_configuration_bits_safety_bit_valid, + { "Networked Safety Bit Valid", "cipm.ccb.networked_safety_bit_valid", + FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x02, + NULL, HFILL } }, + { &hf_connection_configuration_bits_allow_network_safety, + { "Allow Networked Safety", "cipm.ccb.allow_networked_safety", + FT_BOOLEAN, 8, TFS(&tfs_true_false), 0x04, + NULL, HFILL } }, + { &hf_cip_axis_control, { "Axis Control", "cipm.axisctrl", FT_UINT8, BASE_DEC, VALS(cip_axis_control_vals), 0, @@ -3303,6 +3384,7 @@ proto_register_cipmotion(void) &ett_inst_data_header, &ett_cyclic_data_block, &ett_feedback_mode, + &ett_connection_configuration_bits, &ett_control_mode, &ett_feedback_config, &ett_command_data_set, @@ -3321,7 +3403,8 @@ proto_register_cipmotion(void) &ett_set_cyclic_list, &ett_group_sync, &ett_axis_status_set, - &ett_command_control + &ett_command_control, + &ett_configuration_block }; static ei_register_info ei[] = { diff --git a/epan/dissectors/packet-cipmotion.h b/epan/dissectors/packet-cipmotion.h index 10a283300b..8f84258e4d 100644 --- a/epan/dissectors/packet-cipmotion.h +++ b/epan/dissectors/packet-cipmotion.h @@ -14,6 +14,8 @@ #include "packet-cip.h" // For attribute_info_t -extern attribute_info_t cip_motion_attribute_vals[18]; +extern int dissect_motion_configuration_block(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* item, int offset); + +extern attribute_info_t cip_motion_attribute_vals[20]; #endif /* PACKET_CIPMOTION_H */ diff --git a/epan/dissectors/packet-cipsafety.c b/epan/dissectors/packet-cipsafety.c index 5ca3d10404..de6a92d7df 100644 --- a/epan/dissectors/packet-cipsafety.c +++ b/epan/dissectors/packet-cipsafety.c @@ -1131,7 +1131,7 @@ dissect_cip_s_validator_data( proto_tree *item_tree, { /* Success responses */ if (((service & CIP_SC_MASK) == SC_GET_ATT_ALL) && - (req_data.iInstance != (guint32)-1) && + (req_data.iInstance != SEGMENT_VALUE_NOT_SET) && (req_data.iInstance != 0)) { dissect_cip_get_attribute_all_rsp(tvb, pinfo, cmd_data_tree, offset + 4 + add_stat_size, &req_data);