CIP Motion: Version 1.5 Updates

1. Service Data Block: Decode the following services:
  Set Cyclic Write List
  Set Cyclic Read List
  Set Attribute List
2. Axis Status 2 - Add more bit definitions
3. Add a preference to display raw attribute bytes. This is useful
  because not all attributes have parsing, and this allows automated
  tools to consistent pull a common format for all attributes.

Change-Id: Ic7a29f3adddcced8cba958e545436b89c0f7ef6d
Reviewed-on: https://code.wireshark.org/review/35799
Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Dylan Ulis 2020-01-13 18:53:32 -08:00 committed by Anders Broman
parent 4ee1110fa5
commit 6606e95318
1 changed files with 226 additions and 7 deletions

View File

@ -3,7 +3,7 @@
* CIP Motion Home: www.odva.org
*
* This dissector includes items from:
* CIP Volume 9: CIP Motion, Edition 1.3
* CIP Volume 9: CIP Motion, Edition 1.5
*
* Copyright 2006-2007
* Benjamin M. Stocks <bmstocks@ra.rockwell.com>
@ -177,6 +177,17 @@ static int hf_cip_axis_sts2_ac_phase_loss = -1;
static int hf_cip_axis_sts2_ac_freq_change = -1;
static int hf_cip_axis_sts2_ac_sync_loss = -1;
static int hf_cip_axis_sts2_single_phase = -1;
static int hf_cip_axis_sts2_bus_volt_limit = -1;
static int hf_cip_axis_sts2_bus_volt_rate_limit = -1;
static int hf_cip_axis_sts2_active_current_rate_limit = -1;
static int hf_cip_axis_sts2_reactive_current_rate_limit = -1;
static int hf_cip_axis_sts2_reactive_pwr_limit = -1;
static int hf_cip_axis_sts2_reactive_pwr_rate_limit = -1;
static int hf_cip_axis_sts2_active_current_limit = -1;
static int hf_cip_axis_sts2_reactive_current_limit = -1;
static int hf_cip_axis_sts2_motor_pwr_limit = -1;
static int hf_cip_axis_sts2_regen_pwr_limit = -1;
static int hf_cip_axis_sts2_convert_therm_limit = -1;
static int hf_cip_cyclic_wrt_data = -1;
static int hf_cip_cyclic_rd_data = -1;
@ -237,6 +248,10 @@ static int hf_set_axis_attr_list_dimension = -1;
static int hf_set_axis_attr_list_element_size = -1;
static int hf_set_axis_attr_list_start_index = -1;
static int hf_set_axis_attr_list_data_elements = -1;
static int hf_set_cyclic_list_attribute_cnt = -1;
static int hf_set_cyclic_list_attribute_id = -1;
static int hf_set_cyclic_list_read_block_id = -1;
static int hf_set_cyclic_list_attr_sts = -1;
static int hf_var_devce_instance = -1;
static int hf_var_devce_instance_block_size = -1;
static int hf_var_devce_cyclic_block_size = -1;
@ -271,6 +286,7 @@ static gint ett_get_axis_attribute = -1;
static gint ett_set_axis_attribute = -1;
static gint ett_get_axis_attr_list = -1;
static gint ett_set_axis_attr_list = -1;
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;
@ -280,6 +296,8 @@ static expert_field ei_format_rev_conn_pt = EI_INIT;
static dissector_handle_t cipmotion_handle;
static dissector_handle_t cipmotion3_handle;
static gboolean display_full_attribute_data = FALSE;
/* These are the BITMASKS for the Time Data Set header field */
#define TIME_DATA_SET_TIME_STAMP 0x1
#define TIME_DATA_SET_TIME_OFFSET 0x2
@ -529,6 +547,17 @@ static int dissect_axis_status2(packet_info *pinfo _U_, proto_tree *tree, proto_
&hf_cip_axis_sts2_ac_freq_change,
&hf_cip_axis_sts2_ac_sync_loss,
&hf_cip_axis_sts2_single_phase,
&hf_cip_axis_sts2_bus_volt_limit,
&hf_cip_axis_sts2_bus_volt_rate_limit,
&hf_cip_axis_sts2_active_current_rate_limit,
&hf_cip_axis_sts2_reactive_current_rate_limit,
&hf_cip_axis_sts2_reactive_pwr_limit,
&hf_cip_axis_sts2_reactive_pwr_rate_limit,
&hf_cip_axis_sts2_active_current_limit,
&hf_cip_axis_sts2_reactive_current_limit,
&hf_cip_axis_sts2_motor_pwr_limit,
&hf_cip_axis_sts2_regen_pwr_limit,
&hf_cip_axis_sts2_convert_therm_limit,
NULL
};
@ -1373,8 +1402,12 @@ dissect_set_axis_attr_list_request(packet_info *pinfo, tvbuff_t* tvb, proto_tree
int parsed_len = dissect_motion_attribute(pinfo, tvb, local_offset + attribute_start, attribute_id,
instance_id, attr_item, attr_tree, dimension, attribute_size);
// Display any remaining unparsed attribute data.
if ((attribute_size - parsed_len) > 0)
// Display the raw attribute data if configured. Otherwise, just show the remaining unparsed data.
if (display_full_attribute_data)
{
proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
}
else if ((attribute_size - parsed_len) > 0)
{
proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start + parsed_len, attribute_size - parsed_len, ENC_NA);
}
@ -1409,6 +1442,62 @@ dissect_group_sync_request (tvbuff_t* tvb, proto_tree* tree, guint32 offset, gui
proto_tree_add_item(header_tree, hf_cip_ptp_grandmaster, tvb, offset, 8, ENC_LITTLE_ENDIAN);
}
static void dissect_set_cyclic_list_request(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id, const char* service_name)
{
proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_set_cyclic_list, NULL, service_name);
guint32 attribute_cnt;
proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
// Skip Number of Attributes and Reserved field.
offset += 4;
for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
{
guint32 attribute_id;
proto_item* attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
if (pattribute != NULL)
{
proto_item_append_text(attr_item, " (%s)", pattribute->text);
}
offset += 2;
}
}
static void dissect_set_cyclic_list_respone(tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance_id, const char* service_name)
{
proto_tree* header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_set_cyclic_list, NULL, service_name);
guint32 attribute_cnt;
proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_cnt, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_cnt);
proto_tree_add_item(header_tree, hf_set_cyclic_list_read_block_id, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);
// Skip Number of Attributes and Cyclic Read Block ID field.
offset += 4;
for (guint32 attribute = 0; attribute < attribute_cnt; attribute++)
{
guint32 attribute_id;
proto_item* attr_item = proto_tree_add_item_ret_uint(header_tree, hf_set_cyclic_list_attribute_id, tvb, offset, 2, ENC_LITTLE_ENDIAN, &attribute_id);
attribute_info_t* pattribute = cip_get_attribute(CI_CLS_MOTION, instance_id, attribute_id);
if (pattribute != NULL)
{
proto_item_append_text(attr_item, " (%s)", pattribute->text);
}
offset += 2;
proto_tree_add_item(header_tree, hf_set_cyclic_list_attr_sts, tvb, offset, 1, ENC_LITTLE_ENDIAN);
// Skip over Attribute Status and Reserved field.
offset += 2;
}
}
/*
* Function name: dissect_cntr_service
@ -1425,7 +1514,8 @@ dissect_cntr_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint3
guint32 service;
/* Create the tree for the entire service data block */
header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, NULL, "Service Data Block");
proto_item *item;
header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, &item, "Service Data Block");
/* Display the transaction id value */
proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
@ -1447,6 +1537,30 @@ dissect_cntr_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint3
case SC_GROUP_SYNC:
dissect_group_sync_request(tvb, header_tree, offset + 4, size - 4);
break;
case SC_SET_CYCLIC_WRITE_LIST:
dissect_set_cyclic_list_request(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Write List Request");
break;
case SC_SET_CYCLIC_READ_LIST:
dissect_set_cyclic_list_request(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Read List Request");
break;
case SC_SET_ATT_LIST:
{
cip_simple_request_info_t motion_path;
motion_path.iClass = CI_CLS_MOTION;
motion_path.iInstance = instance_id;
tvbuff_t* tvb_set_attr = tvb_new_subset_length(tvb, offset + 4, size - 4);
int parsed_len = dissect_cip_set_attribute_list_req(tvb_set_attr, pinfo, header_tree, item, 0, &motion_path);
// Display any remaining unparsed data.
int remain_len = tvb_reported_length_remaining(tvb, offset + 4 + parsed_len);
if (remain_len > 0)
{
proto_tree_add_item(header_tree, hf_cip_attribute_data, tvb, offset + 4 + parsed_len, size - 4 - parsed_len, ENC_NA);
}
break;
}
default:
/* Display the remainder of the service channel data */
proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
@ -1578,8 +1692,12 @@ dissect_get_axis_attr_list_response(packet_info* pinfo, tvbuff_t* tvb, proto_tre
int parsed_len = dissect_motion_attribute(pinfo, tvb, local_offset + attribute_start, attribute_id,
instance_id, attr_item, attr_tree, dimension, attribute_size);
/* Display the remainder of the service channel data */
if ((attribute_size - parsed_len) > 0)
// Display the raw attribute data if configured. Otherwise, just show the remaining unparsed data
if (display_full_attribute_data)
{
proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start, attribute_size, ENC_NA);
}
else if ((attribute_size - parsed_len) > 0)
{
proto_tree_add_item(attr_tree, hf_cip_attribute_data, tvb, local_offset + attribute_start + parsed_len, attribute_size - parsed_len, ENC_NA);
}
@ -1623,7 +1741,8 @@ dissect_devce_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint
proto_tree *header_tree;
/* Create the tree for the entire service data block */
header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, NULL, "Service Data Block");
proto_item* item;
header_tree = proto_tree_add_subtree(tree, tvb, offset, size, ett_service, &item, "Service Data Block");
/* Display the transaction id value */
proto_tree_add_item(header_tree, hf_cip_svc_transction, tvb, offset, 1, ENC_LITTLE_ENDIAN);
@ -1652,6 +1771,22 @@ dissect_devce_service(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, guint
case SC_GROUP_SYNC:
dissect_group_sync_response(tvb, header_tree, offset + 4);
break;
case SC_SET_CYCLIC_WRITE_LIST:
dissect_set_cyclic_list_respone(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Write List Response");
break;
case SC_SET_CYCLIC_READ_LIST:
dissect_set_cyclic_list_respone(tvb, header_tree, offset + 4, size - 4, instance_id, "Set Cyclic Read List Response");
break;
case SC_SET_ATT_LIST:
{
cip_simple_request_info_t motion_path;
motion_path.iClass = CI_CLS_MOTION;
motion_path.iInstance = instance_id;
tvbuff_t* tvb_set_attr = tvb_new_subset_length(tvb, offset + 4, size - 4);
dissect_cip_set_attribute_list_rsp(tvb_set_attr, pinfo, header_tree, item, 0, &motion_path);
break;
}
default:
/* Display the remainder of the service channel data */
proto_tree_add_item(header_tree, hf_cip_svc_data, tvb, offset + 4, size - 4, ENC_NA);
@ -2768,6 +2903,27 @@ proto_register_cipmotion(void)
"Service Channel: Set Axis Attribute List Data elements", HFILL}
},
{ &hf_set_cyclic_list_attribute_cnt,
{ "Number of attributes", "cipm.set_cyclic.cnt",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_set_cyclic_list_attribute_id,
{ "Attribute ID", "cipm.set_cyclic.id",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_set_cyclic_list_read_block_id,
{ "Cyclic Read Block ID", "cipm.set_cyclic.read_block_id",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_set_cyclic_list_attr_sts,
{ "Attribute Status", "cipm.set_cyclic.sts",
FT_UINT8, BASE_DEC | BASE_EXT_STRING, &cip_gs_vals_ext, 0,
NULL, HFILL }
},
{ &hf_var_devce_instance,
{ "Instance Number", "cipm.var_devce.header.instance",
FT_UINT8, BASE_DEC, NULL, 0,
@ -3007,6 +3163,62 @@ proto_register_cipmotion(void)
NULL, HFILL }
},
{ &hf_cip_axis_sts2_bus_volt_limit,
{ "Bus Voltage Limit", "cipm.axis2.bus_volt_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00002000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_bus_volt_rate_limit,
{ "Bus Voltage Rate Limit", "cipm.axis2.bus_volt_rate_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00004000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_active_current_rate_limit,
{ "Active Current Rate Limit", "cipm.axis2.active_current_rate_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00008000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_reactive_current_rate_limit,
{ "Reactive Current Rate Limit", "cipm.axis2.reactive_current_rate_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00010000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_reactive_pwr_limit,
{ "Reactive Power Limit", "cipm.axis2.reactive_pwr_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00020000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_reactive_pwr_rate_limit,
{ "Reactive Power Rate Limit", "cipm.axis2.reactive_pwr_rate_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00040000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_active_current_limit,
{ "Active Current Limit", "cipm.axis2.active_current_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00080000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_reactive_current_limit,
{ "Reactive Current Limit", "cipm.axis2.reactive_current_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00100000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_motor_pwr_limit,
{ "Motoring Power Limit", "cipm.axis2.motor_pwr_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00200000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_regen_pwr_limit,
{ "Regenerative Power Limit", "cipm.axis2.regen_pwr_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00400000,
NULL, HFILL }
},
{ &hf_cip_axis_sts2_convert_therm_limit,
{ "Converter Thermal Limit", "cipm.axis2.convert_therm_limit",
FT_BOOLEAN, 32, TFS(&tfs_true_false), 0x00800000,
NULL, HFILL }
},
{ &hf_cip_act_pos,
{ "Actual Position", "cipm.actpos",
FT_INT32, BASE_DEC, NULL, 0,
@ -3106,6 +3318,7 @@ proto_register_cipmotion(void)
&ett_set_axis_attribute,
&ett_get_axis_attr_list,
&ett_set_axis_attr_list,
&ett_set_cyclic_list,
&ett_group_sync,
&ett_axis_status_set,
&ett_command_control
@ -3137,6 +3350,12 @@ proto_register_cipmotion(void)
expert_module_t* expert_cipm = expert_register_protocol(proto_cipmotion);
expert_register_field_array(expert_cipm, ei, array_length(ei));
module_t* cipm_module = prefs_register_protocol(proto_cipmotion, NULL);
prefs_register_bool_preference(cipm_module, "display_full_attribute_data",
"Display full attribute data in the Service Data Block",
"Whether the CIP Motion dissector always display the full raw attribute data bytes",
&display_full_attribute_data);
cipmotion_handle = register_dissector("cipmotion", dissect_cipmotion, proto_cipmotion);
cipmotion3_handle = register_dissector("cipmotion3", dissect_cipmotion3, proto_cipmotion3);
}