aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorD. Ulis <daulis0@gmail.com>2015-12-01 14:25:52 -0500
committerMichael Mann <mmann78@netscape.net>2015-12-05 11:51:50 +0000
commit411104bd89e235e8c2642ba41c71f639f49b848f (patch)
treea25c637dd6ae447919a5150c5e8adbe9a4b1282c
parentc88f24f4f855496996cdb9fb641e13f80c5f7b3b (diff)
Enhancements for EtherNet/IP and CIP
EtherNet/IP 1. EtherNet Link object parsed Physical Address attribute response incorrectly. 2. Display Unknown Commands as ENIP instead of just TCP data. CIP 1. For connected data, don't interpret it as a Message Router Request/Response format when the Forward Open connection was not directed to the Message Router. Previously, this data would be incorrectly shown as explicit CIP data. In many cases, this would show as malformed. This traffic will now just display as Data in the Wireshark tree, and "Implicit Data - Class (0x123)" in the Info column. Make this data filterable by "cip.conn_path_class == 0x123". 2. Fix parsing of Unconnected Send responses. Previously, for most cases, the response was not fully parsed, and would just show "Data", or it would parse the response as if the request class was the Connection Manager, which is incorrect. Now, also show the request path of the original embedded message in the tree. 3. Add some detailed error data for malformed Forward Close response. Change-Id: I1c98ce516373d8c0ed6e049e25342f726bc370ea Reviewed-on: https://code.wireshark.org/review/12339 Petri-Dish: Michael Mann <mmann78@netscape.net> Reviewed-by: D. Ulis <daulis0@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Michael Mann <mmann78@netscape.net>
-rw-r--r--epan/dissectors/packet-cip.c154
-rw-r--r--epan/dissectors/packet-cip.h1
-rw-r--r--epan/dissectors/packet-enip.c69
3 files changed, 162 insertions, 62 deletions
diff --git a/epan/dissectors/packet-cip.c b/epan/dissectors/packet-cip.c
index f72a42b4b0..1bc41ce658 100644
--- a/epan/dissectors/packet-cip.c
+++ b/epan/dissectors/packet-cip.c
@@ -448,6 +448,7 @@ static int hf_time_sync_port_proto_addr_info_port_proto_addr = -1;
static int hf_time_sync_steps_removed = -1;
static int hf_time_sync_sys_time_and_offset_time = -1;
static int hf_time_sync_sys_time_and_offset_offset = -1;
+static int hf_conn_path_class = -1;
/* Initialize the subtree pointers */
static gint ett_cip = -1;
@@ -569,6 +570,7 @@ static expert_field ei_mal_rpi_no_data = EI_INIT;
static expert_field ei_mal_inv_config_size = EI_INIT;
static expert_field ei_mal_ot_size = EI_INIT;
static expert_field ei_mal_to_size = EI_INIT;
+static expert_field ei_mal_fwd_close_missing_data = EI_INIT;
dissector_table_t subdissector_class_table;
@@ -4954,6 +4956,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->motion = (connection_path.iClass == 0x42) ? TRUE : FALSE;
preq_info->connInfo->safety = safety_fwdopen;
+ preq_info->connInfo->ClassID = connection_path.iClass;
}
}
}
@@ -5056,6 +5059,35 @@ dissect_cip_cm_fwd_open_rsp_success(cip_req_info_t *preq_info, proto_tree *tree,
}
}
+static void display_previous_request_path(cip_req_info_t *preq_info, proto_tree *item_tree, packet_info *pinfo)
+{
+ if (preq_info && preq_info->IOILen && preq_info->pIOI)
+ {
+ proto_item *pi;
+ proto_tree *epath_tree;
+ tvbuff_t* tvbIOI;
+
+ tvbIOI = tvb_new_real_data((const guint8 *)preq_info->pIOI, preq_info->IOILen * 2, preq_info->IOILen * 2);
+ if (tvbIOI)
+ {
+ pi = proto_tree_add_uint_format_value(item_tree, hf_cip_request_path_size, NULL, 0, 0, preq_info->IOILen, "%d (words)", preq_info->IOILen);
+ PROTO_ITEM_SET_GENERATED(pi);
+
+ /* Add the epath */
+ epath_tree = proto_tree_add_subtree(item_tree, NULL, 0, 0, ett_path, &pi, "Request Path: ");
+ PROTO_ITEM_SET_GENERATED(pi);
+
+ if (preq_info->ciaData == NULL)
+ {
+ preq_info->ciaData = wmem_new(wmem_file_scope(), cip_simple_request_info_t);
+ }
+
+ dissect_epath(tvbIOI, pinfo, epath_tree, pi, 0, preq_info->IOILen * 2, TRUE, FALSE, preq_info->ciaData, NULL);
+ tvb_free(tvbIOI);
+ }
+ }
+}
+
static void
dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
@@ -5086,12 +5118,14 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
add_status = tvb_get_letohs( tvb, offset + 4 );
else
add_status = 0;
- if( gen_status == 0 /* success response ) */
+
+ if( gen_status == CI_GRC_SUCCESS
|| ( ( service & CIP_SC_MASK ) != SC_CM_UNCON_SEND )
|| !( ( gen_status == CI_GRC_FAILURE && (add_status == CM_ES_UNCONNECTED_REQUEST_TIMED_OUT ||
add_status == CM_ES_PORT_NOT_AVAILABLE ||
add_status == CM_ES_LINK_ADDRESS_NOT_VALID ||
- add_status == CM_ES_INVALID_SEGMENT_IN_CONN_PATH) )
+ add_status == CM_ES_INVALID_SEGMENT_IN_CONN_PATH ||
+ add_status == CM_ES_LINK_ADDRESS_TO_SELF_INVALID))
|| gen_status == CI_GRC_NO_RESOURCE
|| gen_status == CI_GRC_BAD_PATH
)
@@ -5103,6 +5137,8 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
{
tvbuff_t *next_tvb;
void *p_save_proto_data;
+ gint service_index;
+ heur_dtbl_entry_t *hdtbl_entry;
p_save_proto_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0 );
p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
@@ -5110,13 +5146,33 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
proto_tree_add_uint_format( item_tree, hf_cip_cm_sc, NULL, 0, 0, SC_CM_UNCON_SEND|CIP_SC_RESPONSE_MASK, "(Service: Unconnected Send (Response))" );
next_tvb = tvb_new_subset_length(tvb, offset, item_length);
+
+ display_previous_request_path(pembedded_req_info, item_tree, pinfo);
+
+ /* Check to see if service is 'generic' */
+ try_val_to_str_idx((service & CIP_SC_MASK), cip_sc_vals, &service_index);
+
if ( pembedded_req_info && pembedded_req_info->dissector )
+ {
call_dissector(pembedded_req_info->dissector, next_tvb, pinfo, item_tree );
+ }
+ else if (service_index >= 0)
+ {
+ /* See if object dissector wants to override generic service handling */
+ if (!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
+ {
+ dissect_cip_generic_service_rsp(tvb, pinfo, item_tree);
+ }
+ }
else
+ {
call_dissector( cip_class_generic_handle, next_tvb, pinfo, item_tree );
+ }
p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
p_add_proto_data(wmem_file_scope(), pinfo, proto_cip, 0, p_save_proto_data);
+
+ /* Return early because the response was only the embedded message response. */
return;
}
}
@@ -5229,8 +5285,9 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
/* If there is any command specific data create a sub-tree for it */
if( ( item_length-4-add_stat_size ) != 0 )
{
+ proto_item *cmd_item;
cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size,
- ett_cm_cmd_data, NULL, "Command Specific Data" );
+ ett_cm_cmd_data, &cmd_item, "Command Specific Data" );
if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
{
@@ -5264,7 +5321,14 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
/* Display the Reserved byte */
proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+4+add_stat_size+9, 1, ENC_LITTLE_ENDIAN);
if (app_rep_size > 0)
- proto_tree_add_item(cmd_data_tree, hf_cip_cm_app_reply_data, tvb, offset+4+add_stat_size+10, app_rep_size, ENC_NA);
+ {
+ if (tvb_reported_length_remaining(tvb, offset + 4 + add_stat_size + 10) <= app_rep_size)
+ {
+ expert_add_info(pinfo, cmd_item, &ei_mal_fwd_close_missing_data);
+ break;
+ }
+ proto_tree_add_item(cmd_data_tree, hf_cip_cm_app_reply_data, tvb, offset+4+add_stat_size+10,app_rep_size, ENC_NA);
+ }
enip_close_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber );
@@ -6173,7 +6237,8 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info
proto_item_set_len( status_item, 2 + add_stat_size*2);
-
+ /* The previous packet service must be Unconnected Send, or match the current
+ service to be a valid match. If they don't, ignore the previous data.*/
if( preq_info
&& !( preq_info->bService == ( service & CIP_SC_MASK )
|| ( preq_info->bService == SC_CM_UNCON_SEND && preq_info->dissector == cip_class_cm_handle )
@@ -6181,42 +6246,25 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info
)
preq_info = NULL;
- if ( preq_info )
- {
- if ( preq_info->IOILen && preq_info->pIOI )
- {
- tvbuff_t* tvbIOI;
-
- tvbIOI = tvb_new_real_data((const guint8 *)preq_info->pIOI, preq_info->IOILen * 2, preq_info->IOILen * 2);
- if ( tvbIOI )
- {
- pi = proto_tree_add_uint_format_value( cip_tree, hf_cip_request_path_size, NULL, 0, 0, preq_info->IOILen, "%d (words)", preq_info->IOILen );
- PROTO_ITEM_SET_GENERATED(pi);
-
- /* Add the epath */
- epath_tree = proto_tree_add_subtree(cip_tree, NULL, 0, 0, ett_path, &pi, "Request Path: ");
- PROTO_ITEM_SET_GENERATED(pi);
-
- preq_info->ciaData = wmem_new(wmem_file_scope(), cip_simple_request_info_t);
- dissect_epath( tvbIOI, pinfo, epath_tree, pi, 0, preq_info->IOILen*2, TRUE, FALSE, preq_info->ciaData, NULL);
- tvb_free(tvbIOI);
- }
- }
- }
+ display_previous_request_path(preq_info, cip_tree, pinfo);
/* Check to see if service is 'generic' */
try_val_to_str_idx((service & CIP_SC_MASK), cip_sc_vals, &service_index);
- if (service_index >= 0)
+
+ /* If the request set a dissector, then check that first. This ensures
+ that Unconnected Send responses are properly parsed based on the
+ embedded request. */
+ if (preq_info && preq_info->dissector)
{
- /* See if object dissector wants to override generic service handling */
- if(!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
- {
- dissect_cip_generic_service_rsp(tvb, pinfo, cip_tree);
- }
+ call_dissector(preq_info->dissector, tvb, pinfo, item_tree);
}
- else if ( preq_info && preq_info->dissector )
+ else if (service_index >= 0)
{
- call_dissector( preq_info->dissector, tvb, pinfo, item_tree );
+ /* See if object dissector wants to override generic service handling */
+ if(!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
+ {
+ dissect_cip_generic_service_rsp(tvb, pinfo, cip_tree);
+ }
}
else
{
@@ -6311,7 +6359,6 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info
} /* End of dissect_cip_data() */
-
static int
dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
@@ -6344,6 +6391,38 @@ dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
return tvb_reported_length(tvb);
}
+static int
+dissect_cip_implicit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
+{
+ proto_item *ti;
+ proto_item *conn_path_class_item;
+ proto_tree *cip_tree;
+ proto_tree *cmd_data_tree;
+ guint32 ClassID = GPOINTER_TO_UINT(data);
+ int length = tvb_reported_length_remaining(tvb, 0);
+
+ /* Make entries in Protocol column and Info column on summary display */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ /* Create display subtree for the protocol */
+ ti = proto_tree_add_item(tree, proto_cip, tvb, 0, length, ENC_NA);
+ cip_tree = proto_item_add_subtree(ti, ett_cip);
+
+ /* Display Class ID from the Forward Open Connection Path. */
+ conn_path_class_item = proto_tree_add_uint(cip_tree, hf_conn_path_class, NULL, 0, 0, ClassID);
+ PROTO_ITEM_SET_GENERATED(conn_path_class_item);
+
+ cmd_data_tree = proto_tree_add_subtree(cip_tree, tvb, 0, length,
+ ett_cmd_data, NULL, "Command Specific Data");
+ proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, 0, length, ENC_NA);
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "Implicit Data - %s",
+ val_to_str(ClassID, cip_class_names_vals, "Class (0x%02x)"));
+
+ return tvb_reported_length(tvb);
+}
+
/*
* Protocol initialization
*/
@@ -6597,6 +6676,7 @@ proto_register_cip(void)
{ &hf_time_sync_steps_removed, { "Steps Removed", "cip.time_sync.steps_removed", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
{ &hf_time_sync_sys_time_and_offset_time, { "System Time (Microseconds)", "cip.time_sync.sys_time_and_offset.time", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
{ &hf_time_sync_sys_time_and_offset_offset, { "System Offset (Microseconds)", "cip.time_sync.sys_time_and_offset.offset", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
+ { &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 }},
};
static hf_register_info hf_cm[] = {
@@ -6872,6 +6952,7 @@ proto_register_cip(void)
{ &ei_mal_inv_config_size, { "cip.malformed.inv_config_size", PI_MALFORMED, PI_WARN, "Invalid configuration size - missing size field", EXPFILL }},
{ &ei_mal_ot_size, { "cip.malformed.ot_size", PI_MALFORMED, PI_WARN, "Invalid O->T size - missing size field", EXPFILL }},
{ &ei_mal_to_size, { "cip.malformed.to_size", PI_MALFORMED, PI_WARN, "Invalid T->O size - missing size field", EXPFILL }},
+ { &ei_mal_fwd_close_missing_data, { "cip.malformed.fwd_close_missing_data", PI_MALFORMED, PI_ERROR, "Forward Close response missing application reply data", EXPFILL }},
};
expert_module_t* expert_cip;
@@ -6879,6 +6960,7 @@ proto_register_cip(void)
/* Register the protocol name and description */
proto_cip = proto_register_protocol("Common Industrial Protocol",
"CIP", "cip");
+ new_register_dissector("cip_implicit", dissect_cip_implicit, proto_cip);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_cip, hf, array_length(hf));
diff --git a/epan/dissectors/packet-cip.h b/epan/dissectors/packet-cip.h
index 893ee7d1d0..76c4556570 100644
--- a/epan/dissectors/packet-cip.h
+++ b/epan/dissectors/packet-cip.h
@@ -294,6 +294,7 @@ typedef struct cip_conn_info {
guint8 TransportClass_trigger;
cip_safety_epath_info_t safety;
gboolean motion;
+ guint32 ClassID;
} cip_conn_info_t;
typedef struct cip_req_info {
diff --git a/epan/dissectors/packet-enip.c b/epan/dissectors/packet-enip.c
index b25c8fad55..1c52ff95eb 100644
--- a/epan/dissectors/packet-enip.c
+++ b/epan/dissectors/packet-enip.c
@@ -304,6 +304,7 @@ static expert_field ei_mal_tcpip_interface_config = EI_INIT;
static expert_field ei_mal_tcpip_mcast_config = EI_INIT;
static expert_field ei_mal_tcpip_last_conflict = EI_INIT;
static expert_field ei_mal_elink_interface_flags = EI_INIT;
+static expert_field ei_mal_elink_physical_address = EI_INIT;
static expert_field ei_mal_elink_interface_counters = EI_INIT;
static expert_field ei_mal_elink_media_counters = EI_INIT;
static expert_field ei_mal_elink_interface_control = EI_INIT;
@@ -324,6 +325,7 @@ static dissector_handle_t data_handle;
static dissector_handle_t arp_handle;
static dissector_handle_t cipsafety_handle;
static dissector_handle_t cipmotion_handle;
+static dissector_handle_t cip_implicit_handle;
static gboolean enip_desegment = TRUE;
static gboolean enip_OTrun_idle = TRUE;
@@ -826,6 +828,7 @@ typedef struct enip_conn_val {
guint32 connid;
cip_safety_epath_info_t safety;
gboolean motion;
+ guint32 ClassID;
} enip_conn_val_t;
typedef struct _enip_conv_info_t {
@@ -993,6 +996,7 @@ enip_open_cip_connection( packet_info *pinfo, cip_conn_info_t* connInfo)
conn_val->TransportClass_trigger = connInfo->TransportClass_trigger;
conn_val->safety = connInfo->safety;
conn_val->motion = connInfo->motion;
+ conn_val->ClassID = connInfo->ClassID;
conn_val->open_frame = connInfo->forward_open_frame;
conn_val->open_reply_frame = pinfo->fd->num;
conn_val->close_frame = 0;
@@ -1465,6 +1469,22 @@ dissect_elink_interface_flags(packet_info *pinfo, proto_tree *tree, proto_item *
}
static int
+dissect_elink_physical_address(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
+ int offset, int total_len)
+
+{
+ if (total_len < 6)
+ {
+ expert_add_info(pinfo, item, &ei_mal_elink_physical_address);
+ return total_len;
+ }
+
+ proto_tree_add_item(tree, hf_elink_physical_address, tvb, offset, 6, ENC_NA);
+ return 6;
+}
+
+
+static int
dissect_elink_interface_counters(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
@@ -1709,7 +1729,7 @@ attribute_info_t enip_attribute_vals[45] = {
/* Ethernet Link object */
{0xF6, FALSE, 1, "Interface Speed", cip_dword, &hf_elink_interface_speed, NULL},
{0xF6, FALSE, 2, "Interface Flags", cip_dissector_func, NULL, dissect_elink_interface_flags},
- {0xF6, FALSE, 3, "Physical Address", cip_byte_array, &hf_elink_physical_address, NULL},
+ {0xF6, FALSE, 3, "Physical Address", cip_dissector_func, NULL, dissect_elink_physical_address },
{0xF6, FALSE, 4, "Interface Counters", cip_dissector_func, NULL, dissect_elink_interface_counters},
{0xF6, FALSE, 5, "Media Counters", cip_dissector_func, NULL, dissect_elink_media_counters},
{0xF6, FALSE, 6, "Interface Control", cip_dissector_func, NULL, dissect_elink_interface_control},
@@ -1890,14 +1910,21 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
/* Call dissector for interface */
next_tvb = tvb_new_subset_length (tvb, offset+8, item_length-2);
- p_add_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_REQUEST_INFO, request_info);
- if ( tvb_reported_length_remaining(next_tvb, 0) <= 0 || !dissector_try_uint(subdissector_sud_table, ifacehndl, next_tvb, pinfo, dissector_tree) )
+
+ if ((conn_info == NULL) || (conn_info->ClassID == CI_CLS_MR))
{
- /* Show the undissected payload */
- if ( tvb_reported_length_remaining(tvb, offset) > 0 )
- call_dissector( data_handle, next_tvb, pinfo, dissector_tree );
+ p_add_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_REQUEST_INFO, request_info);
+ if (!dissector_try_uint(subdissector_sud_table, ifacehndl, next_tvb, pinfo, dissector_tree) )
+ {
+ /* Show the undissected payload */
+ call_dissector( data_handle, next_tvb, pinfo, dissector_tree );
+ }
+ p_remove_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_REQUEST_INFO);
+ }
+ else
+ {
+ call_dissector_with_data( cip_implicit_handle, next_tvb, pinfo, dissector_tree, GUINT_TO_POINTER(conn_info->ClassID) );
}
- p_remove_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_REQUEST_INFO);
}
else
{
@@ -2353,34 +2380,20 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data
static int
dissect_enip_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
- guint16 encap_cmd;
-
- /* An ENIP packet is at least 4 bytes long - we need the command type. */
- if (!tvb_bytes_exist(tvb, 0, 4))
+ /* An ENIP packet is at least 4 bytes long. */
+ if (tvb_captured_length(tvb) < 4)
return 0;
- /* Get the command type and see if it's valid. */
- encap_cmd = tvb_get_letohs( tvb, 0 );
- if (try_val_to_str(encap_cmd, encap_cmd_vals) == NULL)
- return 0; /* not a known command */
-
return dissect_enip_pdu(tvb, pinfo, tree, data);
}
static int
dissect_enip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
- guint16 encap_cmd;
-
- /* An ENIP packet is at least 4 bytes long - we need the command type. */
- if (!tvb_bytes_exist(tvb, 0, 4))
+ /* An ENIP packet is at least 4 bytes long. */
+ if (tvb_captured_length(tvb) < 4)
return 0;
- /* Get the command type and see if it's valid. */
- encap_cmd = tvb_get_letohs( tvb, 0 );
- if (try_val_to_str(encap_cmd, encap_cmd_vals) == NULL)
- return 0; /* not a known command */
-
tcp_dissect_pdus(tvb, pinfo, tree, enip_desegment, 4, get_enip_pdu_len, dissect_enip_pdu, data);
return tvb_captured_length(tvb);
}
@@ -2802,7 +2815,7 @@ proto_register_enip(void)
{ &hf_enip_connection_transport_data,
{ "Data", "enip.connection_transport_data",
- FT_BYTES, BASE_NONE, NULL, 0x0,
+ FT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0x0,
"Connection Transport Data", HFILL }},
{ &hf_tcpip_status,
@@ -3452,6 +3465,7 @@ proto_register_enip(void)
{ &ei_mal_tcpip_mcast_config, { "cip.malformed.tcpip.mcast_config", PI_MALFORMED, PI_ERROR, "Malformed TCP/IP Multicast Config", EXPFILL }},
{ &ei_mal_tcpip_last_conflict, { "cip.malformed.tcpip.last_conflict", PI_MALFORMED, PI_ERROR, "Malformed TCP/IP Last Conflict Detected", EXPFILL }},
{ &ei_mal_elink_interface_flags, { "cip.malformed.elink.interface_flags", PI_MALFORMED, PI_ERROR, "Malformed Ethernet Link Interface Flags", EXPFILL }},
+ { &ei_mal_elink_physical_address, { "cip.malformed.elink.physical_address", PI_MALFORMED, PI_ERROR, "Malformed Ethernet Link Physical Address", EXPFILL } },
{ &ei_mal_elink_interface_counters, { "cip.malformed.elink.interface_counters", PI_MALFORMED, PI_ERROR, "Malformed Ethernet Link Interface Counters", EXPFILL }},
{ &ei_mal_elink_media_counters, { "cip.malformed.elink.media_counters", PI_MALFORMED, PI_ERROR, "Malformed Ethernet Link Media Counters", EXPFILL }},
{ &ei_mal_elink_interface_control, { "cip.malformed.elink.interface_control", PI_MALFORMED, PI_ERROR, "Malformed Ethernet Link Interface Control", EXPFILL }},
@@ -3775,6 +3789,9 @@ proto_reg_handoff_enip(void)
cipsafety_handle = find_dissector("cipsafety");
cipmotion_handle = find_dissector("cipmotion");
+ /* Implicit data dissector */
+ cip_implicit_handle = find_dissector("cip_implicit");
+
/* Register for EtherNet/IP Device Level Ring protocol */
dlr_handle = new_create_dissector_handle(dissect_dlr, proto_dlr);
dissector_add_uint("ethertype", ETHERTYPE_DLR, dlr_handle);