CIP Safety: Improve Analysis

This commit is contained in:
Dylan Ulis 2023-11-22 07:27:59 +00:00 committed by AndersBroman
parent 0d93782443
commit 2e41f52062
4 changed files with 150 additions and 1 deletions

View File

@ -682,6 +682,8 @@ 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;
//// Concurrent Connections
static int hf_cip_cm_cc_version;
@ -3553,6 +3555,13 @@ static void add_cip_class_to_info_column(packet_info *pinfo, guint32 class_id, i
return;
}
// Don't show the Assembly class. It's a generic common class, and there are often multiple entries
// which clutter the display.
if (display_type == DISPLAY_CONNECTION_PATH && class_id == 4)
{
return;
}
if (display_type == DISPLAY_CONNECTION_PATH)
{
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", val_to_str(class_id, cip_class_names_vals, "Class (0x%02x)"));
@ -7352,6 +7361,15 @@ static void fwd_open_analysis_safety_open(packet_info* pinfo, proto_item* cmd_it
{
expert_add_info(pinfo, cmd_item, &ei_cip_safety_open_type2b);
}
if (safety_fwdopen->originator_type == CIP_SAFETY_ORIGINATOR_PRODUCER)
{
expert_add_info(pinfo, cmd_item, &ei_cip_safety_output);
}
else if (safety_fwdopen->originator_type == CIP_SAFETY_ORIGINATOR_CONSUMER)
{
expert_add_info(pinfo, cmd_item, &ei_cip_safety_input);
}
}
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);
@ -7379,6 +7397,8 @@ static void display_connection_information_fwd_open_req(packet_info* pinfo, tvbu
if (conn_info->safety.safety_seg)
{
add_safety_data_type_to_info_column(pinfo, ECIDT_O2T, &conn_info->safety);
pi = proto_tree_add_float(conn_info_tree, hf_cip_safety_nte_ms, tvb, 0, 0, conn_info->safety.nte_value_ms);
proto_item_set_generated(pi);
}
@ -7405,6 +7425,11 @@ static void display_connection_information_fwd_open_rsp(packet_info* pinfo, tvbu
mark_cip_connection(pinfo, tvb, conn_info_tree);
display_previous_route_connection_path(preq_info, conn_info_tree, tvb, pinfo, hf_cip_cm_conn_path_size, DISPLAY_CONNECTION_PATH);
if (preq_info && preq_info->connInfo && preq_info->connInfo->safety.safety_seg)
{
add_safety_data_type_to_info_column(pinfo, ECIDT_T2O, &preq_info->connInfo->safety);
}
}
static void display_connection_information_fwd_close_req(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree)
@ -7452,6 +7477,7 @@ static void display_connection_information_fwd_close_req(packet_info* pinfo, tvb
{
// Make it obvious that the FwdClose is Safety, to match how the FwdOpen looks.
col_append_str(pinfo->cinfo, COL_INFO, " [Safety]");
add_safety_data_type_to_info_column(pinfo, ECIDT_O2T, &conn_info->safety);
}
}
@ -7477,6 +7503,7 @@ static void display_connection_information_fwd_close_rsp(packet_info* pinfo, tvb
{
// Make it obvious that the FwdClose is Safety, to match how the FwdOpen looks.
col_append_str(pinfo->cinfo, COL_INFO, " [Safety]");
add_safety_data_type_to_info_column(pinfo, ECIDT_T2O, &conn_val->safety);
}
}
@ -7759,6 +7786,19 @@ dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvb
preq_info->connInfo->TransportClass_trigger = TransportClass_trigger;
preq_info->connInfo->timeout_multiplier = timeout_multiplier;
preq_info->connInfo->safety = safety_fwdopen;
if (preq_info->connInfo->safety.safety_seg)
{
gboolean server_dir = (TransportClass_trigger & CI_PRODUCTION_DIR_MASK) ? TRUE : FALSE;
if (server_dir)
{
preq_info->connInfo->safety.originator_type = CIP_SAFETY_ORIGINATOR_PRODUCER;
}
else
{
preq_info->connInfo->safety.originator_type = CIP_SAFETY_ORIGINATOR_CONSUMER;
}
}
preq_info->connInfo->connection_path = connection_path;
preq_info->connInfo->FwdOpenPathLenBytes = conn_path_size;
@ -10129,6 +10169,8 @@ proto_register_cip(void)
{ &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_no_fwd_close, { "cip.analysis.no_fwd_close", PI_PROTOCOL, PI_NOTE, "No Forward Close seen for this CIP Connection", EXPFILL } },
};

