From 41e08a5b6236611d6c2aab14992c3e231e1bf2c6 Mon Sep 17 00:00:00 2001 From: Thomas Wiens Date: Fri, 12 Jul 2019 21:07:20 +0200 Subject: s7comm: Implement packet reassembly Implemented packet reassembly of userdata telegrams. Modified existing functions which before had shown only raw data on fragmented telegrams. Change-Id: Ia8d02928c08ad5228da28ac6a4b4df7ed99ab47e Reviewed-on: https://code.wireshark.org/review/33920 Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman --- epan/dissectors/packet-s7comm.c | 660 ++++++++++++++++++++++---------- epan/dissectors/packet-s7comm_szl_ids.c | 349 +++++++---------- epan/dissectors/packet-s7comm_szl_ids.h | 2 +- 3 files changed, 596 insertions(+), 415 deletions(-) diff --git a/epan/dissectors/packet-s7comm.c b/epan/dissectors/packet-s7comm.c index 082e08b637..18a329824b 100644 --- a/epan/dissectors/packet-s7comm.c +++ b/epan/dissectors/packet-s7comm.c @@ -13,6 +13,7 @@ #include "config.h" #include +#include #include #include #include @@ -1533,6 +1534,10 @@ static const int *s7comm_data_blockcontrol_status_fields[] = { static gint ett_s7comm_plcfilename = -1; static gint hf_s7comm_data_ncprg_unackcount = -1; +static gint hf_s7comm_data_ncprg_filelength = -1; +static gint hf_s7comm_data_ncprg_filetime = -1; +static gint hf_s7comm_data_ncprg_filepath = -1; +static gint hf_s7comm_data_ncprg_filedata = -1; /* Variable status */ static gint hf_s7comm_varstat_data_type = -1; /* Type of data, 1 byte, stringlist userdata_prog_vartab_type_names */ @@ -1554,6 +1559,7 @@ static gint hf_s7comm_cycl_jobid = -1; /* PBC, Programmable Block Functions */ static gint hf_s7comm_pbc_unknown = -1; /* unknown, 1 byte */ static gint hf_s7comm_pbc_r_id = -1; /* Request ID R_ID, 4 bytes as hex */ +static gint hf_s7comm_pbc_len = -1; /* Alarm messages */ static gint hf_s7comm_cpu_alarm_message_item = -1; @@ -2283,6 +2289,45 @@ static const value_string modetrans_param_subfunc_names[] = { { 0, NULL } }; +/* These fields used when reassembling S7COMM fragments */ +static gint hf_s7comm_fragments = -1; +static gint hf_s7comm_fragment = -1; +static gint hf_s7comm_fragment_overlap = -1; +static gint hf_s7comm_fragment_overlap_conflict = -1; +static gint hf_s7comm_fragment_multiple_tails = -1; +static gint hf_s7comm_fragment_too_long_fragment = -1; +static gint hf_s7comm_fragment_error = -1; +static gint hf_s7comm_fragment_count = -1; +static gint hf_s7comm_reassembled_in = -1; +static gint hf_s7comm_reassembled_length = -1; +static gint ett_s7comm_fragment = -1; +static gint ett_s7comm_fragments = -1; + +static const fragment_items s7comm_frag_items = { + /* Fragment subtrees */ + &ett_s7comm_fragment, + &ett_s7comm_fragments, + /* Fragment fields */ + &hf_s7comm_fragments, + &hf_s7comm_fragment, + &hf_s7comm_fragment_overlap, + &hf_s7comm_fragment_overlap_conflict, + &hf_s7comm_fragment_multiple_tails, + &hf_s7comm_fragment_too_long_fragment, + &hf_s7comm_fragment_error, + &hf_s7comm_fragment_count, + /* Reassembled in field */ + &hf_s7comm_reassembled_in, + /* Reassembled length field */ + &hf_s7comm_reassembled_length, + /* Reassembled data field */ + NULL, + /* Tag */ + "S7COMM fragments" +}; + +static reassembly_table s7comm_reassembly_table; + /* These are the ids of the subtrees that we are creating */ static gint ett_s7comm = -1; /* S7 communication tree, parent of all other subtree */ static gint ett_s7comm_header = -1; /* Subtree for header block */ @@ -3582,7 +3627,7 @@ static guint32 s7comm_decode_ud_prog_reqdiagdata(tvbuff_t *tvb, proto_tree *data_tree, guint8 subfunc, /* Subfunction */ - guint32 offset) /* Offset on data part +4 */ + guint32 offset) { proto_item *item = NULL; proto_tree *item_tree = NULL; @@ -3850,16 +3895,49 @@ s7comm_decode_ud_prog_vartab_res_item(tvbuff_t *tvb, static guint32 s7comm_decode_ud_security_subfunc(tvbuff_t *tvb, proto_tree *data_tree, - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { /* Display dataset as raw bytes. Maybe this part can be extended with further knowledge. */ - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); offset += dlength; return offset; } +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 6 -> PBC, Programmable Block Functions (e.g. BSEND/BRECV), before reassembly + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_pbc_pre_reass(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *data_tree, + guint8 type, /* Type of data (request/response) */ + guint16 *dlength, + guint32 *r_id, /* R_ID of the PBC communication */ + guint32 offset) +{ + if ((type == S7COMM_UD_TYPE_REQ || type == S7COMM_UD_TYPE_RES) && (*dlength >= 8)) { + proto_tree_add_item(data_tree, hf_s7comm_item_varspec, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_item_varspec_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_item_syntax_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + /* 0x00 when passive partners is sending, 0xcc when active partner is sending? */ + proto_tree_add_item(data_tree, hf_s7comm_pbc_unknown, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_pbc_r_id, tvb, offset, 4, ENC_BIG_ENDIAN); + *r_id = tvb_get_ntohl(tvb, offset); + col_append_fstr(pinfo->cinfo, COL_INFO, " R_ID=0x%X", *r_id); + offset += 4; + *dlength -= 8; + } + return offset; +} + /******************************************************************************************************* * * PDU Type: User Data -> Function group 6 -> PBC, Programmable Block Functions (e.g. BSEND/BRECV) @@ -3868,32 +3946,41 @@ s7comm_decode_ud_security_subfunc(tvbuff_t *tvb, static guint32 s7comm_decode_ud_pbc_subfunc(tvbuff_t *tvb, proto_tree *data_tree, - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { - proto_tree_add_item(data_tree, hf_s7comm_item_varspec, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - proto_tree_add_item(data_tree, hf_s7comm_item_varspec_length, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - proto_tree_add_item(data_tree, hf_s7comm_item_syntax_id, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - proto_tree_add_item(data_tree, hf_s7comm_pbc_unknown, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - proto_tree_add_item(data_tree, hf_s7comm_pbc_r_id, tvb, offset, 4, ENC_BIG_ENDIAN); - offset += 4; - /* Only in the first telegram of possible several segments, an int16 of full data length is following. - * As the dissector can't check this, don't display the information - * and display the data as payload bytes. - */ - dlength = dlength - 4 - 8; /* 4 bytes data header, 8 bytes varspec */ - if (dlength > 0) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); - offset += dlength; - } + proto_tree_add_item(data_tree, hf_s7comm_pbc_len, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 2, ENC_NA); + offset += (dlength - 2); return offset; } +/******************************************************************************************************* + * + * PDU Type: User Data -> NC programming functions (file download/upload), before reassembly + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_ncprg_pre_reass(tvbuff_t *tvb, + proto_tree *data_tree, + guint8 type, /* Type of data (request/response) */ + guint8 subfunc, /* Subfunction */ + guint16 *dlength, + guint32 offset) +{ + if ((type == S7COMM_UD_TYPE_NCRES || type == S7COMM_UD_TYPE_NCPUSH) && + (subfunc == S7COMM_NCPRG_FUNCDOWNLOADBLOCK || + subfunc == S7COMM_NCPRG_FUNCUPLOAD || + subfunc == S7COMM_NCPRG_FUNCSTARTUPLOAD)) { + proto_tree_add_item(data_tree, hf_s7comm_data_blockcontrol_unknown1, tvb, offset, 2, ENC_NA); + offset += 2; + *dlength -= 2; + } + return offset; +} + /******************************************************************************************************* * * PDU Type: User Data -> NC programming functions (file download/upload) @@ -3905,12 +3992,15 @@ s7comm_decode_ud_ncprg_subfunc(tvbuff_t *tvb, proto_tree *data_tree, guint8 type, /* Type of data (request/response) */ guint8 subfunc, /* Subfunction */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { const guint8 *str_filename; + guint32 string_end_offset; + guint32 string_len; + guint32 filelength; + guint32 start_offset; - dlength -= 4; /* There are always 4 bytes header information in data part */ if (dlength >= 2) { if (type == S7COMM_UD_TYPE_NCREQ && subfunc == S7COMM_NCPRG_FUNCREQUESTDOWNLOAD) { proto_tree_add_item_ret_string(data_tree, hf_s7comm_data_blockcontrol_filename, tvb, offset, dlength, @@ -3936,13 +4026,33 @@ s7comm_decode_ud_ncprg_subfunc(tvbuff_t *tvb, } else if (type == S7COMM_UD_TYPE_NCPUSH && (subfunc == S7COMM_NCPRG_FUNCCONTUPLOAD || subfunc == S7COMM_NCPRG_FUNCCONTDOWNLOAD)) { proto_tree_add_item(data_tree, hf_s7comm_data_ncprg_unackcount, tvb, offset, 1, ENC_NA); offset += 1; - /* Guess: If 1, then this is the last telegram of up/download, otherwise 0 */ proto_tree_add_item(data_tree, hf_s7comm_data_blockcontrol_unknown1, tvb, offset, 1, ENC_NA); offset += 1; + } else if ((type == S7COMM_UD_TYPE_NCRES || type == S7COMM_UD_TYPE_NCPUSH) && + (subfunc == S7COMM_NCPRG_FUNCDOWNLOADBLOCK || + subfunc == S7COMM_NCPRG_FUNCUPLOAD || + subfunc == S7COMM_NCPRG_FUNCSTARTUPLOAD)) { + start_offset = offset; + /* file length may be contain only spaces when downloading a directory */ + proto_tree_add_item(data_tree, hf_s7comm_data_ncprg_filelength, tvb, offset, 8, ENC_ASCII|ENC_NA); + offset += 8; + proto_tree_add_item(data_tree, hf_s7comm_data_ncprg_filetime, tvb, offset, 16, ENC_ASCII|ENC_NA); + offset += 16; + /* File path and file data aren't always there */ + if (dlength > 24) { + if (subfunc == S7COMM_NCPRG_FUNCDOWNLOADBLOCK || subfunc == S7COMM_NCPRG_FUNCSTARTUPLOAD || subfunc == S7COMM_NCPRG_FUNCUPLOAD) { + string_end_offset = tvb_find_guint8(tvb, offset, dlength-8-16, 0x0a); + if (string_end_offset > 0) { + string_len = string_end_offset - offset + 1; /* include 0x0a */ + proto_tree_add_item(data_tree, hf_s7comm_data_ncprg_filepath, tvb, offset, string_len, ENC_ASCII|ENC_NA); + offset += string_len; + filelength = dlength - (offset - start_offset); + proto_tree_add_item(data_tree, hf_s7comm_data_ncprg_filedata, tvb, offset, filelength, ENC_NA); + offset += filelength; + } + } + } } else { - /* There is always a 2 bytes header before the data. - * Guess: first byte is used as "data unit reference" - */ proto_tree_add_item(data_tree, hf_s7comm_data_blockcontrol_unknown1, tvb, offset, 2, ENC_NA); offset += 2; dlength -= 2; @@ -3965,8 +4075,8 @@ s7comm_decode_message_service(tvbuff_t *tvb, packet_info *pinfo, proto_tree *data_tree, guint8 type, /* Type of data (request/response) */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { guint8 events; guint8 almtype; @@ -4037,7 +4147,7 @@ s7comm_decode_ud_cpu_alarm_main(tvbuff_t *tvb, proto_tree *data_tree, guint8 type, /* Type of data (request/response) */ guint8 subfunc, /* Subfunction */ - guint32 offset) /* Offset on data part +4 */ + guint32 offset) { guint32 start_offset; guint32 asc_start_offset; @@ -4217,6 +4327,7 @@ s7comm_decode_ud_cpu_alarm_main(tvbuff_t *tvb, proto_item_set_len(msg_item_tree, offset - start_offset); return offset; } + /******************************************************************************************************* * * PDU Type: User Data -> Function group 4 -> alarm query response @@ -4224,9 +4335,8 @@ s7comm_decode_ud_cpu_alarm_main(tvbuff_t *tvb, *******************************************************************************************************/ static guint32 s7comm_decode_ud_cpu_alarm_query_response(tvbuff_t *tvb, - packet_info *pinfo, proto_tree *data_tree, - guint32 offset) /* Offset on data part +4 */ + guint32 offset) { proto_item *msg_item = NULL; proto_tree *msg_item_tree = NULL; @@ -4242,52 +4352,26 @@ s7comm_decode_ud_cpu_alarm_query_response(tvbuff_t *tvb, guint8 alarmtype; guint16 complete_length; gint32 remaining_length; - guint8 n_blocks; - guint8 func; + gboolean cont; start_offset = offset; msg_item = proto_tree_add_item(data_tree, hf_s7comm_cpu_alarm_message_item, tvb, offset, 0, ENC_NA); msg_item_tree = proto_item_add_subtree(msg_item, ett_s7comm_cpu_alarm_message); - /* An alarm response may take more that one response telegram. If it's split into many telegrams, - * then the inner telegrams begins with the dataset parts without any header. - * The last telegrams of this sequence has the first two bytes zero. - */ - func = tvb_get_guint8(tvb, offset); - n_blocks = tvb_get_guint8(tvb, offset + 1); - if (func == 0) { - proto_tree_add_item(msg_item_tree, hf_s7comm_cpu_alarm_message_function, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - proto_tree_add_item(msg_item_tree, hf_s7comm_cpu_alarm_message_nr_objects, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - if (n_blocks == 0) { - col_append_str(pinfo->cinfo, COL_INFO, " [Last]"); - proto_item_set_len(msg_item_tree, offset - start_offset); - return offset; - } - } - if (func > 0) { - col_append_str(pinfo->cinfo, COL_INFO, " [Continuation]"); - complete_length = func; - remaining_length = (gint32)complete_length; - returncode = S7COMM_ITEM_RETVAL_DATA_OK; - } else { - returncode = tvb_get_guint8(tvb, offset); - proto_tree_add_uint(msg_item_tree, hf_s7comm_data_returncode, tvb, offset, 1, returncode); - offset += 1; - proto_tree_add_item(msg_item_tree, hf_s7comm_data_transport_size, tvb, offset, 1, ENC_BIG_ENDIAN); - offset += 1; - /* As with ALARM_S it's only possible to send one alarm description in a single response telegram, - * they are split into many telegrams. Therefore the complete length field is set to 0xffff. - * To reuse the following dissect-loop, the remaining length is set to zero. - */ - complete_length = tvb_get_ntohs(tvb, offset); - proto_tree_add_uint(msg_item_tree, hf_s7comm_cpu_alarm_query_completelen, tvb, offset, 2, complete_length); - remaining_length = (gint32)complete_length; - if (remaining_length == 0xffff) { - remaining_length = 0; - } - offset += 2; - } + + /* Maybe this value here is something different, always 0x00 or 0x01 */ + proto_tree_add_item(msg_item_tree, hf_s7comm_cpu_alarm_message_function, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(msg_item_tree, hf_s7comm_cpu_alarm_message_nr_objects, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + returncode = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(msg_item_tree, hf_s7comm_data_returncode, tvb, offset, 1, returncode); + offset += 1; + proto_tree_add_item(msg_item_tree, hf_s7comm_data_transport_size, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + complete_length = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(msg_item_tree, hf_s7comm_cpu_alarm_query_completelen, tvb, offset, 2, complete_length); + remaining_length = (gint32)complete_length; + offset += 2; if (returncode == S7COMM_ITEM_RETVAL_DATA_OK) { do { @@ -4344,7 +4428,13 @@ s7comm_decode_ud_cpu_alarm_query_response(tvbuff_t *tvb, } remaining_length = remaining_length - (offset - msg_obj_start_offset); proto_item_set_len(msg_obj_item_tree, offset - msg_obj_start_offset); - } while (remaining_length > 0); + /* when complete_length is 0xffff, then loop until terminating null */ + if (complete_length == 0xffff) { + cont = (tvb_get_guint8(tvb, offset) > 0); + } else { + cont = (remaining_length > 0); + } + } while (cont); } proto_item_set_len(msg_item_tree, offset - start_offset); @@ -4437,8 +4527,8 @@ s7comm_decode_ud_time_subfunc(tvbuff_t *tvb, guint8 type, /* Type of data (request/response) */ guint8 subfunc, /* Subfunction */ guint8 ret_val, /* Return value in data part */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { gboolean know_data = FALSE; @@ -4467,8 +4557,8 @@ s7comm_decode_ud_time_subfunc(tvbuff_t *tvb, break; } - if (know_data == FALSE && dlength > 4) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + if (know_data == FALSE && dlength > 0) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); offset += dlength; } return offset; @@ -4487,12 +4577,11 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, guint8 subfunc, /* Subfunction */ guint8 ret_val, /* Return value in data part */ guint8 tsize, /* transport size in data part */ - guint16 len, /* length given in data part */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { - guint16 count; - guint16 i; + guint32 count; + guint32 i; const guint8 *pBlocknumber; guint16 blocknumber; guint8 blocktype; @@ -4509,12 +4598,12 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, * List blocks */ case S7COMM_UD_SUBF_BLOCK_LIST: - if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (type == S7COMM_UD_TYPE_REQ) { /* Is this a possible combination? Never seen it... */ - } else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ - count = len / 4; - for(i = 0; i < count; i++) { + } else if (type == S7COMM_UD_TYPE_RES) { + count = dlength / 4; + for (i = 0; i < count; i++) { /* Insert a new tree of 4 byte length for every item */ item = proto_tree_add_item(data_tree, hf_s7comm_data_item, tvb, offset, 4, ENC_NA); item_tree = proto_item_add_subtree(item, ett_s7comm_data_item); @@ -4533,7 +4622,7 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, * List blocks of type */ case S7COMM_UD_SUBF_BLOCK_LISTTYPE: - if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (type == S7COMM_UD_TYPE_REQ) { if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { blocktype16 = tvb_get_ntohs(tvb, offset); itemadd = proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 2, ENC_ASCII|ENC_NA); @@ -4546,11 +4635,11 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, } know_data = TRUE; - }else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ + } else if (type == S7COMM_UD_TYPE_RES) { if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { - count = len / 4; + count = dlength / 4; - for(i = 0; i < count; i++) { + for (i = 0; i < count; i++) { /* Insert a new tree of 4 byte length for every item */ item = proto_tree_add_item(data_tree, hf_s7comm_data_item, tvb, offset, 4, ENC_NA); item_tree = proto_item_add_subtree(item, ett_s7comm_data_item); @@ -4572,7 +4661,7 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, * Get block infos */ case S7COMM_UD_SUBF_BLOCK_BLOCKINFO: - if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (type == S7COMM_UD_TYPE_REQ) { if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { gint32 num = -1; gboolean num_valid; @@ -4602,7 +4691,7 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, } know_data = TRUE; - }else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ + } else if (type == S7COMM_UD_TYPE_RES) { /* 78 Bytes */ if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK) { itemadd = proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 2, ENC_ASCII|ENC_NA); @@ -4677,8 +4766,8 @@ s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, default: break; } - if (know_data == FALSE && dlength > 4) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + if (know_data == FALSE && dlength > 0) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); offset += dlength; } return offset; @@ -4696,8 +4785,8 @@ s7comm_decode_ud_cyclic_subfunc(tvbuff_t *tvb, proto_tree *data_tree, guint8 type, /* Type of data (request/response) */ guint8 subfunc, /* Subfunction */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { gboolean know_data = FALSE; guint32 offset_old; @@ -4753,8 +4842,8 @@ s7comm_decode_ud_cyclic_subfunc(tvbuff_t *tvb, break; } - if (know_data == FALSE && dlength > 4) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + if (know_data == FALSE && dlength > 0) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); offset += dlength; } return offset; @@ -4770,8 +4859,8 @@ s7comm_decode_ud_prog_subfunc(tvbuff_t *tvb, proto_tree *data_tree, guint8 type, /* Type of data (request/response) */ guint8 subfunc, /* Subfunction */ - guint16 dlength, /* length of data part given in header */ - guint32 offset) /* Offset on data part +4 */ + guint32 dlength, + guint32 offset) { gboolean know_data = FALSE; @@ -4844,12 +4933,198 @@ s7comm_decode_ud_prog_subfunc(tvbuff_t *tvb, } } - if (know_data == FALSE && dlength > 4) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + if (know_data == FALSE && dlength > 0) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength, ENC_NA); offset += dlength; } return offset; } +/******************************************************************************************************* + * + * PDU Type: User Data: Data part and reassembly + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_data(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + guint16 dlength, + guint8 type, + guint8 funcgroup, + guint8 subfunc, + guint8 seq_num, + guint8 data_unit_ref, + guint8 last_data_unit, + guint32 offset) +{ + proto_item *item = NULL; + proto_tree *data_tree = NULL; + guint8 tsize; + guint16 len; + guint8 ret_val; + guint32 length_rem = 0; + gboolean save_fragmented; + guint32 frag_id = 0; + gboolean more_frags = FALSE; + gboolean is_fragmented = FALSE; + tvbuff_t* new_tvb = NULL; + tvbuff_t* next_tvb = NULL; + fragment_head *fd_head; + gchar str_fragadd[32]; + + /* The first 4 bytes of the data part of a userdata telegram are the same for all types. + * This is also the minumum length of the data part. + */ + if (dlength >= 4) { + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + + ret_val = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_data_returncode, tvb, offset, 1, ret_val); + offset += 1; + /* Not definitely known part, kind of "transport size"? constant 0x09, 1 byte + * The position is the same as in a data response/write telegram, + */ + tsize = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_data_transport_size, tvb, offset, 1, tsize); + offset += 1; + len = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_data_length, tvb, offset, 2, len); + offset += 2; + + if (len >= 2) { + more_frags = (last_data_unit == S7COMM_UD_LASTDATAUNIT_NO); + /* Some packets have an additional header before the payload, which must be + * extracted from the data before reassembly. + */ + switch (funcgroup) { + case S7COMM_UD_FUNCGROUP_NCPRG: + offset = s7comm_decode_ud_ncprg_pre_reass(tvb, data_tree, type, subfunc, &len, offset); + /* Unfortunately on NC programming the first PDU is always shown as reassembled also when not fragmented, + * because data_unit_ref may overflow and start again at 0 on big file transfers. + */ + is_fragmented = TRUE; + frag_id = seq_num; + break; + case S7COMM_UD_FUNCGROUP_PBC: + /* The R_ID is used for fragment identification */ + offset = s7comm_decode_ud_pbc_pre_reass(tvb, pinfo, data_tree, type, &len, &frag_id, offset); + is_fragmented = data_unit_ref > 0 || seq_num > 0; + break; + default: + is_fragmented = (data_unit_ref > 0); + frag_id = data_unit_ref; + break; + } + /* Reassembly of fragmented data part */ + save_fragmented = pinfo->fragmented; + if (is_fragmented) { /* fragmented */ + pinfo->fragmented = TRUE; + /* NC programming uses a different method of fragment indication. The sequence number is used as reference-id, + * the data unit reference number is increased with every packet, as the sender does not need to wait for + * the acknowledge of the packet. Also different in NC programming is, that also when a packet is not + * fragmented, data_unit_ref is > 0 and "reassembled" would be displayed even when not fragmented (count number of fragments?) + * Using fragment number does not work here, as it's only one byte. And if there are more than 255 fragments this would fail. + */ + fd_head = fragment_add_seq_next(&s7comm_reassembly_table, + tvb, offset, pinfo, + frag_id, /* ID for fragments belonging together */ + NULL, /* void *data */ + len, /* fragment length - to the end */ + more_frags); /* More fragments? */ + g_snprintf(str_fragadd, sizeof(str_fragadd), " id=%d", frag_id); + new_tvb = process_reassembled_data(tvb, offset, pinfo, + "Reassembled S7COMM", fd_head, &s7comm_frag_items, + NULL, tree); + if (new_tvb) { /* take it all */ + /* add reassembly info only when there's more than one fragment */ + if (fd_head && fd_head->next) { + col_append_fstr(pinfo->cinfo, COL_INFO, " (S7COMM reassembled%s)", str_fragadd); + proto_item_append_text(data_tree, " (S7COMM reassembled%s)", str_fragadd); + } + next_tvb = new_tvb; + offset = 0; + } else { /* make a new subset */ + next_tvb = tvb_new_subset_length_caplen(tvb, offset, -1, -1); + col_append_fstr(pinfo->cinfo, COL_INFO, " (S7COMM fragment%s)", str_fragadd); + proto_item_append_text(data_tree, " (S7COMM fragment%s)", str_fragadd); + offset = 0; + } + } else { /* Not fragmented */ + next_tvb = tvb; + } + pinfo->fragmented = save_fragmented; + length_rem = tvb_reported_length_remaining(next_tvb, offset); + /* TODO: PBC telegrams say "Last data unit = no" and data_unit_ref=0 and not fragmented */ + if (last_data_unit == S7COMM_UD_LASTDATAUNIT_YES && length_rem > 0) { + switch (funcgroup) { + case S7COMM_UD_FUNCGROUP_PROG: + offset = s7comm_decode_ud_prog_subfunc(next_tvb, data_tree, type, subfunc, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_CYCLIC: + offset = s7comm_decode_ud_cyclic_subfunc(next_tvb, pinfo, seq_num, data_tree, type, subfunc, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_BLOCK: + offset = s7comm_decode_ud_block_subfunc(next_tvb, pinfo, data_tree, type, subfunc, ret_val, tsize, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_CPU: + switch (subfunc) { + case S7COMM_UD_SUBF_CPU_READSZL: + offset = s7comm_decode_ud_cpu_szl_subfunc(next_tvb, pinfo, data_tree, type, ret_val, length_rem, offset); + break; + case S7COMM_UD_SUBF_CPU_NOTIFY_IND: + case S7COMM_UD_SUBF_CPU_NOTIFY8_IND: + case S7COMM_UD_SUBF_CPU_ALARMSQ_IND: + case S7COMM_UD_SUBF_CPU_ALARMS_IND: + case S7COMM_UD_SUBF_CPU_SCAN_IND: + case S7COMM_UD_SUBF_CPU_ALARMACK: + case S7COMM_UD_SUBF_CPU_ALARMACK_IND: + case S7COMM_UD_SUBF_CPU_ALARM8_IND: + case S7COMM_UD_SUBF_CPU_ALARM8LOCK: + case S7COMM_UD_SUBF_CPU_ALARM8LOCK_IND: + case S7COMM_UD_SUBF_CPU_ALARM8UNLOCK: + case S7COMM_UD_SUBF_CPU_ALARM8UNLOCK_IND: + offset = s7comm_decode_ud_cpu_alarm_main(next_tvb, pinfo, data_tree, type, subfunc, offset); + break; + case S7COMM_UD_SUBF_CPU_ALARMQUERY: + if (type == S7COMM_UD_TYPE_RES) { + offset = s7comm_decode_ud_cpu_alarm_query_response(next_tvb, data_tree, offset); + } else { + offset = s7comm_decode_ud_cpu_alarm_main(next_tvb, pinfo, data_tree, type, subfunc, offset); + } + break; + case S7COMM_UD_SUBF_CPU_DIAGMSG: + offset = s7comm_decode_ud_cpu_diagnostic_message(next_tvb, pinfo, TRUE, data_tree, offset); + break; + case S7COMM_UD_SUBF_CPU_MSGS: + offset = s7comm_decode_message_service(next_tvb, pinfo, data_tree, type, length_rem, offset); + break; + default: + /* print other currently unknown data as raw bytes */ + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, next_tvb, offset, length_rem, ENC_NA); + break; + } + break; + case S7COMM_UD_FUNCGROUP_SEC: + offset = s7comm_decode_ud_security_subfunc(next_tvb, data_tree, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_PBC: + offset = s7comm_decode_ud_pbc_subfunc(next_tvb, data_tree, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_TIME: + offset = s7comm_decode_ud_time_subfunc(next_tvb, data_tree, type, subfunc, ret_val, length_rem, offset); + break; + case S7COMM_UD_FUNCGROUP_NCPRG: + offset = s7comm_decode_ud_ncprg_subfunc(next_tvb, pinfo, data_tree, type, subfunc, length_rem, offset); + break; + default: + break; + } + } + } + } + return offset; +} /******************************************************************************************************* ******************************************************************************************************* @@ -4868,11 +5143,7 @@ s7comm_decode_ud(tvbuff_t *tvb, { proto_item *item = NULL; proto_tree *param_tree = NULL; - proto_tree *data_tree = NULL; - guint8 ret_val; - guint8 tsize; - guint16 len; guint32 errorcode; guint32 offset_temp; guint32 reqres2; @@ -4887,10 +5158,7 @@ s7comm_decode_ud(tvbuff_t *tvb, item = proto_tree_add_item(tree, hf_s7comm_param, tvb, offset, plength, ENC_NA); param_tree = proto_item_add_subtree(item, ett_s7comm_param); - /* Try do decode some functions... - * Some functions may use data that doesn't fit one telegram - */ - offset_temp = offset; /* Save offset */ + offset_temp = offset; /* 3 bytes constant head */ proto_tree_add_item(param_tree, hf_s7comm_userdata_param_head, tvb, offset_temp, 3, ENC_BIG_ENDIAN); offset_temp += 3; @@ -4996,100 +5264,9 @@ s7comm_decode_ud(tvbuff_t *tvb, col_append_fstr(pinfo->cinfo, COL_INFO, " -> Errorcode:[0x%04x]", errorcode); } } + offset += plength; - /********************************** - * Add data tree - */ - offset += plength; /* set offset to the beginning of the data part */ - /* The first 4 bytes of the data part of a userdata telegram are the same for all types. - * This is also the minumum length of the data part. - */ - if (dlength >= 4) { - item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); - data_tree = proto_item_add_subtree(item, ett_s7comm_data); - - ret_val = tvb_get_guint8(tvb, offset); - proto_tree_add_uint(data_tree, hf_s7comm_data_returncode, tvb, offset, 1, ret_val); - offset += 1; - /* Not definitely known part, kind of "transport size"? constant 0x09, 1 byte - * The position is the same as in a data response/write telegram, - */ - tsize = tvb_get_guint8(tvb, offset); - proto_tree_add_uint(data_tree, hf_s7comm_data_transport_size, tvb, offset, 1, tsize); - offset += 1; - len = tvb_get_ntohs(tvb, offset); - proto_tree_add_uint(data_tree, hf_s7comm_data_length, tvb, offset, 2, len); - offset += 2; - - /* Call function to decode the rest of the data part - * decode only when there is a data part length greater 4 bytes - */ - if (dlength > 4) { - switch (funcgroup) { - case S7COMM_UD_FUNCGROUP_PROG: - offset = s7comm_decode_ud_prog_subfunc(tvb, data_tree, type, subfunc, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_CYCLIC: - offset = s7comm_decode_ud_cyclic_subfunc(tvb, pinfo, seq_num, data_tree, type, subfunc, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_BLOCK: - offset = s7comm_decode_ud_block_subfunc(tvb, pinfo, data_tree, type, subfunc, ret_val, tsize, len, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_CPU: - switch (subfunc) { - case S7COMM_UD_SUBF_CPU_READSZL: - offset = s7comm_decode_ud_cpu_szl_subfunc(tvb, pinfo, data_tree, type, ret_val, len, dlength, data_unit_ref, last_data_unit, offset); - break; - case S7COMM_UD_SUBF_CPU_NOTIFY_IND: - case S7COMM_UD_SUBF_CPU_NOTIFY8_IND: - case S7COMM_UD_SUBF_CPU_ALARMSQ_IND: - case S7COMM_UD_SUBF_CPU_ALARMS_IND: - case S7COMM_UD_SUBF_CPU_SCAN_IND: - case S7COMM_UD_SUBF_CPU_ALARMACK: - case S7COMM_UD_SUBF_CPU_ALARMACK_IND: - case S7COMM_UD_SUBF_CPU_ALARM8_IND: - case S7COMM_UD_SUBF_CPU_ALARM8LOCK: - case S7COMM_UD_SUBF_CPU_ALARM8LOCK_IND: - case S7COMM_UD_SUBF_CPU_ALARM8UNLOCK: - case S7COMM_UD_SUBF_CPU_ALARM8UNLOCK_IND: - offset = s7comm_decode_ud_cpu_alarm_main(tvb, pinfo, data_tree, type, subfunc, offset); - break; - case S7COMM_UD_SUBF_CPU_ALARMQUERY: - if (type == S7COMM_UD_TYPE_RES) { - offset = s7comm_decode_ud_cpu_alarm_query_response(tvb, pinfo, data_tree, offset); - } else { - offset = s7comm_decode_ud_cpu_alarm_main(tvb, pinfo, data_tree, type, subfunc, offset); - } - break; - case S7COMM_UD_SUBF_CPU_DIAGMSG: - offset = s7comm_decode_ud_cpu_diagnostic_message(tvb, pinfo, TRUE, data_tree, offset); - break; - case S7COMM_UD_SUBF_CPU_MSGS: - offset = s7comm_decode_message_service(tvb, pinfo, data_tree, type, dlength - 4, offset); - break; - default: - /* print other currently unknown data as raw bytes */ - proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); - break; - } - break; - case S7COMM_UD_FUNCGROUP_SEC: - offset = s7comm_decode_ud_security_subfunc(tvb, data_tree, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_PBC: - offset = s7comm_decode_ud_pbc_subfunc(tvb, data_tree, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_TIME: - offset = s7comm_decode_ud_time_subfunc(tvb, data_tree, type, subfunc, ret_val, dlength, offset); - break; - case S7COMM_UD_FUNCGROUP_NCPRG: - offset = s7comm_decode_ud_ncprg_subfunc(tvb, pinfo, data_tree, type, subfunc, dlength, offset); - break; - default: - break; - } - } - } + offset = s7comm_decode_ud_data(tvb, pinfo, tree, dlength, type, funcgroup, subfunc, seq_num, data_unit_ref, last_data_unit, offset); return offset; } @@ -5364,6 +5541,16 @@ dissect_s7comm(tvbuff_t *tvb, return TRUE; } +/******************************************************************************************************* + * Reassembly of S7COMM + *******************************************************************************************************/ +static void +s7comm_defragment_init(void) +{ + reassembly_table_init(&s7comm_reassembly_table, + &addresses_ports_reassembly_table_functions); +} + /******************************************************************************************************* *******************************************************************************************************/ void @@ -6096,6 +6283,18 @@ proto_register_s7comm (void) { &hf_s7comm_data_ncprg_unackcount, { "Number of telegrams sent without acknowledge", "s7comm.data.ncprg.unackcount", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_s7comm_data_ncprg_filelength, + { "NC file length", "s7comm.data.ncprg.filelength", FT_STRING, BASE_NONE, NULL, 0x0, + "NC file length: length of file date + file path", HFILL }}, + { &hf_s7comm_data_ncprg_filetime, + { "NC file timestamp", "s7comm.data.ncprg.filetime", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_ncprg_filepath, + { "NC file path", "s7comm.data.ncprg.filepath", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_ncprg_filedata, + { "NC file data", "s7comm.data.ncprg.filedata", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, /* Variable status */ { &hf_s7comm_varstat_data_type, @@ -6147,6 +6346,9 @@ proto_register_s7comm (void) { &hf_s7comm_pbc_r_id, { "PBC BSEND/BRECV R_ID", "s7comm.pbc.req.bsend.r_id", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_s7comm_pbc_len, + { "PBC BSEND/BRECV LEN", "s7comm.pbc.req.bsend.len", FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, /* CPU alarms */ { &hf_s7comm_cpu_alarm_message_item, @@ -6364,6 +6566,38 @@ proto_register_s7comm (void) { &hf_s7comm_tia1200_item_value, { "Value", "s7comm.tiap.item.value", FT_UINT32, BASE_DEC, NULL, 0x0fffffff, NULL, HFILL }}, + + /* Fragment fields */ + { &hf_s7comm_fragment_overlap, + { "Fragment overlap", "s7comm.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Fragment overlaps with other fragments", HFILL }}, + { &hf_s7comm_fragment_overlap_conflict, + { "Conflicting data in fragment overlap", "s7comm.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Overlapping fragments contained conflicting data", HFILL }}, + { &hf_s7comm_fragment_multiple_tails, + { "Multiple tail fragments found", "s7comm.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Several tails were found when defragmenting the packet", HFILL }}, + { &hf_s7comm_fragment_too_long_fragment, + { "Fragment too long", "s7comm.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Fragment contained data past end of packet", HFILL }}, + { &hf_s7comm_fragment_error, + { "Defragmentation error", "s7comm.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "Defragmentation error due to illegal fragments", HFILL }}, + { &hf_s7comm_fragment_count, + { "Fragment count", "s7comm.fragment.count", FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_reassembled_in, + { "Reassembled in", "s7comm.reassembled.in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "S7COMM fragments are reassembled in the given packet", HFILL }}, + { &hf_s7comm_reassembled_length, + { "Reassembled S7COMM length", "s7comm.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0, + "The total length of the reassembled payload", HFILL }}, + { &hf_s7comm_fragment, + { "S7COMM Fragment", "s7comm.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_fragments, + { "S7COMM Fragments", "s7comm.fragments", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, }; static ei_register_info ei[] = { @@ -6394,7 +6628,9 @@ proto_register_s7comm (void) &ett_s7comm_cpu_msgservice_subscribe_events, &ett_s7comm_piservice_parameterblock, &ett_s7comm_data_blockcontrol_status, - &ett_s7comm_plcfilename + &ett_s7comm_plcfilename, + &ett_s7comm_fragments, + &ett_s7comm_fragment, }; proto_s7comm = proto_register_protocol ( @@ -6411,6 +6647,8 @@ proto_register_s7comm (void) expert_s7comm = expert_register_protocol(proto_s7comm); expert_register_field_array(expert_s7comm, ei, array_length(ei)); + + register_init_routine(s7comm_defragment_init); } /* Register this protocol */ diff --git a/epan/dissectors/packet-s7comm_szl_ids.c b/epan/dissectors/packet-s7comm_szl_ids.c index e02de13534..97b8396986 100644 --- a/epan/dissectors/packet-s7comm_szl_ids.c +++ b/epan/dissectors/packet-s7comm_szl_ids.c @@ -241,8 +241,6 @@ static value_string_ext szl_partial_list_names_ext = VALUE_STRING_EXT_INIT(szl_p static gint hf_s7comm_userdata_szl_index = -1; /* SZL index */ static gint hf_s7comm_userdata_szl_tree = -1; /* SZL item tree */ -static gint hf_s7comm_userdata_szl_data = -1; /* SZL raw data */ - /* Index description for SZL Requests */ static const value_string szl_0111_index_names[] = { @@ -1745,8 +1743,8 @@ s7comm_szl_0131_0002_register(int proto) "Bit 2: Breakpoint", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_1_3, - { "Exit HOLD", "s7comm.szl.0131.0002.funkt_1.oexit_hold", FT_BOOLEAN, 8, NULL, 0x08, - "Bit 3: OExit HOLD", HFILL }}, + { "Exit HOLD", "s7comm.szl.0131.0002.funkt_1.exit_hold", FT_BOOLEAN, 8, NULL, 0x08, + "Bit 3: Exit HOLD", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_1_4, { "Memory reset", "s7comm.szl.0131.0002.funkt_1.mem_res", FT_BOOLEAN, 8, NULL, 0x10, @@ -1782,8 +1780,8 @@ s7comm_szl_0131_0002_register(int proto) "Bit 2: Replace job", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_2_3, - { "Reserved", "s7comm.szl.0131.0002.funkt_2.bit3_res", FT_BOOLEAN, 8, NULL, 0x08, - "Bit 3: Reserved", HFILL }}, + { "Block status v2", "s7comm.szl.0131.0002.funkt_2.block_stat_v2", FT_BOOLEAN, 8, NULL, 0x08, + "Bit 3: Block status v2", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_2_4, { "Reserved", "s7comm.szl.0131.0002.funkt_2.bit4_res", FT_BOOLEAN, 8, NULL, 0x10, @@ -1794,8 +1792,8 @@ s7comm_szl_0131_0002_register(int proto) "Bit 5: Reserved", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_2_6, - { "Reserved", "s7comm.szl.0131.0002.funkt_2.bit6_res", FT_BOOLEAN, 8, NULL, 0x40, - "Bit 6: Reserved", HFILL }}, + { "Flash LED", "s7comm.szl.0131.0002.funkt_2.flash_led", FT_BOOLEAN, 8, NULL, 0x40, + "Bit 6: Flash LED", HFILL }}, { &hf_s7comm_szl_0131_0002_funkt_2_7, { "Reserved", "s7comm.szl.0131.0002.funkt_2.bit7_res", FT_BOOLEAN, 8, NULL, 0x80, @@ -3830,10 +3828,6 @@ s7comm_register_szl_types(int proto) { &hf_s7comm_userdata_szl_id_partlist_cnt, { "SZL partial list count", "s7comm.data.userdata.szl_id.partlist_cnt", FT_UINT16, BASE_DEC, NULL, 0x0, "SZL partial list count: the number of datasets in the results", HFILL }}, - /* Raw and unknown data */ - { &hf_s7comm_userdata_szl_data, - { "SZL data", "s7comm.param.userdata.szl_data", FT_BYTES, BASE_NONE, NULL, 0x0, - NULL, HFILL }}, }; /* Register Subtrees */ @@ -3915,31 +3909,27 @@ s7comm_register_szl_types(int proto) *******************************************************************************************************/ guint32 s7comm_decode_ud_cpu_szl_subfunc(tvbuff_t *tvb, - packet_info *pinfo, - proto_tree *data_tree, - guint8 type, /* Type of data (request/response) */ - guint8 ret_val, /* Return value in data part */ - guint16 len, /* length given in data part */ - guint16 dlength, /* length of data part given in header */ - guint8 data_unit_ref, /* Data-unit-reference ID from parameter part, used for response fragment detection */ - guint8 last_data_unit, /* 0 is last, 1 is not last data unit, used for response fragment detection */ - guint32 offset) /* Offset on data part +4 */ + packet_info *pinfo, + proto_tree *data_tree, + guint8 type, /* Type of data (request/response) */ + guint8 ret_val, /* Return value in data part */ + guint32 dlength, + guint32 offset) { guint16 id; guint16 idx; guint16 list_len; guint16 list_count; guint16 i; - guint16 tbytes = 0; + guint32 start_offset; proto_item *szl_item = NULL; proto_tree *szl_item_tree = NULL; proto_item *szl_item_entry = NULL; const gchar* szl_index_description; - - gboolean know_data = FALSE; gboolean szl_decoded = FALSE; - if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + start_offset = offset; + if (type == S7COMM_UD_TYPE_REQ) { id = tvb_get_ntohs(tvb, offset); proto_tree_add_bitmask(data_tree, tvb, offset, hf_s7comm_userdata_szl_id, ett_s7comm_userdata_szl_id, s7comm_userdata_szl_id_fields, ENC_BIG_ENDIAN); @@ -3953,191 +3943,144 @@ s7comm_decode_ud_cpu_szl_subfunc(tvbuff_t *tvb, } proto_item_append_text(data_tree, " (SZL-ID: 0x%04x, Index: 0x%04x)", id, idx); col_append_fstr(pinfo->cinfo, COL_INFO, " ID=0x%04x Index=0x%04x" , id, idx); - know_data = TRUE; - } else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ - /* When response OK, data follows */ + } else if (type == S7COMM_UD_TYPE_RES) { if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK) { - /* A fragmented response has a data-unit-ref <> 0 with Last-data-unit == 1 - * It's only possible to decode the first response of a fragment, because - * only the first PDU contains the ID/Index header. Will result in an display-error when a PDU goes over more than 2 PDUs, but ... eeeek ... no better way to realize this. - * last_data_unit == 0 when it's the last unit - * last_data_unit == 1 when it's not the last unit - */ - if (data_unit_ref != 0 && last_data_unit == 0) { - szl_item = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_tree, tvb, offset, len, ENC_NA); - szl_item_tree = proto_item_add_subtree(szl_item, ett_s7comm_szl); - proto_item_append_text(szl_item, " [Fragment, continuation of previous data]"); - - proto_tree_add_item(szl_item_tree, hf_s7comm_userdata_szl_data, tvb, offset, len, ENC_NA); - offset += len; - col_append_fstr(pinfo->cinfo, COL_INFO, " SZL data fragment"); - } else { - id = tvb_get_ntohs(tvb, offset); - proto_tree_add_bitmask(data_tree, tvb, offset, hf_s7comm_userdata_szl_id, - ett_s7comm_userdata_szl_id, s7comm_userdata_szl_id_fields, ENC_BIG_ENDIAN); - offset += 2; - idx = tvb_get_ntohs(tvb, offset); - szl_item_entry = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_index, tvb, offset, 2, ENC_BIG_ENDIAN); - offset += 2; - szl_index_description = s7comm_get_szl_id_index_description_text(id, idx); - if (szl_index_description != NULL) { - proto_item_append_text(szl_item_entry, " [%s]", szl_index_description); - } - proto_item_append_text(data_tree, " (SZL-ID: 0x%04x, Index: 0x%04x)", id, idx); - col_append_fstr(pinfo->cinfo, COL_INFO, " ID=0x%04x Index=0x%04x" , id, idx); - - /* SZL-Data, 4 Bytes header, 4 bytes id/index = 8 bytes */ - list_len = tvb_get_ntohs(tvb, offset); /* Length of an list set in bytes */ - proto_tree_add_uint(data_tree, hf_s7comm_userdata_szl_id_partlist_len, tvb, offset, 2, list_len); - offset += 2; - list_count = tvb_get_ntohs(tvb, offset); /* count of partlists */ - proto_tree_add_uint(data_tree, hf_s7comm_userdata_szl_id_partlist_cnt, tvb, offset, 2, list_count); - /* Some SZL responses got more lists than fit one PDU (e.g. Diagnosepuffer) and must be read - * out in several telegrams, so we have to check here if the list_count is above limits - * of the length of data part. The remainding bytes will be print as raw bytes, because - * it's not possible to decode this and following telegrams without knowing the previous requests. - */ - tbytes = 0; - if (list_len > 0 && list_count > 0) { - if ( (guint32) (list_count * list_len) > (guint32) (len - 8)) { - list_count = (len - 8) / list_len; - /* remind the number of trailing bytes */ - if (list_count > 0) { - tbytes = (len - 8) % list_count; - } - } - } - else { - tbytes = len - 8; - } - - offset += 2; - /* Add a Data element for each partlist */ - if (len > 8) { /* minimum length of a correct szl data part is 8 bytes */ - for (i = 1; i <= list_count && (list_count * list_len != 0); i++) { - /* Add a separate tree for the SZL data */ - szl_item = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_tree, tvb, offset, list_len, ENC_NA); - szl_item_tree = proto_item_add_subtree(szl_item, ett_s7comm_szl); - proto_item_append_text(szl_item, " (list count no. %d)", i); - - szl_decoded = FALSE; - /* lets try to decode some known szl-id and indexes */ - switch (id) { - case 0x0000: - offset = s7comm_decode_szl_id_xy00(tvb, szl_item_tree, id, idx, offset); + id = tvb_get_ntohs(tvb, offset); + proto_tree_add_bitmask(data_tree, tvb, offset, hf_s7comm_userdata_szl_id, + ett_s7comm_userdata_szl_id, s7comm_userdata_szl_id_fields, ENC_BIG_ENDIAN); + offset += 2; + idx = tvb_get_ntohs(tvb, offset); + szl_item_entry = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_index, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + szl_index_description = s7comm_get_szl_id_index_description_text(id, idx); + if (szl_index_description != NULL) { + proto_item_append_text(szl_item_entry, " [%s]", szl_index_description); + } + proto_item_append_text(data_tree, " (SZL-ID: 0x%04x, Index: 0x%04x)", id, idx); + col_append_fstr(pinfo->cinfo, COL_INFO, " ID=0x%04x Index=0x%04x" , id, idx); + + /* SZL-Data, 4 Bytes header, 4 bytes id/index = 8 bytes */ + list_len = tvb_get_ntohs(tvb, offset); /* Length of an list set in bytes */ + proto_tree_add_uint(data_tree, hf_s7comm_userdata_szl_id_partlist_len, tvb, offset, 2, list_len); + offset += 2; + list_count = tvb_get_ntohs(tvb, offset); /* count of partlists */ + proto_tree_add_uint(data_tree, hf_s7comm_userdata_szl_id_partlist_cnt, tvb, offset, 2, list_count); + offset += 2; + /* Check the listcount, as in fragmented packets some CPUs (firmware bug?) send 0xffff as list count */ + if (((guint32)list_count * (guint32)list_len) > (dlength - (offset - start_offset))) { + /* TODO: Make entry in expert field */ + list_count = (dlength - (offset - start_offset)) / list_len; + } + /* Add a Data element for each partlist */ + if (dlength > 8) { /* minimum length of a correct szl data part is 8 bytes */ + for (i = 1; i <= list_count && (list_count * list_len != 0); i++) { + /* Add a separate tree for the SZL data */ + szl_item = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_tree, tvb, offset, list_len, ENC_NA); + szl_item_tree = proto_item_add_subtree(szl_item, ett_s7comm_szl); + proto_item_append_text(szl_item, " (list count no. %d)", i); + szl_decoded = FALSE; + /* lets try to decode some known szl-id and indexes */ + switch (id) { + case 0x0000: + offset = s7comm_decode_szl_id_xy00(tvb, szl_item_tree, id, idx, offset); + szl_decoded = TRUE; + break; + case 0x0013: + if (idx == 0x0000) { + offset = s7comm_decode_szl_id_0013_idx_0000(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } + break; + case 0x0011: + case 0x0111: + if ((idx == 0x0001) || (idx == 0x0000)) { + offset = s7comm_decode_szl_id_0111_idx_0001(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } + break; + case 0x00a0: + case 0x01a0: + case 0x04a0: + case 0x05a0: + case 0x06a0: + case 0x07a0: + case 0x08a0: + case 0x09a0: + case 0x0aa0: + case 0x0ba0: + case 0x0ca0: + case 0x0da0: + case 0x0ea0: + /* the data structure is the same as used when CPU is sending online such messages */ + offset = s7comm_decode_ud_cpu_diagnostic_message(tvb, pinfo, FALSE, szl_item_tree, offset); + szl_decoded = TRUE; + break; + case 0x0131: + if (idx == 0x0001) { + offset = s7comm_decode_szl_id_0131_idx_0001(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0002) { + offset = s7comm_decode_szl_id_0131_idx_0002(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0003) { + offset = s7comm_decode_szl_id_0131_idx_0003(tvb, szl_item_tree, offset); szl_decoded = TRUE; - break; - case 0x0013: - if (idx == 0x0000) { - offset = s7comm_decode_szl_id_0013_idx_0000(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } - break; - case 0x0011: - case 0x0111: - if ((idx == 0x0001) || (idx == 0x0000)) { - offset = s7comm_decode_szl_id_0111_idx_0001(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } - break; - case 0x00a0: - case 0x01a0: - case 0x04a0: - case 0x05a0: - case 0x06a0: - case 0x07a0: - case 0x08a0: - case 0x09a0: - case 0x0aa0: - case 0x0ba0: - case 0x0ca0: - case 0x0da0: - case 0x0ea0: - /* the data structure is the same as used when CPU is sending online such messages */ - offset = s7comm_decode_ud_cpu_diagnostic_message(tvb, pinfo, FALSE, szl_item_tree, offset); + } else if (idx == 0x0004) { + offset = s7comm_decode_szl_id_0131_idx_0004(tvb, szl_item_tree, offset); szl_decoded = TRUE; - break; - case 0x0131: - if (idx == 0x0001) { - offset = s7comm_decode_szl_id_0131_idx_0001(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0002) { - offset = s7comm_decode_szl_id_0131_idx_0002(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0003) { - offset = s7comm_decode_szl_id_0131_idx_0003(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0004) { - offset = s7comm_decode_szl_id_0131_idx_0004(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0006) { - offset = s7comm_decode_szl_id_0131_idx_0006(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0010) { - offset = s7comm_decode_szl_id_0131_idx_0010(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } - break; - case 0x0132: - if (idx == 0x0001) { - offset = s7comm_decode_szl_id_0132_idx_0001(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0002) { - offset = s7comm_decode_szl_id_0132_idx_0002(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0004) { - offset = s7comm_decode_szl_id_0132_idx_0004(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0005) { - offset = s7comm_decode_szl_id_0132_idx_0005(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } else if (idx == 0x0006) { - offset = s7comm_decode_szl_id_0132_idx_0006(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } - break; - case 0x0019: - case 0x0119: - case 0x0074: - case 0x0174: - offset = s7comm_decode_szl_id_xy74_idx_0000(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - break; - case 0x0124: - case 0x0424: - if (idx == 0x0000) { - offset = s7comm_decode_szl_id_0424_idx_0000(tvb, szl_item_tree, offset); - szl_decoded = TRUE; - } - break; - default: - szl_decoded = FALSE; - break; - } - if (szl_decoded == FALSE) { - proto_tree_add_item(szl_item_tree, hf_s7comm_userdata_szl_partial_list, tvb, offset, list_len, ENC_NA); - offset += list_len; - } - } /* ...for */ - } + } else if (idx == 0x0006) { + offset = s7comm_decode_szl_id_0131_idx_0006(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0010) { + offset = s7comm_decode_szl_id_0131_idx_0010(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } + break; + case 0x0132: + if (idx == 0x0001) { + offset = s7comm_decode_szl_id_0132_idx_0001(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0002) { + offset = s7comm_decode_szl_id_0132_idx_0002(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0004) { + offset = s7comm_decode_szl_id_0132_idx_0004(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0005) { + offset = s7comm_decode_szl_id_0132_idx_0005(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } else if (idx == 0x0006) { + offset = s7comm_decode_szl_id_0132_idx_0006(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } + break; + case 0x0019: + case 0x0119: + case 0x0074: + case 0x0174: + offset = s7comm_decode_szl_id_xy74_idx_0000(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + break; + case 0x0124: + case 0x0424: + if (idx == 0x0000) { + offset = s7comm_decode_szl_id_0424_idx_0000(tvb, szl_item_tree, offset); + szl_decoded = TRUE; + } + break; + default: + szl_decoded = FALSE; + break; + } + if (szl_decoded == FALSE) { + proto_tree_add_item(szl_item_tree, hf_s7comm_userdata_szl_partial_list, tvb, offset, list_len, ENC_NA); + offset += list_len; + } + } /* ...for */ } } else { col_append_fstr(pinfo->cinfo, COL_INFO, " Return value:[%s]", val_to_str(ret_val, s7comm_item_return_valuenames, "Unknown return value:0x%02x")); } - know_data = TRUE; - } - /* add raw bytes of data part when SZL response doesn't fit one PDU */ - if (know_data == TRUE && tbytes > 0) { - /* Add a separate tree for the SZL data fragment */ - szl_item = proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_tree, tvb, offset, tbytes, ENC_NA); - szl_item_tree = proto_item_add_subtree(szl_item, ett_s7comm_szl); - proto_item_append_text(szl_item, " [Fragment, complete response doesn't fit one PDU]"); - proto_tree_add_item(szl_item_tree, hf_s7comm_userdata_szl_data, tvb, offset, tbytes, ENC_NA); - offset += tbytes; - } - if (know_data == FALSE && dlength > 4) { - proto_tree_add_item(data_tree, hf_s7comm_userdata_szl_data, tvb, offset, dlength - 4, ENC_NA); - offset += dlength; } return offset; } diff --git a/epan/dissectors/packet-s7comm_szl_ids.h b/epan/dissectors/packet-s7comm_szl_ids.h index bf7af01786..d762647139 100644 --- a/epan/dissectors/packet-s7comm_szl_ids.h +++ b/epan/dissectors/packet-s7comm_szl_ids.h @@ -13,7 +13,7 @@ #ifndef __PACKET_S7COMM_SZL_IDS_H__ #define __PACKET_S7COMM_SZL_IDS_H__ -guint32 s7comm_decode_ud_cpu_szl_subfunc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *data_tree, guint8 type, guint8 ret_val, guint16 len, guint16 dlength, guint8 data_unit_ref, guint8 last_data_unit, guint32 offset); +guint32 s7comm_decode_ud_cpu_szl_subfunc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *data_tree, guint8 type, guint8 ret_val, guint32 dlength, guint32 offset); void s7comm_register_szl_types(int proto); #endif -- cgit v1.2.3