CIP: Improve Connection Analysis
This commit is contained in:
parent
01796d6f2e
commit
dc148e0ea5
|
@ -678,12 +678,14 @@ static expert_field ei_mal_opt_service_list;
|
|||
static expert_field ei_mal_padded_epath_size;
|
||||
static expert_field ei_mal_missing_string_data;
|
||||
|
||||
static expert_field ei_cip_null_fwd_open;
|
||||
static expert_field ei_cip_safety_open_type1;
|
||||
static expert_field ei_cip_safety_open_type2a;
|
||||
static expert_field ei_cip_safety_open_type2b;
|
||||
static expert_field ei_cip_no_fwd_close;
|
||||
static expert_field ei_cip_safety_input;
|
||||
static expert_field ei_cip_safety_output;
|
||||
static expert_field ei_cip_listen_input_connection;
|
||||
|
||||
//// Concurrent Connections
|
||||
static int hf_cip_cm_cc_version;
|
||||
|
@ -4840,8 +4842,8 @@ dissect_net_param16(tvbuff_t *tvb, int offset, proto_tree *tree,
|
|||
proto_tree_add_item(net_param_tree, hf_owner, tvb, offset, 2, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_type, tvb, offset, 2, ENC_LITTLE_ENDIAN, &conn_info->type);
|
||||
proto_tree_add_item(net_param_tree, hf_priority, tvb, offset, 2, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item(net_param_tree, hf_fixed_var, tvb, offset, 2, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item(net_param_tree, hf_con_size, tvb, offset, 2, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_fixed_var, tvb, offset, 2, ENC_LITTLE_ENDIAN, &conn_info->connection_size_type);
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_con_size, tvb, offset, 2, ENC_LITTLE_ENDIAN, &conn_info->connection_size);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -4859,8 +4861,8 @@ dissect_net_param32(tvbuff_t *tvb, int offset, proto_tree *tree,
|
|||
proto_tree_add_item(net_param_tree, hf_owner, tvb, offset, 4, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_type, tvb, offset, 4, ENC_LITTLE_ENDIAN, &conn_info->type);
|
||||
proto_tree_add_item(net_param_tree, hf_priority, tvb, offset, 4, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item(net_param_tree, hf_fixed_var, tvb, offset, 4, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item(net_param_tree, hf_con_size, tvb, offset, 4, ENC_LITTLE_ENDIAN );
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_fixed_var, tvb, offset, 4, ENC_LITTLE_ENDIAN, &conn_info->connection_size_type);
|
||||
proto_tree_add_item_ret_uint(net_param_tree, hf_con_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &conn_info->connection_size);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -7372,6 +7374,22 @@ static void fwd_open_analysis_safety_open(packet_info* pinfo, proto_item* cmd_it
|
|||
}
|
||||
}
|
||||
|
||||
void fwd_open_analysis_listen_input_connection(packet_info* pinfo, proto_item* cmd_item, guint8 TransportClass_trigger, const cip_connID_info_t* O2T_info)
|
||||
{
|
||||
// Listen Only and Input Only connections must be 'Fixed'.
|
||||
if (O2T_info->connection_size_type != CIP_CONNECTION_SIZE_TYPE_FIXED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
guint8 transport_class = TransportClass_trigger & CI_TRANSPORT_CLASS_MASK;
|
||||
if ((transport_class == 0 && O2T_info->connection_size == 0)
|
||||
|| (transport_class == 1 && O2T_info->connection_size == 2))
|
||||
{
|
||||
expert_add_info(pinfo, cmd_item, &ei_cip_listen_input_connection);
|
||||
}
|
||||
}
|
||||
|
||||
static void display_previous_route_connection_path(cip_req_info_t* preq_info, proto_tree* item_tree, tvbuff_t* tvb, packet_info* pinfo, int hf_path, int display_type);
|
||||
|
||||
// Display all Connection Information and Analysis.
|
||||
|
@ -7414,6 +7432,7 @@ static void display_connection_information_fwd_open_req(packet_info* pinfo, tvbu
|
|||
}
|
||||
|
||||
fwd_open_analysis_safety_open(pinfo, conn_info_item, &conn_info->safety);
|
||||
fwd_open_analysis_listen_input_connection(pinfo, conn_info_item, conn_info->TransportClass_trigger, &conn_info->O2T);
|
||||
}
|
||||
|
||||
static void display_connection_information_fwd_open_rsp(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree, cip_req_info_t* preq_info)
|
||||
|
@ -7676,7 +7695,7 @@ int dissect_concurrent_connection_network_segment(packet_info* pinfo, tvbuff_t*
|
|||
}
|
||||
|
||||
static void
|
||||
dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvbuff_t *tvb, int offset,
|
||||
dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, proto_item* cmd_item, tvbuff_t *tvb, int offset,
|
||||
gboolean large_fwd_open, packet_info *pinfo, gboolean concurrent_connection)
|
||||
{
|
||||
proto_item *pi;
|
||||
|
@ -7764,6 +7783,13 @@ dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvb
|
|||
dissect_epath( tvb, pinfo, epath_tree, pi, offset+26+net_param_offset+6, conn_path_size, FALSE, FALSE, &connection_path, &safety_fwdopen, DISPLAY_CONNECTION_PATH, NULL, FALSE);
|
||||
save_route_connection_path(pinfo, tvb, offset + 26 + net_param_offset + 6, conn_path_size);
|
||||
|
||||
// Null Forward Opens are a special case, so make it obvious.
|
||||
if ((O2T_info.type == CONN_TYPE_NULL) && (T2O_info.type == CONN_TYPE_NULL))
|
||||
{
|
||||
col_append_str(pinfo->cinfo, COL_INFO, " [Null]");
|
||||
expert_add_info(pinfo, cmd_item, &ei_cip_null_fwd_open);
|
||||
}
|
||||
|
||||
if (pinfo->fd->visited)
|
||||
{
|
||||
/* "Connection" is created during ForwardOpen reply (which will be after ForwardOpen request),
|
||||
|
@ -7784,6 +7810,7 @@ dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvb
|
|||
preq_info->connInfo->T2O = T2O_info;
|
||||
|
||||
preq_info->connInfo->TransportClass_trigger = TransportClass_trigger;
|
||||
preq_info->connInfo->IsNullFwdOpen = (O2T_info.type == CONN_TYPE_NULL) && (T2O_info.type == CONN_TYPE_NULL);
|
||||
preq_info->connInfo->timeout_multiplier = timeout_multiplier;
|
||||
preq_info->connInfo->safety = safety_fwdopen;
|
||||
if (preq_info->connInfo->safety.safety_seg)
|
||||
|
@ -7829,6 +7856,12 @@ static void display_previous_route_connection_path(cip_req_info_t *preq_info, pr
|
|||
cip_simple_request_info_t route_conn_path;
|
||||
dissect_epath(tvbIOI, pinfo, epath_tree, pi, 0, preq_info->RouteConnectionPathLen * 2, TRUE, FALSE, &route_conn_path, NULL, display_type, NULL, FALSE);
|
||||
tvb_free(tvbIOI);
|
||||
|
||||
if (preq_info->connInfo && preq_info->connInfo->IsNullFwdOpen)
|
||||
{
|
||||
col_append_str(pinfo->cinfo, COL_INFO, " [Null]");
|
||||
expert_add_info(pinfo, item_tree, &ei_cip_null_fwd_open);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8412,23 +8445,23 @@ dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_
|
|||
/* If there is any command specific data creat a sub-tree for it */
|
||||
if( (item_length-req_path_size-2) != 0 )
|
||||
{
|
||||
|
||||
proto_item* cmd_data_item;
|
||||
cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2,
|
||||
ett_cm_cmd_data, NULL, "Command Specific Data" );
|
||||
ett_cm_cmd_data, &cmd_data_item, "Command Specific Data" );
|
||||
|
||||
/* Check what service code that received */
|
||||
switch (service)
|
||||
{
|
||||
case SC_CM_FWD_OPEN:
|
||||
/* Forward open Request*/
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, FALSE, pinfo, FALSE);
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, cmd_data_item, tvb, offset+2+req_path_size, FALSE, pinfo, FALSE);
|
||||
break;
|
||||
case SC_CM_CONCURRENT_FWD_OPEN:
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, FALSE, pinfo, TRUE);
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, cmd_data_item, tvb, offset+2+req_path_size, FALSE, pinfo, TRUE);
|
||||
break;
|
||||
case SC_CM_LARGE_FWD_OPEN:
|
||||
/* Large Forward open Request*/
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, TRUE, pinfo, FALSE);
|
||||
dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, cmd_data_item, tvb, offset+2+req_path_size, TRUE, pinfo, FALSE);
|
||||
break;
|
||||
case SC_CM_FWD_CLOSE:
|
||||
case SC_CM_CONCURRENT_FWD_CLOSE:
|
||||
|
@ -10187,11 +10220,13 @@ proto_register_cip(void)
|
|||
{ &ei_mal_padded_epath_size, { "cip.malformed.epath.size", PI_MALFORMED, PI_ERROR, "Malformed EPATH vs Size", EXPFILL } },
|
||||
{ &ei_mal_missing_string_data, { "cip.malformed.missing_str_data", PI_MALFORMED, PI_ERROR, "Missing string data", EXPFILL } },
|
||||
|
||||
{ &ei_cip_null_fwd_open, { "cip.analysis.null_fwd_open", PI_PROTOCOL, PI_NOTE, "Null Forward Open", EXPFILL } },
|
||||
{ &ei_cip_safety_open_type1, { "cip.analysis.safety_open_type1", PI_PROTOCOL, PI_NOTE, "Type 1 - Safety Open with Data", EXPFILL } },
|
||||
{ &ei_cip_safety_open_type2a, { "cip.analysis.safety_open_type2a", PI_PROTOCOL, PI_NOTE, "Type 2a - Safety Open with SCID check", EXPFILL } },
|
||||
{ &ei_cip_safety_open_type2b, { "cip.analysis.safety_open_type2b", PI_PROTOCOL, PI_NOTE, "Type 2b - Safety Open without SCID check", EXPFILL } },
|
||||
{ &ei_cip_safety_input, { "cip.analysis.safety_input", PI_PROTOCOL, PI_NOTE, "Safety Input Connection", EXPFILL } },
|
||||
{ &ei_cip_safety_output, { "cip.analysis.safety_output", PI_PROTOCOL, PI_NOTE, "Safety Output Connection", EXPFILL } },
|
||||
{ &ei_cip_listen_input_connection, { "cip.analysis.listen_input_connection", PI_PROTOCOL, PI_NOTE, "[Likely] Listen Only or Input Only Connection", EXPFILL } },
|
||||
{ &ei_cip_no_fwd_close, { "cip.analysis.no_fwd_close", PI_PROTOCOL, PI_NOTE, "No Forward Close seen for this CIP Connection", EXPFILL } },
|
||||
};
|
||||
|
||||
|
|
|
@ -492,6 +492,7 @@ typedef struct cip_service_info {
|
|||
} cip_service_info_t;
|
||||
|
||||
// This describes a one-way connection. Each CIP Connection includes 2 of these.
|
||||
#define CIP_CONNECTION_SIZE_TYPE_FIXED (0)
|
||||
typedef struct cip_connID_info {
|
||||
// Connection ID from Forward Open Request. This may get updated in the Forward Open Response.
|
||||
guint32 connID;
|
||||
|
@ -502,6 +503,8 @@ typedef struct cip_connID_info {
|
|||
|
||||
// Network Connection Parameters
|
||||
guint32 type; // See: cip_con_type_vals
|
||||
guint32 connection_size;
|
||||
guint32 connection_size_type; // 0 = Fixed, 1 = Variable
|
||||
|
||||
// Requested Packet Interval in microseconds.
|
||||
guint32 rpi;
|
||||
|
@ -566,6 +569,9 @@ typedef struct cip_conn_info {
|
|||
guint32 connid;
|
||||
|
||||
gboolean is_concurrent_connection;
|
||||
|
||||
// True if this is a Null Forward Open. In this case, a new connection is not created.
|
||||
gboolean IsNullFwdOpen;
|
||||
} cip_conn_info_t;
|
||||
|
||||
typedef struct cip_req_info {
|
||||
|
|
|
@ -432,6 +432,10 @@ static expert_field ei_mal_eip_cert_capability_flags;
|
|||
static expert_field ei_mal_cpf_item_length_mismatch;
|
||||
static expert_field ei_mal_cpf_item_minimum_size;
|
||||
|
||||
static expert_field ei_cip_request_no_response;
|
||||
static expert_field ei_cip_io_heartbeat;
|
||||
|
||||
|
||||
static dissector_table_t subdissector_srrd_table;
|
||||
static dissector_table_t subdissector_io_table;
|
||||
static dissector_table_t subdissector_decode_as_io_table;
|
||||
|
@ -942,6 +946,10 @@ enip_match_request( packet_info *pinfo, proto_tree *tree, enip_request_key_t *pr
|
|||
NULL, 0, 0, request_info->rep_num);
|
||||
proto_item_set_generated(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
expert_add_info(pinfo, tree, &ei_cip_request_no_response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1198,7 +1206,7 @@ static void enip_open_cip_connection( packet_info *pinfo, cip_conn_info_t* connI
|
|||
return;
|
||||
|
||||
// Don't create connections for Null Forward Opens.
|
||||
if (connInfo->T2O.type == CONN_TYPE_NULL && connInfo->O2T.type == CONN_TYPE_NULL)
|
||||
if (connInfo->IsNullFwdOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -2553,7 +2561,39 @@ void display_fwd_open_connection_path(cip_conn_info_t* conn_info, proto_tree* tr
|
|||
}
|
||||
}
|
||||
|
||||
static void display_connection_information(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree, cip_conn_info_t* conn_info, enum enip_connid_type connid_type)
|
||||
// returns TRUE if this is a likely Heartbeat message
|
||||
// Note: item_length include the CIP Sequence Count, if applicable.
|
||||
static gboolean cip_io_is_likely_heartbeat(const cip_conn_info_t* conn_info, enum enip_connid_type connid_type, guint32 item_length)
|
||||
{
|
||||
// Heartbeat messages only occur in the O->T direction.
|
||||
if (connid_type != ECIDT_O2T)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Class 0 heartbeat messages have 0 length.
|
||||
if (item_length == 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// The only other possibility for a heartbeat is for Class 1 (the 2 bytes is the Sequence Count)
|
||||
if (item_length != 2)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// The only possibility for a heartbeat is: Class 1 with 2 bytes of data only, and it must be a "Fixed" size.
|
||||
guint8 transport_class = conn_info->TransportClass_trigger & CI_TRANSPORT_CLASS_MASK;
|
||||
if (transport_class == 1 && conn_info->O2T.connection_size_type == CIP_CONNECTION_SIZE_TYPE_FIXED)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void display_connection_information(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree, cip_conn_info_t* conn_info,
|
||||
enum enip_connid_type connid_type, guint32 item_length)
|
||||
{
|
||||
proto_item* conn_info_item = NULL;
|
||||
proto_tree* conn_info_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_connection_info, &conn_info_item, "Connection Information");
|
||||
|
@ -2581,6 +2621,11 @@ static void display_connection_information(packet_info* pinfo, tvbuff_t* tvb, pr
|
|||
|
||||
pi = proto_tree_add_uint(conn_info_tree, hf_enip_fwd_open_in, tvb, 0, 0, conn_info->open_req_frame);
|
||||
proto_item_set_generated(pi);
|
||||
|
||||
if (cip_io_is_likely_heartbeat(conn_info, connid_type, item_length))
|
||||
{
|
||||
expert_add_info(pinfo, conn_info_item, &ei_cip_io_heartbeat);
|
||||
}
|
||||
}
|
||||
|
||||
// This dissects Class 0 or Class 1 I/O.
|
||||
|
@ -3023,7 +3068,7 @@ dissect_cpf(enip_request_key_t *request_key, int command, tvbuff_t *tvb,
|
|||
|
||||
if (conn_info)
|
||||
{
|
||||
display_connection_information(pinfo, tvb, enip_layer_tree, conn_info, connid_type);
|
||||
display_connection_information(pinfo, tvb, enip_layer_tree, conn_info, connid_type, item_length);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -4826,6 +4871,10 @@ proto_register_enip(void)
|
|||
{ &ei_mal_eip_cert_capability_flags, { "cip.malformed.eip_cert.capability_flags", PI_MALFORMED, PI_ERROR, "Malformed EIP Certificate Management Capability Flags", EXPFILL }},
|
||||
{ &ei_mal_cpf_item_length_mismatch, { "enip.malformed.cpf_item_length_mismatch", PI_MALFORMED, PI_ERROR, "CPF Item Length Mismatch", EXPFILL } },
|
||||
{ &ei_mal_cpf_item_minimum_size, { "enip.malformed.cpf_item_minimum_size", PI_MALFORMED, PI_ERROR, "CPF Item Minimum Size is 4", EXPFILL } },
|
||||
|
||||
// Analysis Checks
|
||||
{ &ei_cip_request_no_response, { "cip.analysis.request_no_response", PI_PROTOCOL, PI_NOTE, "CIP request without a response", EXPFILL } },
|
||||
{ &ei_cip_io_heartbeat, { "cip.analysis.cip_io_heartbeat", PI_PROTOCOL, PI_NOTE, "[Likely] CIP I/O Heartbeat [Listen/Input Only Connection]", EXPFILL } },
|
||||
};
|
||||
|
||||
/* Setup list of header fields for DLR See Section 1.6.1 for details*/
|
||||
|
|
Loading…
Reference in New Issue