View File

@ -512,6 +512,7 @@ typedef struct cip_connID_info {
enum cip_safety_format_type {CIP_SAFETY_BASE_FORMAT, CIP_SAFETY_EXTENDED_FORMAT};
enum cip_safety_open_type {CIP_SAFETY_OPEN_UNKNOWN, CIP_SAFETY_OPEN_TYPE1, CIP_SAFETY_OPEN_TYPE2A, CIP_SAFETY_OPEN_TYPE2B};
enum cip_safety_originator_type {CIP_SAFETY_ORIGINATOR_UNKNOWN, CIP_SAFETY_ORIGINATOR_CONSUMER, CIP_SAFETY_ORIGINATOR_PRODUCER};
typedef struct cip_connection_triad {
guint16 ConnSerialNumber;
@ -525,6 +526,8 @@ typedef struct cip_safety_epath_info {
enum cip_safety_format_type format;
enum cip_safety_open_type safety_open_type;
enum cip_safety_originator_type originator_type;
// These 3x variables are only used during a first pass calculation.
guint16 running_rollover_value; /* Keep track of the rollover value over the course of the connection */
guint16 running_timestamp_value; /* Keep track of the timestamp value over the course of the connection */

View File

@ -89,6 +89,7 @@ static int hf_cipsafety_crc_s5_1;
static int hf_cipsafety_crc_s5_2;
static int hf_cipsafety_crc_s5_status;
static int hf_cipsafety_complement_data;
static int hf_cip_safety_message_encoding;
/* CIP Safety header field identifiers */
static int hf_cip_reqrsp;
@ -457,6 +458,32 @@ const range_string safety_max_consumer_numbers[] = {
{ 0, 0, NULL }
};
enum message_encoding_type {
MSG_ENCODING_BASE_1_2_BYTE_DATA,
MSG_ENCODING_EXTENDED_1_2_BYTE_DATA,
MSG_ENCODING_BASE_3_250_BYTE_DATA,
MSG_ENCODING_EXTENDED_3_250_BYTE_DATA,
MSG_ENCODING_BASE_TIME_STAMP,
MSG_ENCODING_BASE_TIME_COORDINATION,
MSG_ENCODING_EXTENDED_TIME_COORDINATION,
MSG_ENCODING_BASE_TIME_CORRECTION,
MSG_ENCODING_EXTENDED_TIME_CORRECTION,
};
static const value_string safety_message_encoding_vals[] = {
{ MSG_ENCODING_BASE_1_2_BYTE_DATA, "Base Format, 1 or 2 Byte Data Section" },
{ MSG_ENCODING_EXTENDED_1_2_BYTE_DATA, "Extended Format, 1 or 2 Byte Data Section" },
{ MSG_ENCODING_BASE_3_250_BYTE_DATA, "Base Format, 3 to 250 Byte Data Section" },
{ MSG_ENCODING_EXTENDED_3_250_BYTE_DATA, "Extended Format, 3 to 250 Byte Data Section" },
{ MSG_ENCODING_BASE_TIME_STAMP, "Base Format, Time Stamp Section" },
{ MSG_ENCODING_BASE_TIME_COORDINATION, "Base Format, Time Coordination Section" },
{ MSG_ENCODING_EXTENDED_TIME_COORDINATION, "Extended Format, Time Coordination Section" },
{ MSG_ENCODING_BASE_TIME_CORRECTION, "Base Format, Time Correction Section" },
{ MSG_ENCODING_EXTENDED_TIME_CORRECTION, "Extended Format, Time Correction Section" },
{ 0, NULL }
};
void cip_safety_128us_fmt(gchar *s, guint32 value)
{
// Each tick is 128us.
@ -1527,6 +1554,9 @@ dissect_mcast_byte( proto_tree *tree, tvbuff_t *tvb, int offset)
// Base Format Time Correction Message
static void dissect_base_format_time_correction_message(proto_tree* tree, tvbuff_t* tvb, int offset)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_BASE_TIME_CORRECTION);
proto_item_set_generated(it);
dissect_mcast_byte(tree, tvb, offset);
proto_tree_add_item(tree, hf_cipsafety_time_correction, tvb, offset + 1, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_cipsafety_mcast_byte2, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
@ -1536,6 +1566,9 @@ static void dissect_base_format_time_correction_message(proto_tree* tree, tvbuff
// Extended Format Time Correction Message
static void dissect_extended_format_time_correction_message(proto_tree* tree, tvbuff_t* tvb, int offset)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_EXTENDED_TIME_CORRECTION);
proto_item_set_generated(it);
dissect_mcast_byte(tree, tvb, offset);
proto_tree_add_item(tree, hf_cipsafety_time_correction, tvb, offset + 1, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_cipsafety_crc_s5_0, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
@ -1549,6 +1582,9 @@ static void dissect_extended_format_time_correction_message(proto_tree* tree, tv
static void dissect_base_format_time_stamp_section(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb, int offset,
gboolean compute_crc, guint8 mode_byte, const cip_connection_triad_t* connection_triad)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_BASE_TIME_STAMP);
proto_item_set_generated(it);
proto_tree_add_item(tree, hf_cipsafety_timestamp, tvb, offset, 2, ENC_LITTLE_ENDIAN);
guint16 timestamp = tvb_get_letohs(tvb, offset);
@ -1574,6 +1610,9 @@ static void dissect_base_format_time_stamp_section(packet_info* pinfo, proto_tre
static void dissect_base_format_time_coordination_message(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb,
gboolean compute_crc, const cip_connection_triad_t* connection_triad)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_BASE_TIME_COORDINATION);
proto_item_set_generated(it);
dissect_ack_byte(tree, tvb, 0);
guint8 ack_byte = tvb_get_guint8(tvb, 0);
@ -1602,6 +1641,9 @@ static void dissect_base_format_time_coordination_message(packet_info* pinfo, pr
static void dissect_extended_format_time_coordination_message(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb,
gboolean compute_crc, const cip_connection_triad_t* connection_triad)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_EXTENDED_TIME_COORDINATION);
proto_item_set_generated(it);
dissect_ack_byte(tree, tvb, 0);
guint8 ack_byte = tvb_get_guint8(tvb, 0);
@ -1624,6 +1666,9 @@ static void dissect_extended_format_time_coordination_message(packet_info* pinfo
static void dissect_base_format_1_or_2_byte_data(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb, int io_data_size,
gboolean compute_crc, const cip_connection_triad_t* connection_triad)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_BASE_1_2_BYTE_DATA);
proto_item_set_generated(it);
proto_tree_add_item(tree, hf_cipsafety_data, tvb, 0, io_data_size, ENC_NA);
dissect_mode_byte(tree, tvb, io_data_size, pinfo);
guint8 mode_byte = tvb_get_guint8(tvb, io_data_size);
@ -1663,6 +1708,9 @@ static void dissect_base_format_1_or_2_byte_data(packet_info* pinfo, proto_tree*
static void dissect_base_format_3_to_250_byte_data(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb, int io_data_size,
gboolean compute_crc, const cip_connection_triad_t* connection_triad)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_BASE_3_250_BYTE_DATA);
proto_item_set_generated(it);
proto_tree_add_item(tree, hf_cipsafety_data, tvb, 0, io_data_size, ENC_NA);
dissect_mode_byte(tree, tvb, io_data_size, pinfo);
guint mode_byte = tvb_get_guint8(tvb, io_data_size);
@ -1710,6 +1758,9 @@ static void dissect_base_format_3_to_250_byte_data(packet_info* pinfo, proto_tre
static void dissect_extended_format_1_or_2_byte_data(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb, int io_data_size,
gboolean compute_crc, const cip_connection_triad_t* connection_triad, const cip_safety_packet_data_t* packet_data)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_EXTENDED_1_2_BYTE_DATA);
proto_item_set_generated(it);
proto_tree_add_item(tree, hf_cipsafety_data, tvb, 0, io_data_size, ENC_NA);
dissect_mode_byte(tree, tvb, io_data_size, pinfo);
guint mode_byte = tvb_get_guint8(tvb, io_data_size);
@ -1741,6 +1792,9 @@ static void dissect_extended_format_1_or_2_byte_data(packet_info* pinfo, proto_t
static void dissect_extended_format_3_to_250_byte_data(packet_info* pinfo, proto_tree* tree, tvbuff_t* tvb, int io_data_size,
gboolean compute_crc, const cip_connection_triad_t* connection_triad, const cip_safety_packet_data_t* packet_data)
{
proto_item* it = proto_tree_add_uint(tree, hf_cip_safety_message_encoding, tvb, 0, 0, MSG_ENCODING_EXTENDED_3_250_BYTE_DATA);
proto_item_set_generated(it);
proto_tree_add_item(tree, hf_cipsafety_data, tvb, 0, io_data_size, ENC_NA);
dissect_mode_byte(tree, tvb, io_data_size, pinfo);
guint mode_byte = tvb_get_guint8(tvb, io_data_size);
@ -1832,6 +1886,45 @@ static cip_safety_packet_data_t* get_timestamp_packet_data(packet_info* pinfo, c
return packet_data;
}
enum cip_safety_data_type {CIP_SAFETY_DATA_TYPE_UNKNOWN, CIP_SAFETY_PRODUCE, CIP_SAFETY_CONSUME};
static enum cip_safety_data_type get_cip_safety_data_type(enum enip_connid_type conn_type, const cip_safety_epath_info_t* safety)
{
if (conn_type == ECIDT_O2T && safety->originator_type == CIP_SAFETY_ORIGINATOR_PRODUCER)
{
return CIP_SAFETY_PRODUCE;
}
else if (conn_type == ECIDT_O2T && safety->originator_type == CIP_SAFETY_ORIGINATOR_CONSUMER)
{
return CIP_SAFETY_CONSUME;
}
else if (conn_type == ECIDT_T2O && safety->originator_type == CIP_SAFETY_ORIGINATOR_PRODUCER)
{
return CIP_SAFETY_CONSUME;
}
else if (conn_type == ECIDT_T2O && safety->originator_type == CIP_SAFETY_ORIGINATOR_CONSUMER)
{
return CIP_SAFETY_PRODUCE;
}
else
{
return CIP_SAFETY_DATA_TYPE_UNKNOWN;
}
}
void add_safety_data_type_to_info_column(packet_info *pinfo, enum enip_connid_type conn_type, const cip_safety_epath_info_t* safety)
{
enum cip_safety_data_type data_type = get_cip_safety_data_type(conn_type, safety);
if (data_type == CIP_SAFETY_CONSUME)
{
col_append_str(pinfo->cinfo, COL_INFO, " [C->P]");
}
else // CIP_SAFETY_PRODUCE
{
col_append_str(pinfo->cinfo, COL_INFO, " [P->C]");
}
}
static void
dissect_cip_safety_data( proto_tree *tree, proto_item *item, tvbuff_t *tvb, int item_length, packet_info *pinfo, cip_safety_info_t* safety_info)
{
@ -1881,6 +1974,8 @@ dissect_cip_safety_data( proto_tree *tree, proto_item *item, tvbuff_t *tvb, int
}
/* consumer data */
proto_item_append_text(item, " [Consume]");
col_append_str(pinfo->cinfo, COL_INFO, " [C->P]");
switch (format)
{
@ -1911,6 +2006,9 @@ dissect_cip_safety_data( proto_tree *tree, proto_item *item, tvbuff_t *tvb, int
short_format = FALSE;
/* producer data */
proto_item_append_text(item, " [Produce]");
col_append_str(pinfo->cinfo, COL_INFO, " [P->C]");
switch (format)
{
case CIP_SAFETY_BASE_FORMAT:
@ -2266,7 +2364,7 @@ proto_register_cipsafety(void)
},
{ &hf_cipsafety_timestamp,
{ "Timestamp", "cipsafety.timestamp",
FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }
FT_UINT16, BASE_CUSTOM, CF_FUNC(cip_safety_128us_fmt), 0, NULL, HFILL }
},
{ &hf_cipsafety_ack_byte,
{ "ACK Byte", "cipsafety.ack_byte",
@ -2353,6 +2451,11 @@ proto_register_cipsafety(void)
FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }
},
{ &hf_cip_safety_message_encoding,
{ "Safety Message Encoding", "cipsafety.message_encoding",
FT_UINT32, BASE_DEC, VALS(safety_message_encoding_vals), 0, NULL, HFILL }
},
{ &hf_cip_sercosiii_link_snn,
{ "Data", "cipsafety.sercosiii_link.snn",
FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }

View File

@ -50,6 +50,7 @@ extern void dissect_unid(tvbuff_t *tvb, packet_info *pinfo, int offset, proto_it
int hf_snn_date, int hf_snn_time, int hf_macid, gint ett, gint ett_snn);
extern void dissect_cipsafety_snn(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int offset, int hf_real_datetime, int hf_date, int hf_time);
extern void cip_safety_128us_fmt(gchar *s, guint32 value);
extern void add_safety_data_type_to_info_column(packet_info *pinfo, enum enip_connid_type conn_type, const cip_safety_epath_info_t* safety);
/*
** Exported variables