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 <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Dylan Ulis 2020-05-08 13:00:52 -07:00 committed by Anders Broman
parent 256cc1a85c
commit da8c28dc67
5 changed files with 199 additions and 37 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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[] = {

View File

@ -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 */

View File

@ -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);