CIP Motion: Support Format Revision 3

See Volume 9, version 1.2, sections "6-2.7.1.1" and "7-1.1"
1. Pass Connection Point from FwdOpen to Motion dissector, since that is now needed to parse I/O payload.
2. Move Run/Idle Header function to CIP dissector, since it's a CIP feature, not ENIP.
3. Add a protocol so that Format Revision 3 can be dissected without the Forward Open in the capture.
4. Minor: Highlight more bytes in some EPATH parsing.
5. Minor: Renaming some things to match spec wording.

Change-Id: I93626a6492be2675206d38c04fa1c7ce534c04ca
Reviewed-on: https://code.wireshark.org/review/25570
Petri-Dish: Michael Mann <mmann78@netscape.net>
Tested-by: Petri Dish Buildbot
Reviewed-by: Michael Mann <mmann78@netscape.net>
This commit is contained in:
Dylan Ulis 2018-02-02 11:47:44 -05:00 committed by Michael Mann
parent a4bb6c2d39
commit 2d8606b584
5 changed files with 95 additions and 67 deletions

View File

@ -494,6 +494,11 @@ static int hf_conn_path_class = -1;
static int hf_path_len_usint = -1;
static int hf_path_len_uint = -1;
static int hf_32bitheader = -1;
static int hf_32bitheader_roo = -1;
static int hf_32bitheader_coo = -1;
static int hf_32bitheader_run_idle = -1;
/* Initialize the subtree pointers */
static gint ett_cip = -1;
static gint ett_cip_class_generic = -1;
@ -563,6 +568,7 @@ static gint ett_time_sync_port_profile_id_info = -1;
static gint ett_time_sync_port_phys_addr_info = -1;
static gint ett_time_sync_port_proto_addr_info = -1;
static gint ett_id_status = -1;
static gint ett_32bitheader_tree = -1;
static expert_field ei_mal_identity_revision = EI_INIT;
static expert_field ei_mal_identity_status = EI_INIT;
@ -2669,6 +2675,14 @@ const value_string cip_port_number_vals[] = {
value_string_ext cip_class_names_vals_ext = VALUE_STRING_EXT_INIT(cip_class_names_vals);
/* Translate function to string - Run/Idle */
static const value_string cip_run_idle_vals[] = {
{ 0, "Idle" },
{ 1, "Run" },
{ 0, NULL }
};
static void add_cip_class_to_info_column(packet_info *pinfo, guint32 class_id, int display_type)
{
cip_req_info_t *cip_req_info;
@ -3382,7 +3396,10 @@ static int dissect_single_segment_packed_attr(packet_info *pinfo, proto_tree *tr
proto_item *subitem;
subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_port_path, &subitem, "Path: ");
return dissect_cip_segment_single(pinfo, tvb, offset, 0, subtree, subitem, FALSE, TRUE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
int parsed_len = dissect_cip_segment_single(pinfo, tvb, offset, 0, subtree, subitem, FALSE, TRUE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
proto_item_set_len(subitem, parsed_len);
return parsed_len;
}
static int dissect_single_segment_padded_attr(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
@ -3392,7 +3409,10 @@ static int dissect_single_segment_padded_attr(packet_info *pinfo, proto_tree *tr
proto_item *subitem;
subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_port_path, &subitem, "Path: ");
return dissect_cip_segment_single(pinfo, tvb, offset, 0, subtree, subitem, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
int parsed_len = dissect_cip_segment_single(pinfo, tvb, offset, 0, subtree, subitem, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
proto_item_set_len(subitem, parsed_len);
return parsed_len;
}
static int dissect_port_link_object(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
@ -4435,7 +4455,7 @@ static int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int off
case CI_LOGICAL_SEG_CON_POINT:
segment_len = dissect_cia(tvb, offset, pathpos, segment_type, generate, packed, pinfo,
epath_item, cia_item, cia_tree, path_seg_item, &cia_ret_item,
"Connection Point", NULL, NULL,
"Connection Point", NULL, (req_data == NULL) ? NULL : &req_data->iConnPoint,
hf_cip_conpoint8, hf_cip_conpoint16, hf_cip_conpoint32);
if (segment_len == 0)
{
@ -4946,6 +4966,7 @@ void dissect_epath(tvbuff_t *tvb, packet_info *pinfo, proto_tree *path_tree, pro
req_data->iInstance = (guint32)-1;
req_data->iAttribute = (guint32)-1;
req_data->iMember = (guint32)-1;
req_data->iConnPoint = (guint32)-1;
}
if (safety != NULL)
safety->safety_seg = FALSE;
@ -5875,6 +5896,7 @@ void load_cip_request_data(packet_info *pinfo, cip_simple_request_info_t *req_da
req_data->iInstance = (guint32)-1;
req_data->iAttribute = (guint32)-1;
req_data->iMember = (guint32)-1;
req_data->iConnPoint = (guint32)-1;
}
}
@ -6104,6 +6126,7 @@ dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvb
preq_info->connInfo->motion = (connection_path.iClass == 0x42) ? TRUE : FALSE;
preq_info->connInfo->safety = safety_fwdopen;
preq_info->connInfo->ClassID = connection_path.iClass;
preq_info->connInfo->ConnPoint = connection_path.iConnPoint;
}
}
}
@ -7484,6 +7507,18 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info
} /* End of dissect_cip_data() */
void dissect_cip_run_idle(tvbuff_t* tvb, int offset, proto_tree* item_tree)
{
static const int * run_idle_header[] = {
&hf_32bitheader_roo,
&hf_32bitheader_coo,
&hf_32bitheader_run_idle,
NULL
};
proto_tree_add_bitmask(item_tree, tvb, offset, hf_32bitheader, ett_32bitheader_tree, run_idle_header, ENC_LITTLE_ENDIAN);
}
static int
dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
@ -7839,6 +7874,11 @@ proto_register_cip(void)
{ &hf_conn_path_class, { "CIP Connection Path Class", "cip.conn_path_class", FT_UINT16, BASE_HEX | BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
{ &hf_path_len_usint, { "Path Length (words)", "cip.path_len", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_path_len_uint, { "Path Length (words)", "cip.path_len", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
{ &hf_32bitheader, { "32-bit Header", "cip.32bitheader", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
{ &hf_32bitheader_roo, { "ROO", "cip.32bitheader.roo", FT_UINT32, BASE_HEX, NULL, 0xC, "Ready for Ownership of Outputs", HFILL } },
{ &hf_32bitheader_coo, { "COO", "cip.32bitheader.coo", FT_UINT32, BASE_HEX, NULL, 0x2, "Claim Output Ownership", HFILL } },
{ &hf_32bitheader_run_idle, { "Run/Idle", "cip.32bitheader.run_idle", FT_UINT32, BASE_HEX, VALS(cip_run_idle_vals), 0x1, NULL, HFILL } },
};
static hf_register_info hf_cm[] = {
@ -8030,6 +8070,7 @@ proto_register_cip(void)
&ett_time_sync_port_phys_addr_info,
&ett_time_sync_port_proto_addr_info,
&ett_id_status,
&ett_32bitheader_tree,
};
static gint *ett_cm[] = {

View File

@ -235,6 +235,7 @@ typedef struct cip_simple_request_info {
guint32 iInstance;
guint32 iAttribute;
guint32 iMember;
guint32 iConnPoint;
} cip_simple_request_info_t;
enum cip_datatype {
@ -316,6 +317,7 @@ typedef struct cip_conn_info {
cip_safety_epath_info_t safety;
gboolean motion;
guint32 ClassID;
guint32 ConnPoint;
} cip_conn_info_t;
typedef struct cip_req_info {
@ -355,6 +357,7 @@ extern attribute_info_t* cip_get_attribute(guint class_id, guint instance, guint
extern int dissect_cip_get_attribute_all_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
int offset, cip_simple_request_info_t* req_data);
extern void load_cip_request_data(packet_info *pinfo, cip_simple_request_info_t *req_data);
void dissect_cip_run_idle(tvbuff_t* tvb, int offset, proto_tree* item_tree);
/*
** Exported variables

View File

@ -35,6 +35,7 @@ void proto_reg_handoff_cipmotion(void);
/* Protocol handle for CIP Motion */
static int proto_cipmotion = -1;
static int proto_cipmotion3 = -1;
/* Header field identifiers, these are registered in the
* proto_register_cipmotion function along with the bites/bytes
@ -67,7 +68,7 @@ static int hf_cip_data_rx_time_stamp = -1;
static int hf_cip_data_tx_time_stamp = -1;
static int hf_cip_node_fltalarms = -1;
static int hf_cip_motor_cntrl = -1;
static int hf_cip_fdbk_config = -1;
static int hf_cip_feedback_mode = -1;
static int hf_cip_axis_control = -1;
static int hf_cip_control_status = -1;
static int hf_cip_axis_response = -1;
@ -262,6 +263,7 @@ static gint ett_axis_status_set = -1;
static gint ett_command_control = -1;
static dissector_handle_t cipmotion_handle;
static dissector_handle_t cipmotion3_handle;
/* These are the BITMASKS for the Time Data Set header field */
#define TIME_DATA_SET_TIME_STAMP 0x1
@ -327,8 +329,8 @@ static const value_string cip_motor_control_vals[] = {
{ 0, NULL }
};
/* Translate function to string - feedback config values */
static const value_string cip_fdbk_config_vals[] = {
/* Translate function to string - feedback mode values */
static const value_string cip_feedback_mode_vals[] = {
{ 0, "No Feedback" },
{ 1, "Master Feedback" },
{ 2, "Motor Feedback" },
@ -749,8 +751,8 @@ dissect_cntr_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, gui
/* Add the control mode header field to the tree */
proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
/* Add the feedback config header field to the tree */
proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
/* Add the feedback mode header field to the tree */
proto_tree_add_item(header_tree, hf_cip_feedback_mode, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
/* Add the axis control field to the tree */
proto_tree_add_item(header_tree, hf_cip_axis_control, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
@ -817,7 +819,7 @@ dissect_cntr_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, gui
}
/*
* Function name: dissect_devce_cyclic
* Function name: dissect_device_cyclic
*
* Purpose: Dissect the cyclic data block of a device to controller format message
*
@ -825,7 +827,7 @@ dissect_cntr_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, gui
* as their starting offset
*/
static guint32
dissect_devce_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
dissect_device_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, guint32 offset, guint32 size, guint32 instance _U_)
{
proto_item *temp_proto_item;
proto_tree *header_tree, *temp_proto_tree;
@ -838,8 +840,8 @@ dissect_devce_cyclic(guint32 con_format _U_, tvbuff_t* tvb, proto_tree* tree, gu
/* Add the control mode header field to the tree */
proto_tree_add_item(header_tree, hf_cip_motor_cntrl, tvb, offset, 1, ENC_LITTLE_ENDIAN);
/* Add the feedback config header field to the tree */
proto_tree_add_item(header_tree, hf_cip_fdbk_config, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
/* Add the feedback mode header field to the tree */
proto_tree_add_item(header_tree, hf_cip_feedback_mode, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
/* Add the axis response field to the tree */
proto_tree_add_item(header_tree, hf_cip_axis_response, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
@ -1777,13 +1779,14 @@ dissect_var_devce_conn_header(tvbuff_t* tvb, proto_tree* tree, guint32* inst_cou
* Returns: void
*/
static int
dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_)
dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data)
{
guint32 con_format;
guint32 update_id;
proto_item *proto_item_top;
proto_tree *proto_tree_top;
guint32 offset = 0;
guint32 ConnPoint = GPOINTER_TO_UINT(data);
/* Create display subtree for the protocol by creating an item and then
* creating a subtree from the item, the subtree must have been registered
@ -1795,6 +1798,12 @@ dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* dat
proto_tree_add_item(proto_tree_top, hf_cip_class1_seqnum, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset = (offset + 2);
if (ConnPoint >= 3)
{
dissect_cip_run_idle(tvb, offset, proto_tree_top);
offset += 4;
}
/* Pull the actual values for the connection format and update id from the
* incoming message to be used in the column info */
con_format = tvb_get_guint8(tvb, offset);
@ -1856,7 +1865,7 @@ dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* dat
break;
case FORMAT_VAR_DEVICE_TO_CONTROL:
if ( cyc_size > 0 )
offset = dissect_devce_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
offset = dissect_device_cyclic( con_format, tvb, proto_tree_top, offset, cyc_size, instance );
if ( cyc_blk_size > 0 )
offset = dissect_cyclic_rd( tvb, proto_tree_top, offset, cyc_blk_size );
if ( evnt_size > 0 )
@ -1879,6 +1888,12 @@ dissect_cipmotion(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* dat
return tvb_captured_length(tvb);
}
static int dissect_cipmotion3(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_)
{
guint32 ConnPoint = 3;
return dissect_cipmotion(tvb, pinfo, tree, GUINT_TO_POINTER(ConnPoint));
}
/*
* Function name: proto_register_cipmotion
*
@ -2043,10 +2058,10 @@ proto_register_cipmotion(void)
FT_UINT8, BASE_DEC, VALS(cip_motor_control_vals), 0,
"Cyclic Data Block: Motor Control Mode", HFILL }
},
{ &hf_cip_fdbk_config,
{ "Feedback Config", "cipm.fdbkcfg",
FT_UINT8, BASE_DEC, VALS(cip_fdbk_config_vals), 0,
"Cyclic Data Block: Feedback Configuration", HFILL }
{ &hf_cip_feedback_mode,
{ "Feedback Mode", "cipm.feedback_mode",
FT_UINT8, BASE_DEC, VALS(cip_feedback_mode_vals), 0,
"Cyclic Data Block: Feedback Mode", HFILL }
},
{ &hf_cip_axis_control,
{ "Axis Control", "cipm.axisctrl",
@ -2916,7 +2931,14 @@ proto_register_cipmotion(void)
"Common Industrial Protocol, Motion", /* Full name of protocol */
"CIP Motion", /* Short name of protocol */
"cipm"); /* Abbreviated name of protocol */
;
proto_cipmotion3 = proto_register_protocol_in_name_only(
"Common Industrial Protocol, Motion - Rev 3",
"CIP Motion - Rev 3",
"cipm3",
proto_cipmotion,
FT_PROTOCOL);
/* Register the header fields with the protocol */
proto_register_field_array(proto_cipmotion, hf, array_length(hf));
@ -2924,11 +2946,13 @@ proto_register_cipmotion(void)
proto_register_subtree_array(cip_subtree, array_length(cip_subtree));
cipmotion_handle = register_dissector("cipmotion", dissect_cipmotion, proto_cipmotion);
cipmotion3_handle = register_dissector("cipmotion3", dissect_cipmotion3, proto_cipmotion3);
}
void proto_reg_handoff_cipmotion(void)
{
dissector_add_for_decode_as("enip.io", cipmotion_handle);
dissector_add_for_decode_as("enip.io", cipmotion3_handle);
}
/*

View File

@ -154,10 +154,6 @@ static int hf_enip_cpf_itemcount = -1;
static int hf_enip_cpf_typeid = -1;
static int hf_enip_cpf_length = -1;
static int hf_enip_cpf_cdi_seqcnt = -1;
static int hf_enip_cpf_cdi_32bitheader = -1;
static int hf_enip_cpf_cdi_32bitheader_roo = -1;
static int hf_enip_cpf_cdi_32bitheader_coo = -1;
static int hf_enip_cpf_cdi_32bitheader_run_idle = -1;
static int hf_enip_cpf_cai_connid = -1;
static int hf_enip_cpf_sai_connid = -1;
static int hf_enip_cpf_sai_seqnum = -1;
@ -340,7 +336,6 @@ static gint ett_count_tree = -1;
static gint ett_type_tree = -1;
static gint ett_command_tree = -1;
static gint ett_sockadd = -1;
static gint ett_32bitheader_tree = -1;
static gint ett_lsrcf = -1;
static gint ett_tcpip_status = -1;
static gint ett_tcpip_config_cap = -1;
@ -506,14 +501,6 @@ static const value_string unconn_msg_type_vals[] = {
{ 0, NULL }
};
/* Translate function to string - Run/Idle */
static const value_string enip_run_idle_vals[] = {
{ 0, "Idle" },
{ 1, "Run" },
{ 0, NULL }
};
static const value_string enip_tcpip_status_interface_config_vals[] = {
{ 0, "Not configured" },
{ 1, "BOOTP/DHCP/NVS" },
@ -1088,6 +1075,7 @@ enip_open_cip_connection( packet_info *pinfo, cip_conn_info_t* connInfo)
conn_val->safety = connInfo->safety;
conn_val->motion = connInfo->motion;
conn_val->ClassID = connInfo->ClassID;
conn_val->ConnPoint = connInfo->ConnPoint;
conn_val->open_frame = connInfo->forward_open_frame;
conn_val->open_reply_frame = pinfo->num;
conn_val->close_frame = 0;
@ -2190,8 +2178,8 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
packet_info *pinfo, proto_tree *tree, proto_tree *dissector_tree,
proto_item *enip_item, int offset, guint32 ifacehndl)
{
proto_item *temp_item, *count_item, *type_item, *io_item;
proto_tree *temp_tree, *count_tree, *item_tree, *sockaddr_tree, *io_tree;
proto_item *temp_item, *count_item, *type_item;
proto_tree *temp_tree, *count_tree, *item_tree, *sockaddr_tree;
int item_count, item_length, item, io_length;
unsigned char name_length;
tvbuff_t *next_tvb;
@ -2316,8 +2304,8 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
if ((request_info != NULL) && (request_info->cip_info != NULL) &&
(request_info->cip_info->connInfo != NULL) &&
(request_key != NULL) &&
(((request_info->cip_info->bService & 0x7F) == SC_CM_FWD_OPEN) ||
((request_info->cip_info->bService & 0x7F) == SC_CM_LARGE_FWD_OPEN))&&
(((request_info->cip_info->bService & CIP_SC_MASK) == SC_CM_FWD_OPEN) ||
((request_info->cip_info->bService & CIP_SC_MASK) == SC_CM_LARGE_FWD_OPEN))&&
(request_info->cip_info->dissector == dissector_get_uint_handle( subdissector_class_table, CI_CLS_CM)))
{
if (request_key->requesttype == ENIP_REQUEST_PACKET)
@ -2403,7 +2391,7 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
}
else if (conn_info->motion == TRUE)
{
call_dissector(cipmotion_handle, next_tvb, pinfo, dissector_tree);
call_dissector_with_data(cipmotion_handle, next_tvb, pinfo, dissector_tree, GUINT_TO_POINTER(conn_info->ConnPoint));
}
else
{
@ -2420,15 +2408,7 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
(((connid_type == ECIDT_O2T) && enip_OTrun_idle) ||
((connid_type == ECIDT_T2O) && enip_TOrun_idle)))
{
io_item = proto_tree_add_item( item_tree, hf_enip_cpf_cdi_32bitheader,
tvb, offset+6+(item_length-io_length), 4, ENC_LITTLE_ENDIAN );
io_tree = proto_item_add_subtree( io_item, ett_32bitheader_tree );
proto_tree_add_item(io_tree, hf_enip_cpf_cdi_32bitheader_roo,
tvb, offset+6+(item_length-io_length), 4, ENC_LITTLE_ENDIAN );
proto_tree_add_item(io_tree, hf_enip_cpf_cdi_32bitheader_coo,
tvb, offset+6+(item_length-io_length), 4, ENC_LITTLE_ENDIAN );
proto_tree_add_item(io_tree, hf_enip_cpf_cdi_32bitheader_run_idle,
tvb, offset+6+(item_length-io_length), 4, ENC_LITTLE_ENDIAN );
dissect_cip_run_idle(tvb, offset + 6 + (item_length - io_length), item_tree);
io_length -= 4;
}
@ -3327,26 +3307,6 @@ proto_register_enip(void)
FT_UINT16, BASE_DEC, NULL, 0,
"Common Packet Format: Connected Data Item, Sequence Count", HFILL }},
{ &hf_enip_cpf_cdi_32bitheader,
{ "32-bit Header", "enip.cpf.cdi.32bitheader",
FT_UINT32, BASE_HEX, NULL, 0,
"Common Packet Format: Connected Data Item, 32-bit Header", HFILL }},
{ &hf_enip_cpf_cdi_32bitheader_roo,
{ "ROO", "enip.cpf.cdi.roo",
FT_UINT32, BASE_HEX, NULL, 0xC,
"Common Packet Format: Connected Data Item, Ready for Ownership of Outputs", HFILL }},
{ &hf_enip_cpf_cdi_32bitheader_coo,
{ "COO", "enip.cpf.cdi.coo",
FT_UINT32, BASE_HEX, NULL, 0x2,
"Common Packet Format: Connected Data Item, Claim Output Ownership", HFILL }},
{ &hf_enip_cpf_cdi_32bitheader_run_idle,
{ "Run/Idle", "enip.cpf.cdi.run_idle",
FT_UINT32, BASE_HEX, VALS(enip_run_idle_vals), 0x1,
"Common Packet Format: Connected Data Item, Run/Idle", HFILL }},
/* Connection Address Item */
{ &hf_enip_cpf_cai_connid,
{ "Connection ID", "enip.cpf.cai.connid",
@ -4192,7 +4152,6 @@ proto_register_enip(void)
&ett_type_tree,
&ett_command_tree,
&ett_sockadd,
&ett_32bitheader_tree,
&ett_lsrcf,
&ett_tcpip_status,
&ett_tcpip_config_cap,

View File

@ -117,6 +117,7 @@ typedef struct enip_conn_val {
cip_safety_epath_info_t safety;
gboolean motion;
guint32 ClassID;
guint32 ConnPoint;
} enip_conn_val_t;
enum enip_connid_type {ECIDT_UNKNOWN, ECIDT_O2T, ECIDT_T2O};