From 2e41f52062e6b7e8b68acbccf95aa36064bf563c Mon Sep 17 00:00:00 2001 From: Dylan Ulis Date: Wed, 22 Nov 2023 07:27:59 +0000 Subject: [PATCH] CIP Safety: Improve Analysis --- epan/dissectors/packet-cip.c | 42 ++++++++++++ epan/dissectors/packet-cip.h | 3 + epan/dissectors/packet-cipsafety.c | 105 ++++++++++++++++++++++++++++- epan/dissectors/packet-cipsafety.h | 1 + 4 files changed, 150 insertions(+), 1 deletion(-) diff --git a/epan/dissectors/packet-cip.c b/epan/dissectors/packet-cip.c index 1cb4f394ad..90b87417ae 100644 --- a/epan/dissectors/packet-cip.c +++ b/epan/dissectors/packet-cip.c @@ -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 } }, }; diff --git a/epan/dissectors/packet-cip.h b/epan/dissectors/packet-cip.h index d47f71c039..49b24f8039 100644 --- a/epan/dissectors/packet-cip.h +++ b/epan/dissectors/packet-cip.h @@ -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 */ diff --git a/epan/dissectors/packet-cipsafety.c b/epan/dissectors/packet-cipsafety.c index 595f0536a1..f58f1f2d31 100644 --- a/epan/dissectors/packet-cipsafety.c +++ b/epan/dissectors/packet-cipsafety.c @@ -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 } diff --git a/epan/dissectors/packet-cipsafety.h b/epan/dissectors/packet-cipsafety.h index 82adf6f55c..6849087b0c 100644 --- a/epan/dissectors/packet-cipsafety.h +++ b/epan/dissectors/packet-cipsafety.h @@ -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