From 1c1f187989e982a8172bb03542721c0cda6d9006 Mon Sep 17 00:00:00 2001 From: Thomas Wiens Date: Thu, 21 Aug 2014 19:25:06 +0200 Subject: s7comm: Add dissector for S7 Communication S7 Communication is a Siemens proprietary protocol that runs between programmable logic controllers (PLC) of the Siemens S7-300/400 family. Dissector T.125 has to be disabled to let this dissector work. Change-Id: I578cf270a4ae567f8e20dbabec1ce1e13fc08e6e Reviewed-on: https://code.wireshark.org/review/3777 Reviewed-by: Alexis La Goutte Petri-Dish: Alexis La Goutte Tested-by: Petri Dish Buildbot Reviewed-by: Michael Mann --- epan/dissectors/packet-s7comm.c | 3216 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 3216 insertions(+) create mode 100644 epan/dissectors/packet-s7comm.c (limited to 'epan/dissectors/packet-s7comm.c') diff --git a/epan/dissectors/packet-s7comm.c b/epan/dissectors/packet-s7comm.c new file mode 100644 index 0000000000..4e76253118 --- /dev/null +++ b/epan/dissectors/packet-s7comm.c @@ -0,0 +1,3216 @@ +/* packet-s7comm.c + * + * Author: Thomas Wiens, 2014 (th.wiens@gmx.de) + * Description: Wireshark dissector for S7-Communication + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include + +#include "packet-s7comm.h" +#include "packet-s7comm_szl_ids.h" + +#define PROTO_TAG_S7COMM "S7COMM" + +/* Min. telegram length for heuristic check */ +#define S7COMM_MIN_TELEGRAM_LENGTH 10 + +/* Protocol identifier */ +#define S7COMM_PROT_ID 0x32 + +/* Wireshark ID of the S7COMM protocol */ +static int proto_s7comm = -1; + +/* Forward declarations */ +void proto_reg_handoff_s7comm(void); +void proto_register_s7comm (void); + +/************************************************************************** + * Function call tree of the dissect process + +dissect_s7comm() + + + +-------s7comm_decode_req_resp() + + + + + + response request + + + + + + + +------ s7comm_decode_param_item() + + + + s7comm_decode_response_read_data() + + + + + + + +------ s7comm_decode_pdu_setup_communication() + + + +------ s7comm_decode_plc_controls_param_hex1x() + + + +------ s7comm_decode_plc_controls_param_hex28() + + + +------ s7comm_decode_plc_controls_param_hex29() + + + + + +------ s7comm_decode_response_read_data() + + +------ s7comm_decode_response_write_data() + + +------ s7comm_decode_pdu_setup_communication() + + + + + +-------s7comm_decode_ud() + + + +------ s7comm_decode_ud_prog_subfunc() + + + + + +------- s7comm_decode_ud_prog_vartab_req_item() + + +------- s7comm_decode_ud_prog_vartab_res_item() + + +------- s7comm_decode_ud_prog_reqdiagdata() + + + +------ s7comm_decode_ud_cyclic_subfunc() + + + + + +------- s7comm_decode_param_item() + + +------- s7comm_decode_response_read_data() + + + +------ s7comm_decode_ud_block_subfunc() + +------ s7comm_decode_ud_szl_subfunc() + + + + + +------- s7comm_decode_szl_id_XXXX_idx_XXXX() + + + +------ s7comm_decode_ud_security_subfunc() + +------ s7comm_decode_ud_time_subfunc() + + **************************************************************************/ + + + +/************************************************************************** + * PDU types + */ +#define S7COMM_ROSCTR_JOB 0x01 +#define S7COMM_ROSCTR_ACK 0x02 +#define S7COMM_ROSCTR_ACK_DATA 0x03 +#define S7COMM_ROSCTR_USERDATA 0x07 + +static const value_string rosctr_names[] = { + { S7COMM_ROSCTR_JOB, "Job" }, /* Request: job with acknowledgement */ + { S7COMM_ROSCTR_ACK, "Ack" }, /* acknowledgement without additional field */ + { S7COMM_ROSCTR_ACK_DATA, "Ack_Data" }, /* Response: acknowledgement with additional field */ + { S7COMM_ROSCTR_USERDATA, "Userdata" }, + { 0, NULL } +}; +/************************************************************************** + * Error classes in header + */ +#define S7COMM_ERRCLS_NONE 0x00 +#define S7COMM_ERRCLS_APPREL 0x81 +#define S7COMM_ERRCLS_OBJDEF 0x82 +#define S7COMM_ERRCLS_RESSOURCE 0x83 +#define S7COMM_ERRCLS_SERVICE 0x84 +#define S7COMM_ERRCLS_SUPPLIES 0x85 +#define S7COMM_ERRCLS_ACCESS 0x87 + +static const value_string errcls_names[] = { + { S7COMM_ERRCLS_NONE, "No error" }, + { S7COMM_ERRCLS_APPREL, "Application relationship" }, + { S7COMM_ERRCLS_OBJDEF, "Object definition" }, + { S7COMM_ERRCLS_RESSOURCE, "No ressources available" }, + { S7COMM_ERRCLS_SERVICE, "Error on service processing" }, + { S7COMM_ERRCLS_SUPPLIES, "Error on supplies" }, + { S7COMM_ERRCLS_ACCESS, "Access error" }, + { 0, NULL } +}; + +/************************************************************************** + * Error code in parameter part + */ +#define S7COMM_PERRCOD_NO_ERROR 0x0000 +#define S7COMM_PERRCOD_INVALID_BLOCK_TYPE_NUM 0x0110 +#define S7COMM_PERRCOD_INVALID_PARAM 0x0112 +#define S7COMM_PERRCOD_PG_RESOURCE_ERROR 0x011A +#define S7COMM_PERRCOD_PLC_RESOURCE_ERROR 0x011B +#define S7COMM_PERRCOD_PROTOCOL_ERROR 0x011C +#define S7COMM_PERRCOD_USER_BUFFER_TOO_SHORT 0x011F +#define S7COMM_PERRCOD_REQ_INI_ERR 0x0141 +#define S7COMM_PERRCOD_VERSION_MISMATCH 0x01C0 +#define S7COMM_PERRCOD_NOT_IMPLEMENTED 0x01F0 +#define S7COMM_PERRCOD_L7_INVALID_CPU_STATE 0x8001 +#define S7COMM_PERRCOD_L7_PDU_SIZE_ERR 0x8500 +#define S7COMM_PERRCOD_L7_INVALID_SZL_ID 0xD401 +#define S7COMM_PERRCOD_L7_INVALID_INDEX 0xD402 +#define S7COMM_PERRCOD_L7_DGS_CONN_ALREADY_ANNOU 0xD403 +#define S7COMM_PERRCOD_L7_MAX_USER_NB 0xD404 +#define S7COMM_PERRCOD_L7_DGS_FKT_PAR_SYNTAX_ERR 0xD405 +#define S7COMM_PERRCOD_L7_NO_INFO 0xD406 +#define S7COMM_PERRCOD_L7_PRT_FKT_PAR_SYNTAX_ERR 0xD601 +#define S7COMM_PERRCOD_L7_INVALID_VAR_ADDR 0xD801 +#define S7COMM_PERRCOD_L7_UNKNOWN_REQ 0xD802 +#define S7COMM_PERRCOD_L7_INVALID_REQ_STATUS 0xD803 + +static const value_string param_errcode_names[] = { + { S7COMM_PERRCOD_NO_ERROR, "No error" }, + { S7COMM_PERRCOD_INVALID_BLOCK_TYPE_NUM, "Invalid block type number" }, + { S7COMM_PERRCOD_INVALID_PARAM, "Invalid parameter" }, + { S7COMM_PERRCOD_PG_RESOURCE_ERROR, "PG ressource error" }, + { S7COMM_PERRCOD_PLC_RESOURCE_ERROR, "PLC ressource error" }, + { S7COMM_PERRCOD_PROTOCOL_ERROR, "Protocol error" }, + { S7COMM_PERRCOD_USER_BUFFER_TOO_SHORT, "User buffer too short" }, + { S7COMM_PERRCOD_REQ_INI_ERR, "Request error" }, + { S7COMM_PERRCOD_VERSION_MISMATCH, "Version mismatch" }, + { S7COMM_PERRCOD_NOT_IMPLEMENTED, "Not implemented" }, + { S7COMM_PERRCOD_L7_INVALID_CPU_STATE, "L7 invalid CPU state" }, + { S7COMM_PERRCOD_L7_PDU_SIZE_ERR, "L7 PDU size error" }, + { S7COMM_PERRCOD_L7_INVALID_SZL_ID, "L7 invalid SZL ID" }, + { S7COMM_PERRCOD_L7_INVALID_INDEX, "L7 invalid index" }, + { S7COMM_PERRCOD_L7_DGS_CONN_ALREADY_ANNOU, "L7 DGS Connection already announced" }, + { S7COMM_PERRCOD_L7_MAX_USER_NB, "L7 Max user NB" }, + { S7COMM_PERRCOD_L7_DGS_FKT_PAR_SYNTAX_ERR, "L7 DGS function parameter syntax error" }, + { S7COMM_PERRCOD_L7_NO_INFO, "L7 no info" }, + { S7COMM_PERRCOD_L7_PRT_FKT_PAR_SYNTAX_ERR, "L7 PRT function parameter syntax error" }, + { S7COMM_PERRCOD_L7_INVALID_VAR_ADDR, "L7 invalid variable address" }, + { S7COMM_PERRCOD_L7_UNKNOWN_REQ, "L7 unknown request" }, + { S7COMM_PERRCOD_L7_INVALID_REQ_STATUS, "L7 invalid request status" }, + { 0, NULL } +}; + +/************************************************************************** + * Function codes in parameter part + */ +#define S7COMM_SERV_CPU 0x00 +#define S7COMM_SERV_SETUPCOMM 0xF0 +#define S7COMM_SERV_READVAR 0x04 +#define S7COMM_SERV_WRITEVAR 0x05 + +#define S7COMM_FUNCREQUESTDOWNLOAD 0x1A +#define S7COMM_FUNCDOWNLOADBLOCK 0x1B +#define S7COMM_FUNCDOWNLOADENDED 0x1C +#define S7COMM_FUNCSTARTUPLOAD 0x1D +#define S7COMM_FUNCUPLOAD 0x1E +#define S7COMM_FUNCENDUPLOAD 0x1F +#define S7COMM_FUNC_PLC_CONTROL 0x28 +#define S7COMM_FUNC_PLC_STOP 0x29 + +static const value_string param_functionnames[] = { + { S7COMM_SERV_CPU, "CPU services" }, + { S7COMM_SERV_SETUPCOMM, "Setup communication" }, + { S7COMM_SERV_READVAR, "Read Var" }, + { S7COMM_SERV_WRITEVAR, "Write Var" }, + /* Block management services */ + { S7COMM_FUNCREQUESTDOWNLOAD, "Request download" }, + { S7COMM_FUNCDOWNLOADBLOCK, "Download block" }, + { S7COMM_FUNCDOWNLOADENDED, "Download ended" }, + { S7COMM_FUNCSTARTUPLOAD, "Start upload" }, + { S7COMM_FUNCUPLOAD, "Upload" }, + { S7COMM_FUNCENDUPLOAD, "End upload" }, + { S7COMM_FUNC_PLC_CONTROL, "PLC Control" }, + { S7COMM_FUNC_PLC_STOP, "PLC Stop" }, + { 0, NULL } +}; +/************************************************************************** + * Area names + */ +#define S7COMM_AREA_SYSINFO 0x03 /* System info of 200 family */ +#define S7COMM_AREA_SYSFLAGS 0x05 /* System flags of 200 family */ +#define S7COMM_AREA_ANAIN 0x06 /* analog inputs of 200 family */ +#define S7COMM_AREA_ANAOUT 0x07 /* analog outputs of 200 family */ +#define S7COMM_AREA_P 0x80 /* direct peripheral access */ +#define S7COMM_AREA_INPUTS 0x81 +#define S7COMM_AREA_OUTPUTS 0x82 +#define S7COMM_AREA_FLAGS 0x83 +#define S7COMM_AREA_DB 0x84 /* data blocks */ +#define S7COMM_AREA_DI 0x85 /* instance data blocks */ +#define S7COMM_AREA_LOCAL 0x86 /* local data (should not be accessible over network) */ +#define S7COMM_AREA_V 0x87 /* previous (Vorgaenger) local data (should not be accessible over network) */ +#define S7COMM_AREA_COUNTER 28 /* S7 counters */ +#define S7COMM_AREA_TIMER 29 /* S7 timers */ +#define S7COMM_AREA_COUNTER200 30 /* IEC counters (200 family) */ +#define S7COMM_AREA_TIMER200 31 /* IEC timers (200 family) */ + +static const value_string item_areanames[] = { + { S7COMM_AREA_SYSINFO, "System info of 200 family" }, + { S7COMM_AREA_SYSFLAGS, "System flags of 200 family" }, + { S7COMM_AREA_ANAIN, "Analog inputs of 200 family" }, + { S7COMM_AREA_ANAOUT, "Analog outputs of 200 family" }, + { S7COMM_AREA_P, "Direct peripheral access (P)" }, + { S7COMM_AREA_INPUTS, "Inputs (I)" }, + { S7COMM_AREA_OUTPUTS, "Outputs (Q)" }, + { S7COMM_AREA_FLAGS, "Flags (M)" }, + { S7COMM_AREA_DB, "Data blocks (DB)" }, + { S7COMM_AREA_DI, "Instance data blocks (DI)" }, + { S7COMM_AREA_LOCAL, "Local data (L)" }, + { S7COMM_AREA_V, "Unknown yet (V)" }, + { S7COMM_AREA_COUNTER, "S7 counters (C)" }, + { S7COMM_AREA_TIMER, "S7 timers (T)" }, + { S7COMM_AREA_COUNTER200, "IEC counters (200 family)" }, + { S7COMM_AREA_TIMER200, "IEC timers (200 family)" }, + { 0, NULL } +}; +/************************************************************************** + * Transport sizes in item data + */ + /* types of 1 byte length */ +#define S7COMM_TRANSPORT_SIZE_BIT 1 +#define S7COMM_TRANSPORT_SIZE_BYTE 2 +#define S7COMM_TRANSPORT_SIZE_CHAR 3 + /* types of 2 bytes length */ +#define S7COMM_TRANSPORT_SIZE_WORD 4 +#define S7COMM_TRANSPORT_SIZE_INT 5 + /* types of 4 bytes length */ +#define S7COMM_TRANSPORT_SIZE_DWORD 6 +#define S7COMM_TRANSPORT_SIZE_DINT 7 +#define S7COMM_TRANSPORT_SIZE_REAL 8 + /* Special types */ +#define S7COMM_TRANSPORT_SIZE_DATE 9 +#define S7COMM_TRANSPORT_SIZE_TOD 10 +#define S7COMM_TRANSPORT_SIZE_TIME 11 +#define S7COMM_TRANSPORT_SIZE_S5TIME 12 +#define S7COMM_TRANSPORT_SIZE_DT 15 + /* Timer or counter */ +#define S7COMM_TRANSPORT_SIZE_COUNTER 28 +#define S7COMM_TRANSPORT_SIZE_TIMER 29 +#define S7COMM_TRANSPORT_SIZE_IEC_COUNTER 30 +#define S7COMM_TRANSPORT_SIZE_IEC_TIMER 31 +#define S7COMM_TRANSPORT_SIZE_HS_COUNTER 32 +static const value_string item_transportsizenames[] = { + { S7COMM_TRANSPORT_SIZE_BIT, "BIT" }, + { S7COMM_TRANSPORT_SIZE_BYTE, "BYTE" }, + { S7COMM_TRANSPORT_SIZE_CHAR, "CHAR" }, + { S7COMM_TRANSPORT_SIZE_WORD, "WORD" }, + { S7COMM_TRANSPORT_SIZE_INT, "INT" }, + { S7COMM_TRANSPORT_SIZE_DWORD, "DWORD" }, + { S7COMM_TRANSPORT_SIZE_DINT, "DINT" }, + { S7COMM_TRANSPORT_SIZE_REAL, "REAL" }, + { S7COMM_TRANSPORT_SIZE_TOD, "TOD" }, + { S7COMM_TRANSPORT_SIZE_TIME, "TIME" }, + { S7COMM_TRANSPORT_SIZE_S5TIME, "S5TIME" }, + { S7COMM_TRANSPORT_SIZE_DT, "DATE_AND_TIME" }, + { S7COMM_TRANSPORT_SIZE_COUNTER, "COUNTER" }, + { S7COMM_TRANSPORT_SIZE_TIMER, "TIMER" }, + { S7COMM_TRANSPORT_SIZE_IEC_COUNTER, "IEC TIMER" }, + { S7COMM_TRANSPORT_SIZE_IEC_TIMER, "IEC COUNTER" }, + { S7COMM_TRANSPORT_SIZE_HS_COUNTER, "HS COUNTER" }, + { 0, NULL } +}; + +/************************************************************************** + * Syntax Ids of variable specification + */ +#define S7COMM_SYNTAXID_S7ANY 0x10 /* Adress data S7-Any pointer-like DB1.DBX10.2 */ +#define S7COMM_SYNTAXID_DRIVEESANY 0xa2 /* seen on Drive ES Starter with routing over S7 */ +#define S7COMM_SYNTAXID_1200SYM 0xb2 /* Symbolic address mode of S7-1200 */ +#define S7COMM_SYNTAXID_DBREAD 0xb0 /* Kind of DB block read, seen only at an S7-400 */ + +static const value_string item_syntaxid_names[] = { + { S7COMM_SYNTAXID_S7ANY, "S7ANY" }, + { S7COMM_SYNTAXID_DRIVEESANY, "DRIVEESANY" }, + { S7COMM_SYNTAXID_1200SYM, "1200SYM" }, + { S7COMM_SYNTAXID_DBREAD, "DBREAD" }, + { 0, NULL } +}; + +/************************************************************************** + * Transport sizes in data + */ +#define S7COMM_DATA_TRANSPORT_SIZE_NULL 0 +#define S7COMM_DATA_TRANSPORT_SIZE_BBIT 3 /* bit access, len is in bits */ +#define S7COMM_DATA_TRANSPORT_SIZE_BBYTE 4 /* byte/word/dword acces, len is in bits */ +#define S7COMM_DATA_TRANSPORT_SIZE_BINT 5 /* integer access, len is in bits */ +#define S7COMM_DATA_TRANSPORT_SIZE_BREAL 7 /* real access, len is in bytes */ +#define S7COMM_DATA_TRANSPORT_SIZE_BSTR 9 /* octet string, len is in bytes */ + +static const value_string data_transportsizenames[] = { + { S7COMM_DATA_TRANSPORT_SIZE_NULL, "NULL" }, + { S7COMM_DATA_TRANSPORT_SIZE_BBIT, "BIT" }, + { S7COMM_DATA_TRANSPORT_SIZE_BBYTE, "BYTE/WORD/DWORD" }, + { S7COMM_DATA_TRANSPORT_SIZE_BINT, "INTEGER" }, + { S7COMM_DATA_TRANSPORT_SIZE_BREAL, "REAL" }, + { S7COMM_DATA_TRANSPORT_SIZE_BSTR, "OCTET STRING" }, + { 0, NULL } +}; +/************************************************************************** + * Returnvalues of an item response + */ + +const value_string s7comm_item_return_valuenames[] = { + { S7COMM_ITEM_RETVAL_RESERVED, "Reserved" }, + { S7COMM_ITEM_RETVAL_DATA_HW_FAULT, "Hardware error" }, + { S7COMM_ITEM_RETVAL_DATA_ACCESS_FAULT, "Accessing the object not allowed" }, + { S7COMM_ITEM_RETVAL_DATA_OUTOFRANGE, "Invalid address" }, + { S7COMM_ITEM_RETVAL_DATA_NOT_SUP, "Data type not supported" }, + { S7COMM_ITEM_RETVAL_DATA_SIZEMISMATCH, "Data type inconsistent" }, + { S7COMM_ITEM_RETVAL_DATA_ERR, "Object does not exist" }, + { S7COMM_ITEM_RETVAL_DATA_OK, "Success" }, + { 0, NULL } +}; +/************************************************************************** + * Block Types + */ +#define S7COMM_BLOCKTYPE_OB '8' +#define S7COMM_BLOCKTYPE_DB 'A' +#define S7COMM_BLOCKTYPE_SDB 'B' +#define S7COMM_BLOCKTYPE_FC 'C' +#define S7COMM_BLOCKTYPE_SFC 'D' +#define S7COMM_BLOCKTYPE_FB 'E' +#define S7COMM_BLOCKTYPE_SFB 'F' + +static const value_string blocktype_names[] = { + { S7COMM_BLOCKTYPE_OB, "OB" }, + { S7COMM_BLOCKTYPE_DB, "DB" }, + { S7COMM_BLOCKTYPE_SDB, "SDB" }, + { S7COMM_BLOCKTYPE_FC, "FC" }, + { S7COMM_BLOCKTYPE_SFC, "SFC" }, + { S7COMM_BLOCKTYPE_FB, "FB" }, + { S7COMM_BLOCKTYPE_SFB, "SFB" }, + { 0, NULL } +}; + +/************************************************************************** + * Subblk types + */ +#define S7COMM_SUBBLKTYPE_OB 0x08 +#define S7COMM_SUBBLKTYPE_DB 0x0a +#define S7COMM_SUBBLKTYPE_SDB 0x0b +#define S7COMM_SUBBLKTYPE_FC 0x0c +#define S7COMM_SUBBLKTYPE_SFC 0x0d +#define S7COMM_SUBBLKTYPE_FB 0x0e +#define S7COMM_SUBBLKTYPE_SFB 0x0f + +static const value_string subblktype_names[] = { + { S7COMM_SUBBLKTYPE_OB, "OB" }, + { S7COMM_SUBBLKTYPE_DB, "DB" }, + { S7COMM_SUBBLKTYPE_SDB, "SDB" }, + { S7COMM_SUBBLKTYPE_FC, "FC" }, + { S7COMM_SUBBLKTYPE_SFC, "SFC" }, + { S7COMM_SUBBLKTYPE_FB, "FB" }, + { S7COMM_SUBBLKTYPE_SFB, "SFB" }, + { 0, NULL } +}; + +/************************************************************************** + * Block security + */ +#define S7COMM_BLOCKSECURITY_OFF 0 +#define S7COMM_BLOCKSECURITY_KNOWHOWPROTECT 3 + +static const value_string blocksecurity_names[] = { + { S7COMM_BLOCKSECURITY_OFF, "None" }, + { S7COMM_BLOCKSECURITY_KNOWHOWPROTECT, "Kow How Protect" }, + { 0, NULL } +}; +/************************************************************************** + * Block Languages + */ +static const value_string blocklanguage_names[] = { + { 0x00, "Not defined" }, + { 0x01, "AWL" }, + { 0x02, "KOP" }, + { 0x03, "FUP" }, + { 0x04, "SCL" }, + { 0x05, "DB" }, + { 0x06, "GRAPH" }, + { 0x07, "SDB" }, + { 0x08, "CPU-DB" }, /* DB was created from Plc programm (CREAT_DB) */ + { 0x11, "SDB (after overall reset)" }, /* another SDB, don't know what it means, in SDB 1 and SDB 2, uncertain*/ + { 0x12, "SDB (Routing)" }, /* another SDB, in SDB 999 and SDB 1000 (routing information), uncertain */ + { 0x29, "ENCRYPT" }, /* block is encrypted with S7-Block-Privacy */ + { 0, NULL } +}; + +/************************************************************************** + * Names of types in userdata parameter part + */ + +static const value_string userdata_type_names[] = { + { S7COMM_UD_TYPE_PUSH, "Push" }, /* this type occurs when 2 telegrams follow after another from the same partner, or initiated from PLC */ + { S7COMM_UD_TYPE_REQ, "Request" }, + { S7COMM_UD_TYPE_RES, "Response" }, + { 0, NULL } +}; + +/************************************************************************** + * Userdata Parameter, last data unit + */ +#define S7COMM_UD_LASTDATAUNIT_YES 0x00 +#define S7COMM_UD_LASTDATAUNIT_NO 0x01 + +static const value_string userdata_lastdataunit_names[] = { + { S7COMM_UD_LASTDATAUNIT_YES, "Yes" }, + { S7COMM_UD_LASTDATAUNIT_NO, "No" }, + { 0, NULL } +}; + +/************************************************************************** + * Names of Function groups in userdata parameter part + */ +#define S7COMM_UD_FUNCGROUP_PROG 0x1 +#define S7COMM_UD_FUNCGROUP_CYCLIC 0x2 +#define S7COMM_UD_FUNCGROUP_BLOCK 0x3 +#define S7COMM_UD_FUNCGROUP_CPU 0x4 +#define S7COMM_UD_FUNCGROUP_SEC 0x5 /* Security funnctions e.g. plc password */ +#define S7COMM_UD_FUNCGROUP_TIME 0x7 + +static const value_string userdata_functiongroup_names[] = { + { S7COMM_UD_FUNCGROUP_PROG, "Programmer commands" }, + { S7COMM_UD_FUNCGROUP_CYCLIC, "Cyclic data" }, /* to read data from plc without a request */ + { S7COMM_UD_FUNCGROUP_BLOCK, "Block functions" }, + { S7COMM_UD_FUNCGROUP_CPU, "CPU functions" }, + { S7COMM_UD_FUNCGROUP_SEC, "Security" }, + { S7COMM_UD_FUNCGROUP_TIME, "Time functions" }, + { 0, NULL } +}; + +/************************************************************************** + * Vartab: Typ of data in data part, first two bytes + */ +#define S7COMM_UD_SUBF_PROG_VARTAB_TYPE_REQ 0x14 +#define S7COMM_UD_SUBF_PROG_VARTAB_TYPE_RES 0x04 + +static const value_string userdata_prog_vartab_type_names[] = { + { S7COMM_UD_SUBF_PROG_VARTAB_TYPE_REQ, "Request" }, /* Request of data areas */ + { S7COMM_UD_SUBF_PROG_VARTAB_TYPE_RES, "Response" }, /* Response from plc with data */ + { 0, NULL } +}; + +/************************************************************************** + * Vartab: area of data request + * + * Low Hi + * 0=M 1=BYTE + * 1=E 2=WORD + * 2=A 3=DWORD + * 3=PEx + * 7=DB + * 54=TIMER + * 64=COUNTER + */ +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_MB 0x01 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_MW 0x02 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_MD 0x03 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_EB 0x11 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_EW 0x12 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_ED 0x13 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_AB 0x21 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_AW 0x22 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_AD 0x23 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEB 0x31 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEW 0x32 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_PED 0x33 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBB 0x71 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBW 0x72 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBD 0x73 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_T 0x54 +#define S7COMM_UD_SUBF_PROG_VARTAB_AREA_C 0x64 + +static const value_string userdata_prog_vartab_area_names[] = { + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_MB, "MB" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_MW, "MW" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_MD, "MD" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_EB, "IB" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_EW, "IW" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_ED, "ID" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_AB, "QB" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_AW, "QW" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_AD, "QD" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEB, "PIB" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEW, "PIW" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_PED, "PID" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBB, "DBB" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBW, "DBW" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBD, "DBD" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_T, "TIMER" }, + { S7COMM_UD_SUBF_PROG_VARTAB_AREA_C, "COUNTER" }, + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 1 (Programmer commands) + */ +#define S7COMM_UD_SUBF_PROG_REQDIAGDATA1 0x01 +#define S7COMM_UD_SUBF_PROG_VARTAB1 0x02 +#define S7COMM_UD_SUBF_PROG_ERASE 0x0c +#define S7COMM_UD_SUBF_PROG_READDIAGDATA 0x0e +#define S7COMM_UD_SUBF_PROG_REMOVEDIAGDATA 0x0f +#define S7COMM_UD_SUBF_PROG_FORCE 0x10 +#define S7COMM_UD_SUBF_PROG_REQDIAGDATA2 0x13 + +static const value_string userdata_prog_subfunc_names[] = { + { S7COMM_UD_SUBF_PROG_REQDIAGDATA1, "Request diag data (Type 1)" }, /* Start online block view */ + { S7COMM_UD_SUBF_PROG_VARTAB1, "VarTab" }, /* Variable table */ + { S7COMM_UD_SUBF_PROG_READDIAGDATA, "Read diag data" }, /* online block view */ + { S7COMM_UD_SUBF_PROG_REMOVEDIAGDATA, "Remove diag data" }, /* Stop online block view */ + { S7COMM_UD_SUBF_PROG_ERASE, "Erase" }, + { S7COMM_UD_SUBF_PROG_FORCE, "Forces" }, + { S7COMM_UD_SUBF_PROG_REQDIAGDATA2, "Request diag data (Type2)" }, /* Start online block view */ + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 2 (cyclic data) + */ +#define S7COMM_UD_SUBF_CYCLIC_MEM 0x01 +#define S7COMM_UD_SUBF_CYCLIC_UNSUBSCRIBE 0x04 + +static const value_string userdata_cyclic_subfunc_names[] = { + { S7COMM_UD_SUBF_CYCLIC_MEM, "Memory" }, /* read data from memory (DB/M/etc.) */ + { S7COMM_UD_SUBF_CYCLIC_UNSUBSCRIBE, "Unsubscribe" }, /* Unsubcribe (diable) cyclic data */ + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 3 (Block functions) + */ +#define S7COMM_UD_SUBF_BLOCK_LIST 0x01 +#define S7COMM_UD_SUBF_BLOCK_LISTTYPE 0x02 +#define S7COMM_UD_SUBF_BLOCK_BLOCKINFO 0x03 + +static const value_string userdata_block_subfunc_names[] = { + { S7COMM_UD_SUBF_BLOCK_LIST, "List blocks" }, + { S7COMM_UD_SUBF_BLOCK_LISTTYPE, "List blocks of type" }, + { S7COMM_UD_SUBF_BLOCK_BLOCKINFO, "Get block info" }, + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 4 (CPU functions) + */ + +static const value_string userdata_cpu_subfunc_names[] = { + { S7COMM_UD_SUBF_CPU_READSZL, "Read SZL" }, + { S7COMM_UD_SUBF_CPU_MSGS, "Message service" }, /* Header constant is also different here */ + { S7COMM_UD_SUBF_CPU_TRANSSTOP, "Transition to STOP" }, /* PLC changed state to STOP */ + { S7COMM_UD_SUBF_CPU_ALARMIND, "ALARM indication" }, /* PLC is indicating a ALARM message */ + { S7COMM_UD_SUBF_CPU_ALARMINIT, "ALARM initiate" }, /* HMI/SCADA initiating ALARM subscription */ + { S7COMM_UD_SUBF_CPU_ALARMACK1, "ALARM ack 1" }, /* Alarm was acknowledged in HMI/SCADA */ + { S7COMM_UD_SUBF_CPU_ALARMACK2, "ALARM ack 2" }, /* Alarm was acknowledged in HMI/SCADA */ + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 5 (Security?) + */ +#define S7COMM_UD_SUBF_SEC_PASSWD 0x01 + +static const value_string userdata_sec_subfunc_names[] = { + { S7COMM_UD_SUBF_SEC_PASSWD, "PLC password" }, + { 0, NULL } +}; + +/************************************************************************** + * Names of userdata subfunctions in group 7 (Time functions) + */ +#define S7COMM_UD_SUBF_TIME_READ 0x01 +#define S7COMM_UD_SUBF_TIME_SET 0x02 +#define S7COMM_UD_SUBF_TIME_READF 0x03 +#define S7COMM_UD_SUBF_TIME_SET2 0x04 + +static const value_string userdata_time_subfunc_names[] = { + { S7COMM_UD_SUBF_TIME_READ, "Read clock" }, + { S7COMM_UD_SUBF_TIME_SET, "Set clock" }, + { S7COMM_UD_SUBF_TIME_READF, "Read clock (following)" }, + { S7COMM_UD_SUBF_TIME_SET2, "Set clock" }, + { 0, NULL } +}; + +/******************************************************************************************************* + * Weekday names in DATE_AND_TIME + */ +static const value_string weekdaynames[] = { + { 0, "Undefined" }, + { 1, "Sunday" }, + { 2, "Monday" }, + { 3, "Tuesday" }, + { 4, "Wednesday" }, + { 5, "Thursday" }, + { 6, "Friday" }, + { 7, "Saturday" }, + { 0, NULL } +}; + +/************************************************************************** + **************************************************************************/ + +/************************************************************************** + * Flags for LID access + */ +#define S7COMM_TIA1200_VAR_ENCAPS_LID 0x2 +#define S7COMM_TIA1200_VAR_ENCAPS_IDX 0x3 +#define S7COMM_TIA1200_VAR_OBTAIN_LID 0x4 +#define S7COMM_TIA1200_VAR_OBTAIN_IDX 0x5 +#define S7COMM_TIA1200_VAR_PART_START 0x6 +#define S7COMM_TIA1200_VAR_PART_LEN 0x7 + +static const value_string tia1200_var_lid_flag_names[] = { + { S7COMM_TIA1200_VAR_ENCAPS_LID, "Encapsulated LID" }, + { S7COMM_TIA1200_VAR_ENCAPS_IDX, "Encapsulated Index" }, + { S7COMM_TIA1200_VAR_OBTAIN_LID, "Obtain by LID" }, + { S7COMM_TIA1200_VAR_OBTAIN_IDX, "Obtain by Index" }, + { S7COMM_TIA1200_VAR_PART_START, "Part Start Address" }, + { S7COMM_TIA1200_VAR_PART_LEN, "Part Length" }, + { 0, NULL } +}; + +/************************************************************************** + * TIA 1200 Area Names for variable access + */ +#define S7COMM_TIA1200_VAR_ITEM_AREA1_DB 0x8a0e /* Reading DB, 2 byte DB-Number following */ +#define S7COMM_TIA1200_VAR_ITEM_AREA1_IQMCT 0x0000 /* Reading I/Q/M/C/T, 2 Byte detail area following */ + +static const value_string tia1200_var_item_area1_names[] = { + { S7COMM_TIA1200_VAR_ITEM_AREA1_DB, "DB" }, + { S7COMM_TIA1200_VAR_ITEM_AREA1_IQMCT, "IQMCT" }, + { 0, NULL } +}; + +#define S7COMM_TIA1200_VAR_ITEM_AREA2_I 0x50 +#define S7COMM_TIA1200_VAR_ITEM_AREA2_Q 0x51 +#define S7COMM_TIA1200_VAR_ITEM_AREA2_M 0x52 +#define S7COMM_TIA1200_VAR_ITEM_AREA2_C 0x53 +#define S7COMM_TIA1200_VAR_ITEM_AREA2_T 0x54 + +static const value_string tia1200_var_item_area2_names[] = { + { S7COMM_TIA1200_VAR_ITEM_AREA2_I, "Inputs (I)" }, + { S7COMM_TIA1200_VAR_ITEM_AREA2_Q, "Outputs (Q)" }, + { S7COMM_TIA1200_VAR_ITEM_AREA2_M, "Flags (M)" }, + { S7COMM_TIA1200_VAR_ITEM_AREA2_C, "Counter (C)" }, + { S7COMM_TIA1200_VAR_ITEM_AREA2_T, "Timer (T)" }, + { 0, NULL } +}; + +static gint hf_s7comm_tia1200_item_reserved1 = -1; /* 1 Byte Reserved (always 0xff?) */ +static gint hf_s7comm_tia1200_item_area1 = -1; /* 2 Byte2 Root area (DB or IQMCT) */ +static gint hf_s7comm_tia1200_item_area2 = -1; /* 2 Bytes detail area (I/Q/M/C/T) */ +static gint hf_s7comm_tia1200_item_area2unknown = -1; /* 2 Bytes detail area for possible unknown or not seen areas */ +static gint hf_s7comm_tia1200_item_dbnumber = -1; /* 2 Bytes DB number */ +static gint hf_s7comm_tia1200_item_crc = -1; /* 4 Bytes CRC */ + +static gint hf_s7comm_tia1200_substructure_item = -1; /* Substructure */ +static gint hf_s7comm_tia1200_var_lid_flags = -1; /* LID Flags */ +static gint hf_s7comm_tia1200_item_value = -1; + +/************************************************************************** + **************************************************************************/ + +/* Header Block */ +static gint hf_s7comm_header = -1; +static gint hf_s7comm_header_protid = -1; /* Header Byte 0 */ +static gint hf_s7comm_header_rosctr = -1; /* Header Bytes 1 */ +static gint hf_s7comm_header_redid = -1; /* Header Bytes 2, 3 */ +static gint hf_s7comm_header_pduref = -1; /* Header Bytes 4, 5 */ +static gint hf_s7comm_header_parlg = -1; /* Header Bytes 6, 7 */ +static gint hf_s7comm_header_datlg = -1; /* Header Bytes 8, 9 */ +static gint hf_s7comm_header_errcls = -1; /* Header Byte 10, only available at type 2 or 3 */ +static gint hf_s7comm_header_errcod = -1; /* Header Byte 11, only available at type 2 or 3 */ +/* Parameter Block */ +static gint hf_s7comm_param = -1; +static gint hf_s7comm_param_errcod = -1; /* Parameter part: Error code */ +static gint hf_s7comm_param_service = -1; /* Parameter part: service */ +static gint hf_s7comm_param_itemcount = -1; /* Parameter part: item count */ +static gint hf_s7comm_param_data = -1; /* Parameter part: data */ +static gint hf_s7comm_param_neg_pdu_length = -1; /* Parameter part: Negotiate PDU length */ +static gint hf_s7comm_param_setup_reserved1 = -1; /* Parameter part: Reserved byte in communication setup pdu*/ + +static gint hf_s7comm_param_maxamq_calling = -1; /* Parameter part: Max AmQ calling */ +static gint hf_s7comm_param_maxamq_called = -1; /* Parameter part: Max AmQ called */ + +/* Item data */ +static gint hf_s7comm_param_item = -1; +static gint hf_s7comm_param_subitem = -1; /* Substructure */ +static gint hf_s7comm_item_varspec = -1; /* Variable specification */ +static gint hf_s7comm_item_varspec_length = -1; /* Length of following address specification */ +static gint hf_s7comm_item_syntax_id = -1; /* Syntax Id */ +static gint hf_s7comm_item_transport_size = -1; /* Transport size, 1 Byte*/ +static gint hf_s7comm_item_length = -1; /* length, 2 Bytes*/ +static gint hf_s7comm_item_db = -1; /* DB/M/E/A, 2 Bytes */ +static gint hf_s7comm_item_area = -1; /* Area code, 1 byte */ +static gint hf_s7comm_item_address = -1; /* Bit adress, 3 Bytes */ +/* Special variable read with Syntax-Id 0xb0 (DBREAD) */ +static gint hf_s7comm_item_dbread_numareas = -1; /* Number of areas following, 1 Byte*/ +static gint hf_s7comm_item_dbread_length = -1; /* length, 1 Byte*/ +static gint hf_s7comm_item_dbread_db = -1; /* DB number, 2 Bytes*/ +static gint hf_s7comm_item_dbread_startadr = -1; /* Start address, 2 Bytes*/ + +static gint hf_s7comm_data = -1; +static gint hf_s7comm_data_returncode = -1; /* return code, 1 byte */ +static gint hf_s7comm_data_transport_size = -1; /* transport size 1 byte */ +static gint hf_s7comm_data_length = -1; /* Length of data, 2 Bytes */ + +static gint hf_s7comm_data_item = -1; + +static gint hf_s7comm_readresponse_data = -1; +static gint hf_s7comm_data_fillbyte = -1; + +/* timefunction: s7 timestamp */ +static gint hf_s7comm_data_ts = -1; +static gint hf_s7comm_data_ts_reserved = -1; +static gint hf_s7comm_data_ts_year1 = -1; /* first byte of BCD coded year, should be ignored */ +static gint hf_s7comm_data_ts_year2 = -1; /* second byte of BCD coded year, if 00...89 then it's 2000...2089, else 1990...1999*/ +static gint hf_s7comm_data_ts_month = -1; +static gint hf_s7comm_data_ts_day = -1; +static gint hf_s7comm_data_ts_hour = -1; +static gint hf_s7comm_data_ts_minute = -1; +static gint hf_s7comm_data_ts_second = -1; +static gint hf_s7comm_data_ts_millisecond = -1; +static gint hf_s7comm_data_ts_weekday = -1; + +/* userdata, block services */ +static gint hf_s7comm_userdata_data = -1; + +static gint hf_s7comm_userdata_param_head = -1; +static gint hf_s7comm_userdata_param_len = -1; +static gint hf_s7comm_userdata_param_reqres2 = -1; /* unknown */ +static gint hf_s7comm_userdata_param_type = -1; +static gint hf_s7comm_userdata_param_funcgroup = -1; +static gint hf_s7comm_userdata_param_subfunc_prog = -1; +static gint hf_s7comm_userdata_param_subfunc_cyclic = -1; +static gint hf_s7comm_userdata_param_subfunc_block = -1; +static gint hf_s7comm_userdata_param_subfunc_cpu = -1; +static gint hf_s7comm_userdata_param_subfunc_sec = -1; +static gint hf_s7comm_userdata_param_subfunc_time = -1; +static gint hf_s7comm_userdata_param_subfunc = -1; /* for all other subfunctions */ +static gint hf_s7comm_userdata_param_seq_num = -1; +static gint hf_s7comm_userdata_param_dataunitref = -1; +static gint hf_s7comm_userdata_param_dataunit = -1; + +/* block functions, list blocks of type */ +static gint hf_s7comm_ud_blockinfo_block_type = -1; /* Block type, 1 byte, stringlist blocktype_names */ +static gint hf_s7comm_ud_blockinfo_block_num = -1; /* Block number, 2 bytes as int */ +static gint hf_s7comm_ud_blockinfo_block_cnt = -1; /* Count, 2 bytes as int */ +static gint hf_s7comm_ud_blockinfo_block_flags = -1; /* Block flags (unknown), 1 byte */ +static gint hf_s7comm_ud_blockinfo_block_lang = -1; /* Block language, 1 byte, stringlist blocklanguage_names */ +/* block functions, get block infos */ +static gint hf_s7comm_ud_blockinfo_block_num_ascii = -1; /* Block number, 5 bytes, ASCII*/ +static gint hf_s7comm_ud_blockinfo_filesys = -1; /* Filesystem, 1 byte, ASCII*/ +static gint hf_s7comm_ud_blockinfo_res_const1 = -1; /* Constant 1, 1 byte, HEX*/ +static gint hf_s7comm_ud_blockinfo_res_infolength = -1; /* Length of Info, 2 bytes as int */ +static gint hf_s7comm_ud_blockinfo_res_unknown2 = -1; /* Unknown blockinfo 2, 2 bytes, HEX*/ +static gint hf_s7comm_ud_blockinfo_res_const3 = -1; /* Constant 3, 2 bytes, ASCII */ +static gint hf_s7comm_ud_blockinfo_res_unknown = -1; /* Unknown byte(s) */ +static gint hf_s7comm_ud_blockinfo_subblk_type = -1; /* Subblk type, 1 byte, stringlist subblktype_names */ +static gint hf_s7comm_ud_blockinfo_load_mem_len = -1; /* Length load memory, 4 bytes, int */ +static gint hf_s7comm_ud_blockinfo_blocksecurity = -1; /* Block Security, 4 bytes, stringlist blocksecurity_names*/ +static gint hf_s7comm_ud_blockinfo_interface_timestamp = -1;/* Interface Timestamp, string */ +static gint hf_s7comm_ud_blockinfo_code_timestamp = -1; /* Code Timestamp, string */ +static gint hf_s7comm_ud_blockinfo_ssb_len = -1; /* SSB length, 2 bytes, int */ +static gint hf_s7comm_ud_blockinfo_add_len = -1; /* ADD length, 2 bytes, int */ +static gint hf_s7comm_ud_blockinfo_localdata_len = -1; /* Length localdata, 2 bytes, int */ +static gint hf_s7comm_ud_blockinfo_mc7_len = -1; /* Length MC7 code, 2 bytes, int */ +static gint hf_s7comm_ud_blockinfo_author = -1; /* Author, 8 bytes, ASCII */ +static gint hf_s7comm_ud_blockinfo_family = -1; /* Family, 8 bytes, ASCII */ +static gint hf_s7comm_ud_blockinfo_headername = -1; /* Name (Header), 8 bytes, ASCII */ +static gint hf_s7comm_ud_blockinfo_headerversion = -1; /* Version (Header), 8 bytes, ASCII */ +static gint hf_s7comm_ud_blockinfo_checksum = -1; /* Block checksum, 2 bytes, HEX */ +static gint hf_s7comm_ud_blockinfo_reserved1 = -1; /* Reserved 1, 4 bytes, HEX */ +static gint hf_s7comm_ud_blockinfo_reserved2 = -1; /* Reserved 2, 4 bytes, HEX */ + +static gint hf_s7comm_userdata_blockinfo_flags = -1; /* Some flags in Block info response */ +static gint hf_s7comm_userdata_blockinfo_linked = -1; /* Some flags in Block info response */ +static gint hf_s7comm_userdata_blockinfo_standard_block = -1; +static gint hf_s7comm_userdata_blockinfo_nonretain = -1; /* Some flags in Block info response */ +static gint ett_s7comm_userdata_blockinfo_flags = -1; +static const int *s7comm_userdata_blockinfo_flags_fields[] = { + &hf_s7comm_userdata_blockinfo_linked, + &hf_s7comm_userdata_blockinfo_standard_block, + &hf_s7comm_userdata_blockinfo_nonretain, + NULL +}; + +/* Programmer commands, diagnostic data */ +static gint hf_s7comm_diagdata_req_askheadersize = -1; /* Ask header size, 2 bytes as int */ +static gint hf_s7comm_diagdata_req_asksize = -1; /* Ask size, 2 bytes as int */ +static gint hf_s7comm_diagdata_req_unknown = -1; /* for all unknown bytes */ +static gint hf_s7comm_diagdata_req_answersize = -1; /* Answer size, 2 bytes as int */ +static gint hf_s7comm_diagdata_req_block_type = -1; /* Block type, 1 byte, stringlist subblktype_names */ +static gint hf_s7comm_diagdata_req_block_num = -1; /* Block number, 2 bytes as int */ +static gint hf_s7comm_diagdata_req_startaddr_awl = -1; /* Start address AWL, 2 bytes as int */ +static gint hf_s7comm_diagdata_req_saz = -1; /* Step address counter (SAZ), 2 bytes as int */ +static gint hf_s7comm_diagdata_req_number_of_lines = -1; /* Number of lines, 1 byte as int */ +static gint hf_s7comm_diagdata_req_line_address = -1; /* Address, 2 bytes as int */ + +/* Flags for requested registers in diagnostic data telegrams */ +static gint hf_s7comm_diagdata_registerflag = -1; /* Registerflags */ +static gint hf_s7comm_diagdata_registerflag_stw = -1; /* STW = Status word */ +static gint hf_s7comm_diagdata_registerflag_accu1 = -1; /* Accumulator 1 */ +static gint hf_s7comm_diagdata_registerflag_accu2 = -1; /* Accumulator 2 */ +static gint hf_s7comm_diagdata_registerflag_ar1 = -1; /* Addressregister 1 */ +static gint hf_s7comm_diagdata_registerflag_ar2 = -1; /* Addressregister 2 */ +static gint hf_s7comm_diagdata_registerflag_db1 = -1; /* Datablock register 1 */ +static gint hf_s7comm_diagdata_registerflag_db2 = -1; /* Datablock register 2 */ +static gint ett_s7comm_diagdata_registerflag = -1; +static const int *s7comm_diagdata_registerflag_fields[] = { + &hf_s7comm_diagdata_registerflag_stw, + &hf_s7comm_diagdata_registerflag_accu1, + &hf_s7comm_diagdata_registerflag_accu2, + &hf_s7comm_diagdata_registerflag_ar1, + &hf_s7comm_diagdata_registerflag_ar2, + &hf_s7comm_diagdata_registerflag_db1, + &hf_s7comm_diagdata_registerflag_db2, + NULL +}; + +/* Function 0x28 (PLC control functions) */ +static gint hf_s7comm_data_plccontrol_part1_unknown = -1; /* Unknown bytes */ +static gint hf_s7comm_data_plccontrol_part1_len = -1; /* Length part 1 in bytes, 2 Bytes Int */ +static gint hf_s7comm_data_plccontrol_argument = -1; /* Argument, 2 Bytes as char */ +static gint hf_s7comm_data_plccontrol_block_cnt = -1; /* Number of blocks, 1 Byte as int */ +static gint hf_s7comm_data_plccontrol_part1_unknown2 = -1; /* Unknown 1 byte */ +static gint hf_s7comm_data_plccontrol_block_unknown = -1; /* Unknown 1 byte, as ASCII */ +static gint hf_s7comm_data_plccontrol_block_type = -1; /* Block type, 1 Byte, stringlist blocktype_names */ +static gint hf_s7comm_data_plccontrol_block_num = -1; /* Block number, 5 Bytes as ASCII */ +static gint hf_s7comm_data_plccontrol_dest_filesys = -1; /* Destination filesystem, 1 Byte as ASCII */ +static gint hf_s7comm_data_plccontrol_part2_len = -1; /* Length part 2 in bytes, 1 Byte as Int */ +static gint hf_s7comm_data_plccontrol_pi_service = -1; /* PI (program invocation) Service, String as ASCII */ + +/* block control functions */ +static gint hf_s7comm_data_blockcontrol_unknown1 = -1; /* for all unknown bytes in blockcontrol */ +static gint hf_s7comm_data_blockcontrol_errorcode = -1; /* Error code 2 bytes as int, 0 is no error */ +static gint hf_s7comm_data_blockcontrol_part1_len = -1; /* Length of part 1, 1 byte as int */ +static gint hf_s7comm_data_blockcontrol_file_ident = -1; /* File identifier, as ASCII */ +static gint hf_s7comm_data_blockcontrol_block_unknown = -1; /* unknown prefix before block type, ASCII */ +static gint hf_s7comm_data_blockcontrol_block_type = -1; /* Block type, 1 Byte, stringlist blocktype_names */ +static gint hf_s7comm_data_blockcontrol_block_num = -1; /* Block number, 5 Bytes, als ASCII */ +static gint hf_s7comm_data_blockcontrol_dest_filesys = -1; /* Destination filesystem, 1 Byte, ASCII */ +static gint hf_s7comm_data_blockcontrol_part2_len = -1; /* Length part 2 in bytes, 1 Byte Int */ +static gint hf_s7comm_data_blockcontrol_part2_unknown = -1; /* Unknown char, ASCII */ +static gint hf_s7comm_data_blockcontrol_loadmem_len = -1; /* Length load memory in bytes, ASCII */ +static gint hf_s7comm_data_blockcontrol_mc7code_len = -1; /* Length of MC7 code in bytes, ASCII */ + +/* Variable table */ +static gint hf_s7comm_vartab_data_type = -1; /* Type of data, 1 byte, stringlist userdata_prog_vartab_type_names */ +static gint hf_s7comm_vartab_byte_count = -1; /* Byte count, 2 bytes, int */ +static gint hf_s7comm_vartab_unknown = -1; /* Unknown byte(s), hex */ +static gint hf_s7comm_vartab_item_count = -1; /* Item count, 2 bytes, int */ +static gint hf_s7comm_vartab_req_memory_area = -1; /* Memory area, 1 byte, stringlist userdata_prog_vartab_area_names */ +static gint hf_s7comm_vartab_req_repetition_factor = -1; /* Repetition factor, 1 byte as int */ +static gint hf_s7comm_vartab_req_db_number = -1; /* DB number, 2 bytes as int */ +static gint hf_s7comm_vartab_req_startaddress = -1; /* Startaddress, 2 bytes as int */ + +/* cyclic data */ +static gint hf_s7comm_cycl_interval_timebase = -1; /* Interval timebase, 1 byte, int */ +static gint hf_s7comm_cycl_interval_time = -1; /* Interval time, 1 byte, int */ + +/* 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 */ +static gint ett_s7comm_param = -1; /* Subtree for parameter block */ +static gint ett_s7comm_param_item = -1; /* Subtree for items in parameter block */ +static gint ett_s7comm_param_subitem = -1; /* Subtree for subitems under items in parameter block */ +static gint ett_s7comm_data = -1; /* Subtree for data block */ +static gint ett_s7comm_data_item = -1; /* Subtree for an item in data block */ + +static const char mon_names[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +/******************************************************************************************************* + * + * Converts a siemens special timestamp to a string of 25+1 bytes length (e.g. "Apr 15, 2009 12:49:30.520"). + * The timestamp is 6 bytes long, one word is the number of days since 1.1.1984, and 4 bytes millisecods of the day + * + *******************************************************************************************************/ +static void +s7comm_get_timestring_from_s7time(tvbuff_t *tvb, guint offset, char *str, gint max) +{ + guint16 days; + guint32 day_msec; + struct tm *mt; + time_t t; + + day_msec = tvb_get_ntohl(tvb, offset); + days = tvb_get_ntohs(tvb, offset + 4); + + t = 441763200L; /* 1.1.1984 00:00:00 */ + t += days * (24*60*60); + t += day_msec / 1000; + mt = gmtime(&t); + str[0] = '\0'; + if (mt != NULL) { + g_snprintf(str, max, "%s %2d, %d %02d:%02d:%02d.%03d", mon_names[mt->tm_mon], mt->tm_mday, + mt->tm_year + 1900, mt->tm_hour, mt->tm_min, mt->tm_sec, day_msec % 1000); + } +} + +/******************************************************************************************************* + * + * Helper for time functions + * Get int from bcd + * + *******************************************************************************************************/ +static guint8 +s7comm_guint8_from_bcd(guint8 i) +{ + return 10 * (i /16) + (i % 16); +} + +/******************************************************************************************************* + * + * Helper for time functions + * Add a BCD coded timestamp (10 Bytes length) to tree + * + *******************************************************************************************************/ +static guint32 +s7comm_add_timestamp_to_tree(tvbuff_t *tvb, + proto_tree *tree, + guint32 offset, + gboolean append_text) +{ + guint8 time[10]; + guint8 i; + guint8 tmp; + guint8 year_org; + guint16 msec; + nstime_t tv; + proto_item *item = NULL; + proto_item *time_tree = NULL; + struct tm mt; + + /* The low nibble of byte 10 is weekday, the high nibble the LSD of msec */ + for (i = 0;i < 9; i++) { + time[i] = s7comm_guint8_from_bcd(tvb_get_guint8(tvb, offset + i)); + } + tmp = tvb_get_guint8(tvb, offset + 9) >> 4; + time[9] = s7comm_guint8_from_bcd(tmp); + + msec = (guint16)time[8] * 10 + (guint16)time[9]; + year_org = time[1]; + /* year special: ignore the first byte, since some cpus give 1914 for 2014 + * if second byte is below 89, it's 2000..2089, if over 90 it's 1990..1999 + */ + if (time[2] < 89) { + time[1] = 20; + } + /* convert time to nstime_t */ + mt.tm_year = (time[1] * 100 + time[2]) - 1900; + mt.tm_mon = time[3] - 1; + mt.tm_mday = time[4]; + mt.tm_hour = time[5]; + mt.tm_min = time[6]; + mt.tm_sec = time[7]; + mt.tm_isdst = -1; + tv.secs = mktime(&mt); + tv.nsecs = msec * 1000000; + item = proto_tree_add_time_format(tree, hf_s7comm_data_ts, tvb, offset, 10, &tv, + "S7 Timestamp: %s %2d, %d %02d:%02d:%02d.%03d", mon_names[mt.tm_mon], mt.tm_mday, + mt.tm_year + 1900, mt.tm_hour, mt.tm_min, mt.tm_sec, + msec); + time_tree = proto_item_add_subtree(item, ett_s7comm_data_item); + + /* timefunction: s7 timestamp */ + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_reserved, tvb, offset, 1, time[0]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_year1, tvb, offset, 1, year_org); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_year2, tvb, offset, 1, time[2]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_month, tvb, offset, 1, time[3]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_day, tvb, offset, 1, time[4]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_hour, tvb, offset, 1, time[5]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_minute, tvb, offset, 1, time[6]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_second, tvb, offset, 1, time[7]); + offset += 1; + proto_tree_add_uint(time_tree, hf_s7comm_data_ts_millisecond, tvb, offset, 2, msec); + proto_tree_add_item(time_tree, hf_s7comm_data_ts_weekday, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + if (append_text == TRUE) { + proto_item_append_text(tree, "(Timestamp: %s %2d, %d %02d:%02d:%02d.%03d)", mon_names[mt.tm_mon], mt.tm_mday, + mt.tm_year + 1900, mt.tm_hour, mt.tm_min, mt.tm_sec, + msec); + } + return offset; +} + +/******************************************************************************************************* + * + * Generate a comma separated string for registerflags + * + *******************************************************************************************************/ +static void +make_registerflag_string(gchar *str, guint8 flags, gint max) +{ + g_strlcpy(str, "", max); + if (flags & 0x01) g_strlcat(str, "STW, ", max); + if (flags & 0x02) g_strlcat(str, "ACCU1, ", max); + if (flags & 0x04) g_strlcat(str, "ACCU2, ", max); + if (flags & 0x08) g_strlcat(str, "AR1, ", max); + if (flags & 0x10) g_strlcat(str, "AR2, ", max); + if (flags & 0x20) g_strlcat(str, "DB1, ", max); + if (flags & 0x40) g_strlcat(str, "DB2, ", max); + if (strlen(str) > 2) + str[strlen(str) - 2 ] = '\0'; +} + +/******************************************************************************************************* + * + * Dissect the parameter details of a read/write request (Items) + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_param_item(tvbuff_t *tvb, + guint32 offset, + proto_tree *sub_tree, + guint8 item_no) +{ + guint32 a_address = 0; + guint32 bytepos = 0; + guint32 bitpos = 0; + guint8 t_size = 0; + guint16 len = 0; + guint16 db = 0; + guint16 i; + guint8 area = 0; + proto_item *item = NULL; + proto_tree *item_tree = NULL; + proto_tree *sub_item_tree = NULL; + guint8 number_of_areas = 0; + + guint8 var_spec_type = 0; + guint8 var_spec_length = 0; + guint8 var_spec_syntax_id = 0; + proto_item *sub_item = NULL; + guint16 tia_var_area1 = 0; + guint16 tia_var_area2 = 0; + guint8 tia_lid_flags = 0; + guint32 tia_value = 0; + + /* At first check type and length of variable specification */ + var_spec_type = tvb_get_guint8(tvb, offset); + var_spec_length = tvb_get_guint8(tvb, offset + 1); + var_spec_syntax_id = tvb_get_guint8(tvb, offset + 2); + + /* Classic S7: type = 0x12, len=10, syntax-id=0x10 for ANY-Pointer + * TIA S7-1200: type = 0x12, len=14, syntax-id=0xb2 (symbolic addressing??) + * Drive-ES Starter with routing: type = 0x12, len=10, syntax-id=0xa2 for ANY-Pointer + */ + + /* Insert a new tree for every item */ + item = proto_tree_add_item(sub_tree, hf_s7comm_param_item, tvb, offset, var_spec_length + 2, ENC_NA); + item_tree = proto_item_add_subtree(item, ett_s7comm_param_item); + + proto_item_append_text(item, " [%d]:", item_no + 1); + + /* Item head, constant 3 bytes */ + proto_tree_add_item(item_tree, hf_s7comm_item_varspec, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(item_tree, hf_s7comm_item_varspec_length, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(item_tree, hf_s7comm_item_syntax_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /****************************************************************************/ + /************************** Step 7 Classic 300 400 **************************/ + if (var_spec_type == 0x12 && var_spec_length == 10 && var_spec_syntax_id == S7COMM_SYNTAXID_S7ANY) { + /* Transport size, 1 byte */ + t_size = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_transport_size, tvb, offset, 1, t_size); + offset += 1; + /* Length, 2 bytes */ + len = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_length, tvb, offset, 2, len); + offset += 2; + /* DB number, 2 bytes */ + db = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_db, tvb, offset, 2, db); + offset += 2; + /* Area, 1 byte */ + area = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_area, tvb, offset, 1, area); + offset += 1; + /* Address, 3 bytes */ + a_address = tvb_get_ntoh24(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_address, tvb, offset, 3, a_address); + bytepos = a_address / 8; + bitpos = a_address % 8; + /* build a full adress to show item data directly beside the item */ + switch (area) { + case (S7COMM_AREA_P): + proto_item_append_text(item_tree, " (P"); + break; + case (S7COMM_AREA_INPUTS): + proto_item_append_text(item_tree, " (I"); + break; + case (S7COMM_AREA_OUTPUTS): + proto_item_append_text(item_tree, " (Q"); + break; + case (S7COMM_AREA_FLAGS): + proto_item_append_text(item_tree, " (M"); + break; + case (S7COMM_AREA_DB): + proto_item_append_text(item_tree, " (DB%d.DBX", db); + break; + case (S7COMM_AREA_DI): + proto_item_append_text(item_tree, " (DI%d.DIX", db); + break; + case (S7COMM_AREA_LOCAL): + proto_item_append_text(item_tree, " (L"); + break; + case (S7COMM_AREA_COUNTER): + proto_item_append_text(item_tree, " (C"); + break; + case (S7COMM_AREA_TIMER): + proto_item_append_text(item_tree, " (T"); + break; + default: + proto_item_append_text(item_tree, " (unknown area"); + break; + } + if (area == S7COMM_AREA_TIMER || area == S7COMM_AREA_COUNTER) { + proto_item_append_text(item_tree, " %d)", a_address); + } else { + proto_item_append_text(item_tree, " %d.%d %s %d)", + bytepos, bitpos, val_to_str(t_size, item_transportsizenames, "Unknown transport size: 0x%02x"), len); + } + offset += 3; + /****************************************************************************/ + /******************** S7-400 special address mode (kind of cyclic read) *****/ + /* The response to this kind of request can't be decoded, because in the response + * the data fields don't contain any header information. There is only one byte + */ + } else if (var_spec_type == 0x12 && var_spec_length >= 7 && var_spec_syntax_id == S7COMM_SYNTAXID_DBREAD) { + /* Number of data area specifications following, 1 Byte */ + number_of_areas = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_item_dbread_numareas, tvb, offset, 1, number_of_areas); + proto_item_append_text(item_tree, " (%d Data-Areas of Syntax-Id DBREAD)", number_of_areas); + offset += 1; + for (i = 1; i <= number_of_areas; i++) { + sub_item = proto_tree_add_item(item_tree, hf_s7comm_param_subitem, tvb, offset, 5, ENC_NA); + sub_item_tree = proto_item_add_subtree(sub_item, ett_s7comm_param_subitem); + /* Number of Bytes to read, 1 Byte */ + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(sub_item_tree, hf_s7comm_item_dbread_length, tvb, offset, 1, len); + offset += 1; + /* DB number, 2 Bytes */ + db = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(sub_item_tree, hf_s7comm_item_dbread_db, tvb, offset, 2, db); + offset += 2; + /* Start address, 2 Bytes */ + bytepos = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(sub_item_tree, hf_s7comm_item_dbread_startadr, tvb, offset, 2, bytepos); + offset += 2; + /* Display as pseudo S7-Any Format */ + proto_item_append_text(sub_item, " [%d]: (DB%d.DBB %d BYTE %d)", i, db, bytepos, len); + } + /****************************************************************************/ + /******************** TIA S7 1200 symbolic address mode *********************/ + } else if (var_spec_type == 0x12 && var_spec_length >= 14 && var_spec_syntax_id == S7COMM_SYNTAXID_1200SYM) { + proto_item_append_text(item_tree, " 1200 symbolic address"); + /* first byte in address seems always be 0xff */ + proto_tree_add_item(item_tree, hf_s7comm_tia1200_item_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + /* When Bytes 2/3 == 0, then Bytes 4/5 defines the area as known from classic 300/400 address mode + * when Bytes 2/3 == 0x8a0e then bytes 4/5 are containing the DB number + */ + tia_var_area1 = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(item_tree, hf_s7comm_tia1200_item_area1, tvb, offset, 2, tia_var_area1); + offset += 2; + tia_var_area2 = tvb_get_ntohs(tvb, offset); + if (tia_var_area1 == S7COMM_TIA1200_VAR_ITEM_AREA1_IQMCT) { + proto_tree_add_uint(item_tree, hf_s7comm_tia1200_item_area2, tvb, offset, 2, tia_var_area2); + proto_item_append_text(item_tree, " - Accessing %s", val_to_str(tia_var_area2, tia1200_var_item_area2_names, "Unknown IQMCT Area: 0x%04x")); + offset += 2; + } else if (tia_var_area1 == S7COMM_TIA1200_VAR_ITEM_AREA1_DB) { + proto_tree_add_uint(item_tree, hf_s7comm_tia1200_item_dbnumber, tvb, offset, 2, tia_var_area2); + proto_item_append_text(item_tree, " - Accessing DB%d", tia_var_area2); + offset += 2; + } else { + /* for current unknown areas, I don't know if there are other valid areas */ + proto_tree_add_uint(item_tree, hf_s7comm_tia1200_item_area2unknown, tvb, offset, 2, tia_var_area2); + proto_item_append_text(item_tree, " - Unknown area specification"); + offset += 2; + } + proto_tree_add_item(item_tree, hf_s7comm_tia1200_item_crc, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + + for (i = 0; i < (var_spec_length - 10) / 4; i++) { + /* Insert a new tree for every sub-struct */ + sub_item = proto_tree_add_item(item_tree, hf_s7comm_tia1200_substructure_item, tvb, offset, 4, ENC_NA); + sub_item_tree = proto_item_add_subtree(sub_item, ett_s7comm_param_subitem); + tia_lid_flags = tvb_get_guint8(tvb, offset) >> 4; + proto_tree_add_item(sub_item_tree, hf_s7comm_tia1200_var_lid_flags, tvb, offset, 1, ENC_BIG_ENDIAN); + tia_value = tvb_get_ntohl(tvb, offset) & 0x0fffffff; + proto_item_append_text(sub_item, " [%d]: %s, Value: %u", i + 1, + val_to_str(tia_lid_flags, tia1200_var_lid_flag_names, "Unknown flags: 0x%02x"), + tia_value + ); + proto_tree_add_item(sub_item_tree, hf_s7comm_tia1200_item_value, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + } + else { + /* var spec, length and syntax id are still added to tree here */ + offset += var_spec_length - 1; + proto_item_append_text(item_tree, " Unknown variable specification"); + } + return offset; +} + +/******************************************************************************************************* + * + * Decode parameter part of a PDU for setup communication + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_pdu_setup_communication(tvbuff_t *tvb, + proto_tree *tree, + guint32 offset) +{ + proto_tree_add_item(tree, hf_s7comm_param_setup_reserved1, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(tree, hf_s7comm_param_maxamq_calling, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(tree, hf_s7comm_param_maxamq_called, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(tree, hf_s7comm_param_neg_pdu_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Response -> Function Write -> Data part + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_response_write_data(tvbuff_t *tvb, + proto_tree *tree, + guint8 item_count, + guint32 offset) +{ + guint8 ret_val = 0; + guint8 i = 0; + proto_item *item = NULL; + proto_tree *item_tree = NULL; + + for (i = 1; i <= item_count; i++) { + ret_val = tvb_get_guint8(tvb, offset); + /* Insert a new tree for every item */ + item = proto_tree_add_item(tree, hf_s7comm_data_item, tvb, offset, 1, ENC_NA); + item_tree = proto_item_add_subtree(item, ett_s7comm_data_item); + proto_item_append_text(item, " [%d]: (%s)", i, val_to_str(ret_val, s7comm_item_return_valuenames, "Unknown code: 0x%02x")); + proto_tree_add_uint(item_tree, hf_s7comm_data_returncode, tvb, offset, 1, ret_val); + offset += 1; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Response -> Function Read -> Data part + * Request -> Function Write -> Data part + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_response_read_data(tvbuff_t *tvb, + proto_tree *tree, + guint8 item_count, + guint32 offset) +{ + guint8 ret_val = 0; + guint8 tsize = 0; + guint16 len = 0, len2 = 0; + guint16 head_len = 4; /* 1 byte res-code, 1 byte transp-size, 2 bytes len */ + guint8 i = 0; + proto_item *item = NULL; + proto_tree *item_tree = NULL; + + for (i = 1; i <= item_count; i++) { + ret_val = tvb_get_guint8(tvb, offset); + if (ret_val == S7COMM_ITEM_RETVAL_RESERVED || + ret_val == S7COMM_ITEM_RETVAL_DATA_OK || + ret_val == S7COMM_ITEM_RETVAL_DATA_ERR + ) { + tsize = tvb_get_guint8(tvb, offset + 1); + len = tvb_get_ntohs(tvb, offset + 2); + /* calculate length in bytes */ + if (tsize == S7COMM_DATA_TRANSPORT_SIZE_BBIT || + tsize == S7COMM_DATA_TRANSPORT_SIZE_BBYTE || + tsize == S7COMM_DATA_TRANSPORT_SIZE_BINT + ) { /* given length is in number of bits */ + if (len % 8) { /* len is not a multiple of 8, then round up to next number */ + len /= 8; + len = len + 1; + } else { + len /= 8; + } + } + + /* the PLC places extra bytes at the end of all but last result, if length is not a multiple of 2 */ + if ((len % 2) && (i < item_count)) { + len2 = len + 1; + } else { + len2 = len; + } + } + /* Insert a new tree for every item */ + item = proto_tree_add_item(tree, hf_s7comm_data_item, tvb, offset, len + head_len, ENC_NA); + item_tree = proto_item_add_subtree(item, ett_s7comm_data_item); + proto_item_append_text(item, " [%d]: (%s)", i, val_to_str(ret_val, s7comm_item_return_valuenames, "Unknown code: 0x%02x")); + + proto_tree_add_uint(item_tree, hf_s7comm_data_returncode, tvb, offset, 1, ret_val); + proto_tree_add_uint(item_tree, hf_s7comm_data_transport_size, tvb, offset + 1, 1, tsize); + proto_tree_add_uint(item_tree, hf_s7comm_data_length, tvb, offset + 2, 2, len); + offset += head_len; + + if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK || ret_val == S7COMM_ITEM_RETVAL_RESERVED) { + proto_tree_add_item(item_tree, hf_s7comm_readresponse_data, tvb, offset, len, ENC_NA); + offset += len; + if (len != len2) { + proto_tree_add_item(item_tree, hf_s7comm_data_fillbyte, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + } + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Request or Response -> Function 0x28 (PLC control functions) + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_plc_controls_param_hex28(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + guint32 offset) +{ + guint16 len; + guint8 count; + guint8 i; + guint8 *str; + + /* The first byte 0x28 is checked and inserted to tree outside, so skip it here */ + offset += 1; + + /* First part is unknown, 7 bytes */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_part1_unknown, tvb, offset, 7, ENC_NA); + offset += 7; + /* Part 1 */ + len = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(tree, hf_s7comm_data_plccontrol_part1_len, tvb, offset, 2, len); + offset += 2; + /* no block function, cold start e.g. */ + if (len == 2) { + /* C = cold start */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_argument, tvb, offset, 2, ENC_ASCII|ENC_NA); + offset +=2; + } else if (len > 2) { + count = tvb_get_guint8(tvb, offset); /* number of blocks following */ + proto_tree_add_uint(tree, hf_s7comm_data_plccontrol_block_cnt, tvb, offset, 1, count); + offset += 1; + /* Next byte reserved? is 0x00 */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_part1_unknown2, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + for (i = 0; i < count; i++) { + /* First byte of block type seems to be every time '0' as single char*/ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_block_unknown, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset +=1; + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + col_append_fstr(pinfo->cinfo, COL_INFO, " Type:[%s]", val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + offset += 1; + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_block_num, tvb, offset, 5, ENC_ASCII|ENC_NA); + str = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 5, ENC_ASCII); + col_append_fstr(pinfo->cinfo, COL_INFO, " No.:[%s]", str); + offset += 5; + /* 'P', 'B' or 'A' is following + Destination filesystem? + P = passive filesystem + A = active filesystem? + */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_dest_filesys, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + } + } + /* Part 2 */ + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(tree, hf_s7comm_data_plccontrol_part2_len, tvb, offset, 1, len); + offset += 1; + /* Function (PI_SERVICE) as string (program invocation) + * Known functions: + * _INSE = Activate a module + * _DELE = Delete a passive module + * _PROGRAM = Start/Stop the PLC + * _PLC_MEMORYRESET = Reset the PLC memory + */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_pi_service, tvb, offset, len, ENC_ASCII|ENC_NA); + offset += len; + + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Request or Response -> Function 0x29 (PLC control functions -> STOP) + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_plc_controls_param_hex29(tvbuff_t *tvb, + proto_tree *tree, + guint32 offset) +{ + guint8 len; + + /* The first byte 0x29 is checked and inserted to tree outside, so skip it here */ + offset += 1; + /* Meaning of first 5 bytes (Part 1) is unknown */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_part1_unknown, tvb, offset, 5, ENC_NA); + offset += 5; + /* Part 2 */ + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(tree, hf_s7comm_data_plccontrol_part2_len, tvb, offset, 1, len); + offset += 1; + /* Function as string */ + proto_tree_add_item(tree, hf_s7comm_data_plccontrol_pi_service, tvb, offset, len, ENC_ASCII|ENC_NA); + offset += len; + + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Request or Response -> Function 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f (block control functions) + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_plc_controls_param_hex1x(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + guint16 plength, + guint32 offset) +{ + guint8 len; + guint8 function; + guint8 *str; + + function = tvb_get_guint8(tvb, offset); + offset += 1; + + /* Meaning of first byte is unknown */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_unknown1, tvb, offset, 1, ENC_NA); + offset += 1; + /* These 2 bytes seems to be an error code. If an upload fails, this value is also shown in Manager as errorcode. Zero on success. */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_errorcode, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + /* unknown 4 bytes */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_unknown1, tvb, offset, 4, ENC_NA); + offset += 4; + if (plength <= 8) { + /* Upload or End upload functions have no other data */ + return offset; + } + + /* Part 1: Block information*/ + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(tree, hf_s7comm_data_blockcontrol_part1_len, tvb, offset, 1, len); + offset += 1; + /* Prefix + * File identifier: + * _ means: "complete module" + * $ means: "Module header for up-loading" + */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_file_ident, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + /* First byte of block type is every time '0' */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_block_unknown, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + col_append_fstr(pinfo->cinfo, COL_INFO, " Type:[%s]", val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + offset += 1; + + str = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 5, ENC_ASCII); + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_block_num, tvb, offset, 5, ENC_ASCII|ENC_NA); + col_append_fstr(pinfo->cinfo, COL_INFO, " No.:[%s]", str); + offset += 5; + /* 'P', 'B' or 'A' is following */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_dest_filesys, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + + /* Part 2, only available in "request download" */ + if (function == S7COMM_FUNCREQUESTDOWNLOAD && plength > 18) { + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(tree, hf_s7comm_data_blockcontrol_part2_len, tvb, offset, 1, len); + offset += 1; + /* first byte unknown '1' */ + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_part2_unknown, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_loadmem_len, tvb, offset, 6, ENC_ASCII|ENC_NA); + offset += 6; + proto_tree_add_item(tree, hf_s7comm_data_blockcontrol_mc7code_len, tvb, offset, 6, ENC_ASCII|ENC_NA); + offset += 6; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 1 -> Programmer commands -> Request diagnostic data (0x13 or 0x01) + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_prog_reqdiagdata(tvbuff_t *tvb, + proto_tree *data_tree, + guint8 subfunc, /* Subfunction */ + guint32 offset) /* Offset on data part +4 */ +{ + proto_item *item = NULL; + proto_tree *item_tree = NULL; + guint16 line_nr; + guint16 line_cnt; + guint16 ask_size; + guint16 item_size = 4; + guint8 registerflags; + gchar str_flags[80]; + + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_askheadersize, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + ask_size = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_diagdata_req_asksize, tvb, offset, 2, ask_size); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_unknown, tvb, offset, 6, ENC_NA); + offset += 6; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_answersize, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_unknown, tvb, offset, 13, ENC_NA); + offset += 13; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_block_num, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_startaddr_awl, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_saz, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_unknown, tvb, offset, 1, ENC_NA); + offset += 1; + if (subfunc == 0x13) { + line_cnt = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_diagdata_req_number_of_lines, tvb, offset, 2, line_cnt); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_diagdata_req_unknown, tvb, offset, 1, ENC_NA); + offset += 1; + } else { + line_cnt = (ask_size - 2) / 2; + } + proto_tree_add_bitmask(data_tree, tvb, offset, hf_s7comm_diagdata_registerflag, + ett_s7comm_diagdata_registerflag, s7comm_diagdata_registerflag_fields, ENC_BIG_ENDIAN); + offset += 1; + + if (subfunc == 0x13) { + item_size = 4; + } else { + item_size = 2; + } + for (line_nr = 0; line_nr < line_cnt; line_nr++) { + + item = proto_tree_add_item(data_tree, hf_s7comm_data_item, tvb, offset, item_size, ENC_NA); + item_tree = proto_item_add_subtree(item, ett_s7comm_data_item); + if (subfunc == 0x13) { + proto_tree_add_item(item_tree, hf_s7comm_diagdata_req_line_address, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + } + proto_tree_add_item(item_tree, hf_s7comm_diagdata_req_unknown, tvb, offset, 1, ENC_NA); + offset += 1; + + registerflags = tvb_get_guint8(tvb, offset); + make_registerflag_string(str_flags, registerflags, sizeof(str_flags)); + proto_item_append_text(item, " [%d]: (%s)", line_nr+1, str_flags); + proto_tree_add_bitmask(item_tree, tvb, offset, hf_s7comm_diagdata_registerflag, + ett_s7comm_diagdata_registerflag, s7comm_diagdata_registerflag_fields, ENC_BIG_ENDIAN); + offset += 1; + } + + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 1 -> Programmer commands -> Variable table -> request + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_prog_vartab_req_item(tvbuff_t *tvb, + guint32 offset, + proto_tree *sub_tree, + guint16 item_no) +{ + guint32 bytepos = 0; + guint16 len = 0; + guint16 db = 0; + guint8 area = 0; + proto_item *item = NULL; + + /* Insert a new tree with 6 bytes for every item */ + item = proto_tree_add_item(sub_tree, hf_s7comm_param_item, tvb, offset, 6, ENC_NA); + + sub_tree = proto_item_add_subtree(item, ett_s7comm_param_item); + + proto_item_append_text(item, " [%d]:", item_no + 1); + + /* Area, 1 byte */ + area = tvb_get_guint8(tvb, offset); + proto_tree_add_item(sub_tree, hf_s7comm_vartab_req_memory_area, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* Length (repetition factor), 1 byte */ + len = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(sub_tree, hf_s7comm_vartab_req_repetition_factor, tvb, offset, 1, len); + offset += 1; + + /* DB number, 2 bytes */ + db = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(sub_tree, hf_s7comm_vartab_req_db_number, tvb, offset, 2, db); + offset += 2; + + /* byte offset, 2 bytes */ + bytepos = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(sub_tree, hf_s7comm_vartab_req_startaddress, tvb, offset, 2, bytepos); + offset += 2; + + /* build a full adress to show item data directly beside the item */ + switch (area) { + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_MB: + proto_item_append_text(sub_tree, " (M%d.0 BYTE %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_MW: + proto_item_append_text(sub_tree, " (M%d.0 WORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_MD: + proto_item_append_text(sub_tree, " (M%d.0 DWORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_EB: + proto_item_append_text(sub_tree, " (I%d.0 BYTE %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_EW: + proto_item_append_text(sub_tree, " (I%d.0 WORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_ED: + proto_item_append_text(sub_tree, " (I%d.0 DWORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_AB: + proto_item_append_text(sub_tree, " (Q%d.0 BYTE %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_AW: + proto_item_append_text(sub_tree, " (Q%d.0 WORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_AD: + proto_item_append_text(sub_tree, " (Q%d.0 DWORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEB: + proto_item_append_text(sub_tree, " (PI%d.0 BYTE %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_PEW: + proto_item_append_text(sub_tree, " (PI%d.0 WORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_PED: + proto_item_append_text(sub_tree, " (PI%d.0 DWORD %d)", bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBB: + proto_item_append_text(sub_tree, " (DB%d.DX%d.0 BYTE %d)", db, bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBW: + proto_item_append_text(sub_tree, " (DB%d.DX%d.0 WORD %d)", db, bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_DBD: + proto_item_append_text(sub_tree, " (DB%d.DX%d.0 DWORD %d)", db, bytepos, len); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_T: + /* it's possible to read multiple timers */ + if (len >1) + proto_item_append_text(sub_tree, " (T %d..%d)", bytepos, bytepos + len - 1); + else + proto_item_append_text(sub_tree, " (T %d)", bytepos); + break; + case S7COMM_UD_SUBF_PROG_VARTAB_AREA_C: + /* it's possible to read multiple counters */ + if (len >1) + proto_item_append_text(sub_tree, " (C %d..%d)", bytepos, bytepos + len - 1); + else + proto_item_append_text(sub_tree, " (C %d)", bytepos); + break; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 1 -> Programmer commands -> Variable table -> response + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_prog_vartab_res_item(tvbuff_t *tvb, + guint32 offset, + proto_tree *sub_tree, + guint16 item_no) +{ + guint16 len = 0, len2 = 0; + guint8 ret_val = 0; + guint8 tsize = 0; + guint8 head_len = 4; + + proto_item *item = NULL; + + ret_val = tvb_get_guint8(tvb, offset); + if (ret_val == S7COMM_ITEM_RETVAL_RESERVED || + ret_val == S7COMM_ITEM_RETVAL_DATA_OK || + ret_val == S7COMM_ITEM_RETVAL_DATA_ERR + ) { + tsize = tvb_get_guint8(tvb, offset + 1); + len = tvb_get_ntohs(tvb, offset + 2); + + if (tsize == S7COMM_DATA_TRANSPORT_SIZE_BBYTE || tsize == S7COMM_DATA_TRANSPORT_SIZE_BINT) { + len /= 8; + } + /* the PLC places extra bytes at the end if length is not a multiple of 2 */ + if (len % 2) { + len2 = len + 1; + }else { + len2 = len; + } + } + /* Insert a new tree for every item */ + item = proto_tree_add_item(sub_tree, hf_s7comm_data_item, tvb, offset, len + head_len, ENC_NA); + sub_tree = proto_item_add_subtree(item, ett_s7comm_data_item); + + proto_item_append_text(item, " [%d]: (%s)", item_no + 1, val_to_str(ret_val, s7comm_item_return_valuenames, "Unknown code: 0x%02x")); + + proto_tree_add_uint(sub_tree, hf_s7comm_data_returncode, tvb, offset, 1, ret_val); + proto_tree_add_uint(sub_tree, hf_s7comm_data_transport_size, tvb, offset + 1, 1, tsize); + proto_tree_add_uint(sub_tree, hf_s7comm_data_length, tvb, offset + 2, 2, len); + + offset += head_len; + if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK || ret_val == S7COMM_ITEM_RETVAL_RESERVED) { + proto_tree_add_item(sub_tree, hf_s7comm_readresponse_data, tvb, offset, len, ENC_NA); + offset += len; + if (len != len2) { + proto_tree_add_item(sub_tree, hf_s7comm_data_fillbyte, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 5 -> Security functions? + * + *******************************************************************************************************/ +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 */ +{ + /* 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); + offset += dlength; + + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 7 -> time functions + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_time_subfunc(tvbuff_t *tvb, + proto_tree *data_tree, + 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 */ +{ + gboolean know_data = FALSE; + + switch (subfunc) { + case S7COMM_UD_SUBF_TIME_READ: + case S7COMM_UD_SUBF_TIME_READF: + if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ + if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK) { + proto_item_append_text(data_tree, ": "); + offset = s7comm_add_timestamp_to_tree(tvb, data_tree, offset, TRUE); + } + know_data = TRUE; + } + break; + case S7COMM_UD_SUBF_TIME_SET: + case S7COMM_UD_SUBF_TIME_SET2: + if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK) { + proto_item_append_text(data_tree, ": "); + offset = s7comm_add_timestamp_to_tree(tvb, data_tree, offset, TRUE); + } + know_data = TRUE; + } + break; + default: + break; + } + + if (know_data == FALSE && dlength > 4) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + offset += dlength; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 3 -> block functions + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud_block_subfunc(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *data_tree, + guint8 type, /* Type of data (request/response) */ + 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 */ +{ + guint16 count; + guint16 i; + guint8 *pBlocknumber; + guint16 blocknumber; + guint8 blocktype; + gboolean know_data = FALSE; + proto_item *item = NULL; + proto_tree *item_tree = NULL; + char str_timestamp[30]; + char str_number[10]; + char str_version[10]; + + switch (subfunc) { + /************************************************* + * List blocks + */ + case S7COMM_UD_SUBF_BLOCK_LIST: + if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + /* 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++) { + /* 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); + offset += 1; /* skip first byte */ + proto_item_append_text(item, " [%d]: (Block type %s)", i+1, val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + proto_tree_add_item(item_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(item_tree, hf_s7comm_ud_blockinfo_block_cnt, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + } + know_data = TRUE; + } + break; + /************************************************* + * List blocks of type + */ + case S7COMM_UD_SUBF_BLOCK_LISTTYPE: + if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { + offset += 1; /* skip first byte */ + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + col_append_fstr(pinfo->cinfo, COL_INFO, " Type:[%s]", + val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + proto_item_append_text(data_tree, ": (%s)", + val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + offset += 1; + } + know_data = TRUE; + + }else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ + if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { + count = len / 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); + + proto_item_append_text(item, " [%d]: (Block number %d)", i+1, tvb_get_ntohs(tvb, offset)); + proto_tree_add_item(item_tree, hf_s7comm_ud_blockinfo_block_num, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + /* The first Byte is unknown, kind of flags? */ + proto_tree_add_item(item_tree, hf_s7comm_ud_blockinfo_block_flags, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(item_tree, hf_s7comm_ud_blockinfo_block_lang, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + } + know_data = TRUE; + } + break; + /************************************************* + * Get block infos + */ + case S7COMM_UD_SUBF_BLOCK_BLOCKINFO: + if (type == S7COMM_UD_TYPE_REQ) { /*** Request ***/ + if (tsize != S7COMM_DATA_TRANSPORT_SIZE_NULL) { + /* 8 Bytes of Data follow, 1./ 2. type, 3-7 blocknumber as ascii number */ + offset += 1; /* skip first byte */ + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_item_append_text(data_tree, ": (Block type: %s", + val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + /* Add block type and number to info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " Type:[%s]", + val_to_str(tvb_get_guint8(tvb, offset), blocktype_names, "Unknown Block type: 0x%02x")); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_num_ascii, tvb, offset, 5, ENC_ASCII|ENC_NA); + pBlocknumber = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, 5, ENC_ASCII); + col_append_fstr(pinfo->cinfo, COL_INFO, " No.:[%s]", pBlocknumber); + proto_item_append_text(data_tree, ", Number: %s)", pBlocknumber); + offset += 5; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_filesys, tvb, offset, 1, ENC_ASCII|ENC_NA); + offset += 1; + } + know_data = TRUE; + + }else if (type == S7COMM_UD_TYPE_RES) { /*** Response ***/ + /* 78 Bytes */ + if (ret_val == S7COMM_ITEM_RETVAL_DATA_OK) { + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_const1, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_type, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_infolength, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_unknown2, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_const3, tvb, offset, 2, ENC_ASCII|ENC_NA); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_unknown, tvb, offset, 1, ENC_NA); + offset += 1; + + /* Configuration flags or Bits? + * Bits: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + * Pos : 31 .. ..0 + * + * Bit : 0 -> DB Linked = true + * Bit : 5 -> DB Non Retain = true + * Standard FC/FC/DB -> 0x0101 0x0100 -> is this bit (8) in FBs for multiinstance? + * SFC: 0x0009 SFB: 0x0109 or 0x010d (e.g. SFB8, 414) + */ + + proto_tree_add_bitmask(data_tree, tvb, offset, hf_s7comm_userdata_blockinfo_flags, + ett_s7comm_userdata_blockinfo_flags, s7comm_userdata_blockinfo_flags_fields, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_block_lang, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + blocktype = tvb_get_guint8(tvb, offset); + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_subblk_type, tvb, offset, 1, ENC_BIG_ENDIAN); + /* Add block type and number to info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " Type:[%s]", + val_to_str(blocktype, subblktype_names, "Unknown Subblk type: 0x%02x")); + proto_item_append_text(data_tree, ": (Subblk type: %s", + val_to_str(blocktype, subblktype_names, "Unknown Subblk type: 0x%02x")); + offset += 1; + blocknumber = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(data_tree, hf_s7comm_ud_blockinfo_block_num, tvb, offset, 2, blocknumber); + g_snprintf(str_number, sizeof(str_number), "%05d", blocknumber); + col_append_fstr(pinfo->cinfo, COL_INFO, " No.:[%s]", str_number); + proto_item_append_text(data_tree, ", Number: %05d)", blocknumber); + offset += 2; + /* "Length Load mem" -> the length in Step7 Manager seems to be this length +6 bytes */ + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_load_mem_len, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_blocksecurity, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + s7comm_get_timestring_from_s7time(tvb, offset, str_timestamp, sizeof(str_timestamp)); + proto_tree_add_string(data_tree, hf_s7comm_ud_blockinfo_code_timestamp, tvb, offset, 6, str_timestamp); + offset += 6; + s7comm_get_timestring_from_s7time(tvb, offset, str_timestamp, sizeof(str_timestamp)); + proto_tree_add_string(data_tree, hf_s7comm_ud_blockinfo_interface_timestamp, tvb, offset, 6, str_timestamp); + offset += 6; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_ssb_len, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_add_len, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_localdata_len, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_mc7_len, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_author, tvb, offset, 8, ENC_ASCII|ENC_NA); + offset += 8; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_family, tvb, offset, 8, ENC_ASCII|ENC_NA); + offset += 8; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_headername, tvb, offset, 8, ENC_ASCII|ENC_NA); + offset += 8; + g_snprintf(str_version, sizeof(str_version), "%d.%d", ((tvb_get_guint8(tvb, offset) & 0xf0) >> 4), tvb_get_guint8(tvb, offset) & 0x0f); + proto_tree_add_string(data_tree, hf_s7comm_ud_blockinfo_headerversion, tvb, offset, 1, str_version); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_res_unknown, tvb, offset, 1, ENC_NA); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_checksum, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_reserved1, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + proto_tree_add_item(data_tree, hf_s7comm_ud_blockinfo_reserved2, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + } + know_data = TRUE; + } + break; + default: + break; + } + if (know_data == FALSE && dlength > 4) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + offset += dlength; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 2 -> cyclic data + * + *******************************************************************************************************/ +static guint32 +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 */ +{ + gboolean know_data = FALSE; + guint32 offset_old; + guint32 len_item; + guint8 item_count; + guint8 i; + + switch (subfunc) + { + case S7COMM_UD_SUBF_CYCLIC_MEM: + item_count = tvb_get_guint8(tvb, offset + 1); /* first byte reserved??? */ + proto_tree_add_uint(data_tree, hf_s7comm_param_itemcount, tvb, offset, 2, item_count); + offset += 2; + if (type == S7COMM_UD_TYPE_REQ) { /* Request to PLC to send cyclic data */ + proto_tree_add_item(data_tree, hf_s7comm_cycl_interval_timebase, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(data_tree, hf_s7comm_cycl_interval_time, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + /* parse item data */ + for (i = 0; i < item_count; i++) { + offset_old = offset; + offset = s7comm_decode_param_item(tvb, offset, data_tree, i); + /* if length is not a multiple of 2 and this is not the last item, then add a fill-byte */ + len_item = offset - offset_old; + if ((len_item % 2) && (i < item_count)) { + offset += 1; + } + } + + } else if (type == S7COMM_UD_TYPE_RES || type == S7COMM_UD_TYPE_PUSH) { /* Response from PLC with the requested data */ + /* parse item data */ + offset = s7comm_decode_response_read_data(tvb, data_tree, item_count, offset); + } + know_data = TRUE; + break; + } + + if (know_data == FALSE && dlength > 4) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + offset += dlength; + } + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: User Data -> Function group 1 -> Programmer commands + * + *******************************************************************************************************/ +static guint32 +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 */ +{ + gboolean know_data = FALSE; + + guint8 data_type; + guint16 byte_count; + guint16 item_count; + guint16 i; + + switch(subfunc) + { + case S7COMM_UD_SUBF_PROG_REQDIAGDATA1: + case S7COMM_UD_SUBF_PROG_REQDIAGDATA2: + /* start variable table or block online view */ + /* TODO: Can only handle requests/response, not the "following" telegrams because it's neccessary to correlate them + with the previous request */ + if (type != S7COMM_UD_TYPE_PUSH) { + offset = s7comm_decode_ud_prog_reqdiagdata(tvb, data_tree, subfunc, offset); + know_data = TRUE; + } + break; + + case S7COMM_UD_SUBF_PROG_VARTAB1: + /* online status in variable table */ + offset += 1; /* 1 Byte const 0, skip */ + data_type = tvb_get_guint8(tvb, offset); /* 1 Byte type: 0x14 = Request, 0x04 = Response */ + proto_tree_add_uint(data_tree, hf_s7comm_vartab_data_type, tvb, offset, 1, data_type); + offset += 1; + + byte_count = tvb_get_ntohs(tvb, offset); /* 2 Bytes: Number of bytes of item-data including item-count */ + proto_tree_add_uint(data_tree, hf_s7comm_vartab_byte_count, tvb, offset, 2, byte_count); + offset += 2; + + switch (data_type) + { + case S7COMM_UD_SUBF_PROG_VARTAB_TYPE_REQ: + /*** Request of data areas ***/ + + /* 20 Bytes unknown part */ + proto_tree_add_item(data_tree, hf_s7comm_vartab_unknown, tvb, offset, 20, ENC_NA); + offset += 20; + + item_count = tvb_get_ntohs(tvb, offset); /* 2 Bytes header: number of items following */ + proto_tree_add_uint(data_tree, hf_s7comm_vartab_item_count, tvb, offset, 2, item_count); + offset += 2; + + /* parse item data */ + for (i = 0; i < item_count; i++) { + offset = s7comm_decode_ud_prog_vartab_req_item(tvb, offset, data_tree, i); + } + know_data = TRUE; + break; + + case S7COMM_UD_SUBF_PROG_VARTAB_TYPE_RES: + /*** Response of PLC to requested data-areas ***/ + + /* 4 Bytes unknown part */ + proto_tree_add_item(data_tree, hf_s7comm_vartab_unknown, tvb, offset, 4, ENC_NA); + offset += 4; + + item_count = tvb_get_ntohs(tvb, offset); /* 2 Bytes: number of items following */ + proto_tree_add_uint(data_tree, hf_s7comm_vartab_item_count, tvb, offset, 2, item_count); + offset += 2; + + /* parse item data */ + for (i = 0; i < item_count; i++) { + offset = s7comm_decode_ud_prog_vartab_res_item(tvb, offset, data_tree, i); + } + know_data = TRUE; + break; + } + } + + if (know_data == FALSE && dlength > 4) { + proto_tree_add_item(data_tree, hf_s7comm_userdata_data, tvb, offset, dlength - 4, ENC_NA); + offset += dlength; + } + return offset; +} + +/******************************************************************************************************* + ******************************************************************************************************* + * + * PDU Type: User Data + * + ******************************************************************************************************* + *******************************************************************************************************/ +static guint32 +s7comm_decode_ud(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + guint16 plength, + guint16 dlength, + guint32 offset) +{ + proto_item *item = NULL; + proto_tree *param_tree = NULL; + proto_tree *data_tree = NULL; + + guint8 ret_val; + guint8 tsize; + guint16 len; + guint32 offset_temp; + + guint8 type; + guint8 funcgroup; + guint8 subfunc; + guint8 data_unit_ref = 0; + guint8 last_data_unit = 0; + + /* Add parameter tree */ + 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 does't fit one telegram + */ + offset_temp = offset; /* Save 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; + /* 1 byte length of following parameter (8 or 12 bytes) */ + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_len, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + /* 1 byte unknown, maybe indicating request/response */ + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_reqres2, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + /* High nibble (following/request/response) */ + type = (tvb_get_guint8(tvb, offset_temp) & 0xf0) >> 4; + funcgroup = (tvb_get_guint8(tvb, offset_temp) & 0x0f); + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_type, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + + col_append_fstr(pinfo->cinfo, COL_INFO, " Function:[%s] -> [%s]", + val_to_str(type, userdata_type_names, "Unknown type: 0x%02x"), + val_to_str(funcgroup, userdata_functiongroup_names, "Unknown function: 0x%02x") + ); + + proto_item_append_text(param_tree, ": (%s)", val_to_str(type, userdata_type_names, "Unknown type: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(funcgroup, userdata_functiongroup_names, "Unknown function: 0x%02x")); + + /* Low nibble function group */ + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_funcgroup, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + /* 1 Byte subfunction */ + subfunc = tvb_get_guint8(tvb, offset_temp); + switch (funcgroup){ + case S7COMM_UD_FUNCGROUP_PROG: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_prog, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_prog_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_prog_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + case S7COMM_UD_FUNCGROUP_CYCLIC: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_cyclic, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_cyclic_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_cyclic_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + case S7COMM_UD_FUNCGROUP_BLOCK: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_block, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_block_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_block_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + case S7COMM_UD_FUNCGROUP_CPU: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_cpu, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_cpu_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_cpu_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + case S7COMM_UD_FUNCGROUP_SEC: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_sec, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_sec_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_sec_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + case S7COMM_UD_FUNCGROUP_TIME: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc_time, tvb, offset_temp, 1, subfunc); + col_append_fstr(pinfo->cinfo, COL_INFO, " -> [%s]", + val_to_str(subfunc, userdata_time_subfunc_names, "Unknown subfunc: 0x%02x")); + proto_item_append_text(param_tree, " ->(%s)", val_to_str(subfunc, userdata_time_subfunc_names, "Unknown subfunc: 0x%02x")); + break; + default: + proto_tree_add_uint(param_tree, hf_s7comm_userdata_param_subfunc, tvb, offset_temp, 1, subfunc); + break; + } + offset_temp += 1; + /* 1 Byte sequence number */ + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_seq_num, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + if (plength >= 12) { + /* 1 Byte data unit reference. If packet is fragmented, all packets with this number belong together */ + data_unit_ref = tvb_get_guint8(tvb, offset_temp); + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_dataunitref, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + /* 1 Byte fragmented flag, if this is not the last data unit (telegram is fragmented) this is != 0 */ + last_data_unit = tvb_get_guint8(tvb, offset_temp); + proto_tree_add_item(param_tree, hf_s7comm_userdata_param_dataunit, tvb, offset_temp, 1, ENC_BIG_ENDIAN); + offset_temp += 1; + proto_tree_add_item(param_tree, hf_s7comm_param_errcod, tvb, offset_temp, 2, ENC_BIG_ENDIAN); + } + + /********************************** + * Add data tree + */ + offset += plength; /* set offset to the beginning of the data part */ + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + + /* the first 4 bytes of the data part of a userdata telegram are the same for all types */ + if (dlength >= 4) { + 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, 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: + if (subfunc == 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); + } else { + /* 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; + case S7COMM_UD_FUNCGROUP_SEC: + offset = s7comm_decode_ud_security_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; + default: + break; + } + } + } + + return offset; +} + +/******************************************************************************************************* + * + * PDU Type: Request or Response + * + *******************************************************************************************************/ +static guint32 +s7comm_decode_req_resp(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + guint16 plength, + guint16 dlength, + guint32 offset, + guint8 rosctr) +{ + proto_item *item = NULL; + proto_tree *param_tree = NULL; + proto_tree *data_tree = NULL; + guint8 function = 0; + guint8 item_count = 0; + guint8 i; + guint32 offset_old; + guint32 len; + + if (plength > 0) { + /* Add parameter tree */ + item = proto_tree_add_item(tree, hf_s7comm_param, tvb, offset, plength, ENC_NA); + param_tree = proto_item_add_subtree(item, ett_s7comm_param); + /* Analyze function */ + function = tvb_get_guint8(tvb, offset); + /* add param.function to info column */ + col_append_fstr(pinfo->cinfo, COL_INFO, " Function:[%s]", val_to_str(function, param_functionnames, "Unknown function: 0x%02x")); + proto_tree_add_uint(param_tree, hf_s7comm_param_service, tvb, offset, 1, function); + /* show param.function code at the tree */ + proto_item_append_text(param_tree, ": (%s)", val_to_str(function, param_functionnames, "Unknown function: 0x%02x")); + offset += 1; + + if (rosctr == S7COMM_ROSCTR_JOB) { + switch (function){ + case S7COMM_SERV_READVAR: + case S7COMM_SERV_WRITEVAR: + item_count = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(param_tree, hf_s7comm_param_itemcount, tvb, offset, 1, item_count); + offset += 1; + /* parse item data */ + for (i = 0; i < item_count; i++) { + offset_old = offset; + offset = s7comm_decode_param_item(tvb, offset, param_tree, i); + /* if length is not a multiple of 2 and this is not the last item, then add a fill-byte */ + len = offset - offset_old; + if ((len % 2) && (i < item_count)) { + offset += 1; + } + } + /* in write-function there is a data part */ + if ((function == S7COMM_SERV_WRITEVAR) && (dlength > 0)) { + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + /* Add returned data to data-tree */ + offset = s7comm_decode_response_read_data(tvb, data_tree, item_count, offset); + } + break; + case S7COMM_SERV_SETUPCOMM: + offset = s7comm_decode_pdu_setup_communication(tvb, param_tree, offset); + break; + /* Special functions */ + case S7COMM_FUNCREQUESTDOWNLOAD: + case S7COMM_FUNCDOWNLOADBLOCK: + case S7COMM_FUNCDOWNLOADENDED: + case S7COMM_FUNCSTARTUPLOAD: + case S7COMM_FUNCUPLOAD: + case S7COMM_FUNCENDUPLOAD: + offset = s7comm_decode_plc_controls_param_hex1x(tvb, pinfo, param_tree, plength, offset -1); + break; + case S7COMM_FUNC_PLC_CONTROL: + offset = s7comm_decode_plc_controls_param_hex28(tvb, pinfo, param_tree, offset -1); + break; + case S7COMM_FUNC_PLC_STOP: + offset = s7comm_decode_plc_controls_param_hex29(tvb, param_tree, offset -1); + break; + + default: + /* Print unknown part as raw bytes */ + if (plength > 1) { + proto_tree_add_item(param_tree, hf_s7comm_param_data, tvb, offset, plength - 1, ENC_NA); + } + offset += plength - 1; /* 1 byte function code */ + if (dlength > 0) { + /* Add data tree + * First 2 bytes in data seem to be a length indicator of (dlength -4 ), so next 2 bytes + * seem to indicate something else. But I'm not sure, so leave it as it is..... + */ + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + proto_tree_add_item(data_tree, hf_s7comm_readresponse_data, tvb, offset, dlength, ENC_NA); + offset += dlength; + } + break; + } + } else if (rosctr == S7COMM_ROSCTR_ACK_DATA) { + switch (function){ + case S7COMM_SERV_READVAR: + case S7COMM_SERV_WRITEVAR: + /* This is a read-response, so the requested data may follow when address in request was ok */ + item_count = tvb_get_guint8(tvb, offset); + proto_tree_add_uint(param_tree, hf_s7comm_param_itemcount, tvb, offset, 1, item_count); + offset += 1; + /* Add data tree */ + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + /* Add returned data to data-tree */ + if ((function == S7COMM_SERV_READVAR) && (dlength > 0)) { + offset = s7comm_decode_response_read_data(tvb, data_tree, item_count, offset); + } else if ((function == S7COMM_SERV_WRITEVAR) && (dlength > 0)) { + offset = s7comm_decode_response_write_data(tvb, data_tree, item_count, offset); + } + break; + case S7COMM_SERV_SETUPCOMM: + offset = s7comm_decode_pdu_setup_communication(tvb, param_tree, offset); + break; + default: + /* Print unknown part as raw bytes */ + if (plength > 1) { + proto_tree_add_item(param_tree, hf_s7comm_param_data, tvb, offset, plength - 1, ENC_NA); + } + offset += plength - 1; /* 1 byte function code */ + if (dlength > 0) { + /* Add data tree + * First 2 bytes in data seem to be a length indicator of (dlength -4 ), so next 2 bytes + * seem to indicate something else. But I'm not sure, so leave it as it is..... + */ + item = proto_tree_add_item(tree, hf_s7comm_data, tvb, offset, dlength, ENC_NA); + data_tree = proto_item_add_subtree(item, ett_s7comm_data); + proto_tree_add_item(data_tree, hf_s7comm_readresponse_data, tvb, offset, dlength, ENC_NA); + offset += dlength; + } + break; + } + } + } + return offset; +} + +/******************************************************************************************************* + ******************************************************************************************************* + * + * S7-Protocol (main tree) + * + ******************************************************************************************************* + *******************************************************************************************************/ +static gboolean +dissect_s7comm(tvbuff_t *tvb, + packet_info *pinfo, + proto_tree *tree, + void *data _U_) +{ + proto_item *s7comm_item = NULL; + proto_item *s7comm_sub_item = NULL; + proto_tree *s7comm_tree = NULL; + proto_tree *s7comm_header_tree = NULL; + + guint32 offset = 0; + + guint8 rosctr = 0; + guint8 hlength = 10; /* Header 10 Bytes, when type 2 or 3 (Response) -> 12 Bytes */ + guint16 plength = 0; + guint16 dlength = 0; + + /*----------------- Heuristic Checks - Begin */ + /* 1) check for minimum length */ + if(tvb_captured_length(tvb) < S7COMM_MIN_TELEGRAM_LENGTH) + return FALSE; + /* 2) first byte must be 0x32 */ + if (tvb_get_guint8(tvb, 0) != S7COMM_PROT_ID) + return FALSE; + /* 3) second byte is a type field and only can contain values between 0x01-0x07 (1/2/3/7) */ + if (tvb_get_guint8(tvb, 1) < 0x01 || tvb_get_guint8(tvb, 1) > 0x07) + return FALSE; + /*----------------- Heuristic Checks - End */ + + col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_S7COMM); + col_clear(pinfo->cinfo, COL_INFO); + + rosctr = tvb_get_guint8(tvb, 1); /* Get the type byte */ + if (rosctr == 2 || rosctr == 3) hlength = 12; /* Header 10 Bytes, when type 2 or 3 (response) -> 12 Bytes */ + + /* display some infos in info-column of wireshark */ + col_add_fstr(pinfo->cinfo, COL_INFO, "ROSCTR:[%-8s]", val_to_str(rosctr, rosctr_names, "Unknown: 0x%02x")); + + s7comm_item = proto_tree_add_item(tree, proto_s7comm, tvb, 0, -1, ENC_NA); + s7comm_tree = proto_item_add_subtree(s7comm_item, ett_s7comm); + + /* insert header tree */ + s7comm_sub_item = proto_tree_add_item(s7comm_tree, hf_s7comm_header, + tvb, offset, hlength, ENC_NA); + + /* insert sub-items in header tree */ + s7comm_header_tree = proto_item_add_subtree(s7comm_sub_item, ett_s7comm_header); + + /* Protocol Identifier, constant 0x32 */ + proto_tree_add_item(s7comm_header_tree, hf_s7comm_header_protid, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + /* ROSCTR (Remote Operating Service Control) - PDU Type */ + proto_tree_add_uint(s7comm_header_tree, hf_s7comm_header_rosctr, tvb, offset, 1, rosctr); + /* Show pdu type beside the header tree */ + proto_item_append_text(s7comm_header_tree, ": (%s)", val_to_str(rosctr, rosctr_names, "Unknown ROSCTR: 0x%02x")); + offset += 1; + /* Redundacy ID, reserved */ + proto_tree_add_item(s7comm_header_tree, hf_s7comm_header_redid, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + /* Protocol Data Unit Reference */ + proto_tree_add_item(s7comm_header_tree, hf_s7comm_header_pduref, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + /* Parameter length */ + plength = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(s7comm_header_tree, hf_s7comm_header_parlg, tvb, offset, 2, plength); + offset += 2; + /* Data length */ + dlength = tvb_get_ntohs(tvb, offset); + proto_tree_add_uint(s7comm_header_tree, hf_s7comm_header_datlg, tvb, offset, 2, dlength); + offset += 2; + /* when type is 2 or 3 there are 2 bytes with errorclass and errorcode */ + if (hlength == 12) { + proto_tree_add_item(s7comm_header_tree, hf_s7comm_header_errcls, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(s7comm_header_tree, hf_s7comm_header_errcod, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + } + + switch (rosctr) { + case S7COMM_ROSCTR_JOB: + case S7COMM_ROSCTR_ACK_DATA: + s7comm_decode_req_resp(tvb, pinfo, s7comm_tree, plength, dlength, offset, rosctr); + break; + case S7COMM_ROSCTR_USERDATA: + s7comm_decode_ud(tvb, pinfo, s7comm_tree, plength, dlength, offset); + break; + } + /*else { Unknown pdu, maybe passed to another dissector? } + */ + return TRUE; +} + +/******************************************************************************************************* + *******************************************************************************************************/ +void +proto_register_s7comm (void) +{ + /* format: + * {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}. + */ + static hf_register_info hf[] = { + { &hf_s7comm_header, + { "Header", "s7comm.header", FT_NONE, BASE_NONE, NULL, 0x0, + "This is the header of S7 communication", HFILL }}, + { &hf_s7comm_header_protid, + { "Protocol Id", "s7comm.header.protid", FT_UINT8, BASE_HEX, NULL, 0x0, + "Protocol Identification, 0x32 for S7", HFILL }}, + { &hf_s7comm_header_rosctr, + { "ROSCTR", "s7comm.header.rosctr", FT_UINT8, BASE_DEC, VALS(rosctr_names), 0x0, + "Remote Operating Service Control", HFILL }}, + { &hf_s7comm_header_redid, + { "Redundancy Identification (Reserved)", "s7comm.header.redid", FT_UINT16, BASE_HEX, NULL, 0x0, + "Redundancy Identification (Reserved), should be always 0x0000", HFILL }}, + { &hf_s7comm_header_pduref, + { "Protocol Data Unit Reference", "s7comm.header.pduref", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_header_parlg, + { "Parameter length", "s7comm.header.parlg", FT_UINT16, BASE_DEC, NULL, 0x0, + "Specifies the entire length of the parameter block in bytes", HFILL }}, + { &hf_s7comm_header_datlg, + { "Data length", "s7comm.header.datlg", FT_UINT16, BASE_DEC, NULL, 0x0, + "Specifies the entire length of the data block in bytes", HFILL }}, + { &hf_s7comm_header_errcls, + { "Error class", "s7comm.header.errcls", FT_UINT8, BASE_HEX, VALS(errcls_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_header_errcod, + { "Error code", "s7comm.header.errcod", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_param, + { "Parameter", "s7comm.param", FT_NONE, BASE_NONE, NULL, 0x0, + "This is the parameter part of S7 communication", HFILL }}, + { &hf_s7comm_param_errcod, + { "Error code", "s7comm.param.errcod", FT_UINT16, BASE_HEX, VALS(param_errcode_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_service, + { "Function", "s7comm.param.func", FT_UINT8, BASE_HEX, VALS(param_functionnames), 0x0, + "Indicates the function of parameter/data", HFILL }}, + { &hf_s7comm_param_maxamq_calling, + { "Max AmQ (parallel jobs with ack) calling", "s7comm.param.maxamq_calling", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_maxamq_called, + { "Max AmQ (parallel jobs with ack) called", "s7comm.param.maxamq_called", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_setup_reserved1, + { "Reserved", "s7comm.param.setup_reserved1", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_neg_pdu_length, + { "PDU length", "s7comm.param.pdu_length", FT_UINT16, BASE_DEC, NULL, 0x0, + "Negotiated PDU length", HFILL }}, + { &hf_s7comm_param_itemcount, + { "Item count", "s7comm.param.itemcount", FT_UINT8, BASE_DEC, NULL, 0x0, + "Number of Items in parameter/data part", HFILL }}, + { &hf_s7comm_param_data, + { "Parameter data", "s7comm.param.data", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_item, + { "Item", "s7comm.param.item", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_param_subitem, + { "Subitem", "s7comm.param.subitem", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_varspec, + { "Variable specification", "s7comm.param.item.varspec", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_varspec_length, + { "Length of following address specification", "s7comm.param.item.varspec_length", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_syntax_id, + { "Syntax Id", "s7comm.param.item.syntaxid", FT_UINT8, BASE_HEX, VALS(item_syntaxid_names), 0x0, + "Syntax Id, format type of following address specification", HFILL }}, + { &hf_s7comm_item_transport_size, + { "Transport size", "s7comm.param.item.transp_size", FT_UINT8, BASE_DEC, VALS(item_transportsizenames), 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_length, + { "Length", "s7comm.param.item.length", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_db, + { "DB number", "s7comm.param.item.db", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_area, + { "Area", "s7comm.param.item.area", FT_UINT8, BASE_HEX, VALS(item_areanames), 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_address, + { "Address", "s7comm.param.item.address", FT_UINT24, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + /* Special variable read with Syntax-Id 0xb0 (DBREAD) */ + { &hf_s7comm_item_dbread_numareas, + { "Number of areas", "s7comm.param.item.dbread.numareas", FT_UINT8, BASE_DEC, NULL, 0x0, + "Number of area specifications following", HFILL }}, + { &hf_s7comm_item_dbread_length, + { "Bytes to read", "s7comm.param.item.dbread.length", FT_UINT8, BASE_DEC, NULL, 0x0, + "Number of bytes to read", HFILL }}, + { &hf_s7comm_item_dbread_db, + { "DB number", "s7comm.param.item.dbread.db", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_item_dbread_startadr, + { "Start address", "s7comm.param.item.dbread.startaddress", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_data, + { "Data", "s7comm.data", FT_NONE, BASE_NONE, NULL, 0x0, + "This is the data part of S7 communication", HFILL }}, + { &hf_s7comm_data_returncode, + { "Return code", "s7comm.data.returncode", FT_UINT8, BASE_HEX, VALS(s7comm_item_return_valuenames), 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_transport_size, + { "Transport size", "s7comm.data.transportsize", FT_UINT8, BASE_HEX, VALS(data_transportsizenames), 0x0, + "Data type / Transport size. If 3, 4 or 5 the following length gives the number of bits, otherwise the number of bytes.", HFILL }}, + { &hf_s7comm_data_length, + { "Length", "s7comm.data.length", FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of data", HFILL }}, + + { &hf_s7comm_data_item, + { "Item", "s7comm.data.item", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_readresponse_data, + { "Data", "s7comm.resp.data", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_fillbyte, + { "Fill byte", "s7comm.data.fillbyte", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_userdata_data, + { "Data", "s7comm.data.userdata", FT_BYTES, BASE_NONE, NULL, 0x0, + "Userdata data", HFILL }}, + + /* Userdata parameter 8/12 Bytes len*/ + { &hf_s7comm_userdata_param_head, + { "Parameter head", "s7comm.param.userdata.head", FT_UINT24, BASE_HEX, NULL, 0x0, + "Header before parameter (constant 0x000112)", HFILL }}, + { &hf_s7comm_userdata_param_len, + { "Parameter length", "s7comm.param.userdata.length", FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of following parameter data (without head)", HFILL }}, + { &hf_s7comm_userdata_param_reqres2, + { "Unknown (Request/Response)", "s7comm.param.userdata.reqres1", FT_UINT8, BASE_HEX, NULL, 0x0, + "Unknown part, possible request/response (0x11, 0x12), but not in programmer commands", HFILL }}, + + { &hf_s7comm_userdata_param_type, + { "Type", "s7comm.param.userdata.type", FT_UINT8, BASE_DEC, VALS(userdata_type_names), 0xf0, + "Type of parameter", HFILL }}, + + { &hf_s7comm_userdata_param_funcgroup, + { "Function group", "s7comm.param.userdata.funcgroup", FT_UINT8, BASE_DEC, VALS(userdata_functiongroup_names), 0x0f, + NULL, HFILL }}, + + { &hf_s7comm_userdata_param_subfunc_prog, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_prog_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc_cyclic, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_cyclic_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc_block, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_block_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc_cpu, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_cpu_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc_sec, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_sec_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc_time, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_DEC, VALS(userdata_time_subfunc_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_userdata_param_subfunc, + { "Subfunction", "s7comm.param.userdata.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_userdata_param_seq_num, + { "Sequence number", "s7comm.param.userdata.seq_num", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + { &hf_s7comm_userdata_param_dataunitref, + { "Data unit reference number", "s7comm.param.userdata.dataunitref", FT_UINT8, BASE_DEC, NULL, 0x0, + "Data unit reference number if PDU is fragmented", HFILL }}, + + { &hf_s7comm_userdata_param_dataunit, + { "Last data unit", "s7comm.param.userdata.lastdataunit", FT_UINT8, BASE_HEX, VALS(userdata_lastdataunit_names), 0x0, + NULL, HFILL }}, + + /* block functions / info */ + { &hf_s7comm_ud_blockinfo_block_type, + { "Block type", "s7comm.blockinfo.blocktype", FT_UINT8, BASE_DEC, VALS(blocktype_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_block_cnt, + { "Block count", "s7comm.blockinfo.block_count", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_block_num, + { "Block number", "s7comm.blockinfo.block_num", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_block_flags, + { "Block flags (unknown)", "s7comm.blockinfo.flags", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_block_lang, + { "Block language", "s7comm.blockinfo.block_lang", FT_UINT8, BASE_DEC, VALS(blocklanguage_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_block_num_ascii, + { "Block number", "s7comm.data.blockinfo.block_number", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_filesys, + { "Filesystem", "s7comm.data.blockinfo.filesys", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_res_const1, + { "Constant 1", "s7comm.blockinfo.res_const1", FT_UINT8, BASE_HEX, NULL, 0x0, + "Possible constant 1", HFILL }}, + { &hf_s7comm_ud_blockinfo_res_infolength, + { "Length of Info", "s7comm.blockinfo.res_infolength", FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of Info in bytes", HFILL }}, + { &hf_s7comm_ud_blockinfo_res_unknown2, + { "Unknown blockinfo 2", "s7comm.blockinfo.res_unknown2", FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_res_const3, + { "Constant 3", "s7comm.blockinfo.res_const3", FT_STRING, BASE_NONE, NULL, 0x0, + "Possible constant 3, seems to be always 'pp'", HFILL }}, + { &hf_s7comm_ud_blockinfo_res_unknown, + { "Unknown byte(s) blockinfo", "s7comm.blockinfo.res_unknown", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_subblk_type, + { "Subblk type", "s7comm.blockinfo.subblk_type", FT_UINT8, BASE_DEC, VALS(subblktype_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_load_mem_len, + { "Length load memory", "s7comm.blockinfo.load_mem_len", FT_UINT32, BASE_DEC, NULL, 0x0, + "Length of load memory in bytes", HFILL }}, + { &hf_s7comm_ud_blockinfo_blocksecurity, + { "Block Security", "s7comm.blockinfo.blocksecurity", FT_UINT32, BASE_DEC, VALS(blocksecurity_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_interface_timestamp, + { "Interface timestamp", "s7comm.blockinfo.interface_timestamp", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_code_timestamp, + { "Code timestamp", "s7comm.blockinfo.code_timestamp", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_ssb_len, + { "SSB length", "s7comm.blockinfo.ssb_len", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_add_len, + { "ADD length", "s7comm.blockinfo.add_len", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_localdata_len, + { "Localdata length", "s7comm.blockinfo.localdata_len", FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of localdata in bytes", HFILL }}, + { &hf_s7comm_ud_blockinfo_mc7_len, + { "MC7 code length", "s7comm.blockinfo.mc7_len", FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of MC7 code in bytes", HFILL }}, + { &hf_s7comm_ud_blockinfo_author, + { "Author", "s7comm.blockinfo.author", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_family, + { "Family", "s7comm.blockinfo.family", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_headername, + { "Name (Header)", "s7comm.blockinfo.headername", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_headerversion, + { "Version (Header)", "s7comm.blockinfo.headerversion", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_checksum, + { "Block checksum", "s7comm.blockinfo.checksum", FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_reserved1, + { "Reserved 1", "s7comm.blockinfo.reserved1", FT_UINT32, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_ud_blockinfo_reserved2, + { "Reserved 2", "s7comm.blockinfo.reserved2", FT_UINT32, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + /* Flags in blockinfo response */ + { &hf_s7comm_userdata_blockinfo_flags, + { "Block flags", "s7comm.param.userdata.blockinfo.flags", FT_UINT8, BASE_HEX, NULL, 0xff, + "Some block configuration flags", HFILL }}, + /* Bit : 0 -> DB Linked = true */ + { &hf_s7comm_userdata_blockinfo_linked, + { "Linked", "s7comm.param.userdata.blockinfo.linked", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x01, + NULL, HFILL }}, + /* Bit : 1 -> Standard block = true */ + { &hf_s7comm_userdata_blockinfo_standard_block, + { "Standard block", "s7comm.param.userdata.blockinfo.standard_block", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x02, + NULL, HFILL }}, + /* Bit : 5 -> DB Non Retain = true */ + { &hf_s7comm_userdata_blockinfo_nonretain, + { "Non Retain", "s7comm.param.userdata.blockinfo.nonretain", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x08, + NULL, HFILL }}, + + /* Programmer commands, diagnostic data */ + { &hf_s7comm_diagdata_req_askheadersize, + { "Ask header size", "s7comm.diagdata.req.askheadersize", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_asksize, + { "Ask size", "s7comm.diagdata.req.asksize", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_unknown, + { "Unknown byte(s) diagdata", "s7comm.diagdata.req.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_answersize, + { "Answer size", "s7comm.diagdata.req.answersize", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_block_type, + { "Block type", "s7comm.diagdata.req.blocktype", FT_UINT8, BASE_DEC, VALS(subblktype_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_block_num, + { "Block number", "s7comm.diagdata.req.blocknumber", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_startaddr_awl, + { "Start address AWL", "s7comm.diagdata.req.startaddr_awl", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_saz, + { "Step address counter (SAZ)", "s7comm.diagdata.req.saz", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_number_of_lines, + { "Number of lines", "s7comm.diagdata.req.number_of_lines", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_diagdata_req_line_address, + { "Address", "s7comm.diagdata.req.line_address", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* Flags for requested registers in diagnostic data telegrams */ + { &hf_s7comm_diagdata_registerflag, + { "Registers", "s7comm.diagdata.register", FT_UINT8, BASE_HEX, NULL, 0x00, + "Requested registers", HFILL }}, + { &hf_s7comm_diagdata_registerflag_stw, + { "STW", "s7comm.diagdata.register.stw", FT_BOOLEAN, 8, NULL, 0x01, + "STW / Status word", HFILL }}, + { &hf_s7comm_diagdata_registerflag_accu1, + { "ACCU1", "s7comm.diagdata.register.accu1", FT_BOOLEAN, 8, NULL, 0x02, + "ACCU1 / Accumulator 1", HFILL }}, + { &hf_s7comm_diagdata_registerflag_accu2, + { "ACCU2", "s7comm.diagdata.register.accu2", FT_BOOLEAN, 8, NULL, 0x04, + "ACCU2 / Accumulator 2", HFILL }}, + { &hf_s7comm_diagdata_registerflag_ar1, + { "AR1", "s7comm.diagdata.register.ar1", FT_BOOLEAN, 8, NULL, 0x08, + "AR1 / Addressregister 1", HFILL }}, + { &hf_s7comm_diagdata_registerflag_ar2, + { "AR2", "s7comm.diagdata.register.ar2", FT_BOOLEAN, 8, NULL, 0x10, + "AR2 / Addressregister 2", HFILL }}, + { &hf_s7comm_diagdata_registerflag_db1, + { "DB1", "s7comm.diagdata.register.db1", FT_BOOLEAN, 8, NULL, 0x20, + "DB1 (global)/ Datablock register 1", HFILL }}, + { &hf_s7comm_diagdata_registerflag_db2, + { "DB2", "s7comm.diagdata.register.db2", FT_BOOLEAN, 8, NULL, 0x40, + "DB2 (instance) / Datablock register 2", HFILL }}, + + /* timefunction: s7 timestamp */ + { &hf_s7comm_data_ts, + { "S7 Timestamp", "s7comm.data.ts", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x00, + "S7 Timestamp, BCD coded", HFILL }}, + { &hf_s7comm_data_ts_reserved, + { "S7 Timestamp - Reserved", "s7comm.data.ts_reserved", FT_UINT8, BASE_HEX, NULL, 0x00, + "S7 Timestamp: Reserved byte", HFILL }}, + { &hf_s7comm_data_ts_year1, + { "S7 Timestamp - Year 1", "s7comm.data.ts_year1", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded year thousands/hundreds, should be ignored (19 or 20)", HFILL }}, + { &hf_s7comm_data_ts_year2, + { "S7 Timestamp - Year 2", "s7comm.data.ts_year2", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded year, if 00...89 then it's 2000...2089, else 1990...1999", HFILL }}, + { &hf_s7comm_data_ts_month, + { "S7 Timestamp - Month", "s7comm.data.ts_month", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded month", HFILL }}, + { &hf_s7comm_data_ts_day, + { "S7 Timestamp - Day", "s7comm.data.ts_day", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded day", HFILL }}, + { &hf_s7comm_data_ts_hour, + { "S7 Timestamp - Hour", "s7comm.data.ts_hour", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded hour", HFILL }}, + { &hf_s7comm_data_ts_minute, + { "S7 Timestamp - Minute", "s7comm.data.ts_minute", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded minute", HFILL }}, + { &hf_s7comm_data_ts_second, + { "S7 Timestamp - Second", "s7comm.data.ts_second", FT_UINT8, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded second", HFILL }}, + { &hf_s7comm_data_ts_millisecond, + { "S7 Timestamp - Milliseconds", "s7comm.data.ts_millisecond", FT_UINT16, BASE_DEC, NULL, 0x00, + "S7 Timestamp: BCD coded milliseconds (left 3 nibbles)", HFILL }}, + { &hf_s7comm_data_ts_weekday, + { "S7 Timestamp - Weekday", "s7comm.data.ts_weekday", FT_UINT16, BASE_DEC, VALS(weekdaynames), 0x000f, + "S7 Timestamp: Weekday number (right nibble, 1=Su,2=Mo,..)", HFILL }}, + + /* Function 0x28 (PLC control functions) ans 0x29 */ + { &hf_s7comm_data_plccontrol_part1_unknown, + { "Part 1 unknown bytes", "s7comm.data.plccontrol.part1_unknown", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_part1_len, + { "Length part 1", "s7comm.data.plccontrol.part1_len", FT_UINT16, BASE_DEC, NULL, 0x0, + "Length of part 1 in bytes", HFILL }}, + { &hf_s7comm_data_plccontrol_argument, + { "Argument", "s7comm.data.plccontrol.argument", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_block_cnt, + { "Number of blocks", "s7comm.data.plccontrol.block_cnt", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_part1_unknown2, + { "Unknown byte", "s7comm.data.plccontrol.part1_unknown2", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_block_unknown, + { "Unknown char before Block type", "s7comm.data.plccontrol.block_unknown", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_block_type, + { "Block type", "s7comm.data.plccontrol.block_type", FT_UINT8, BASE_DEC, VALS(blocktype_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_block_num, + { "Block number", "s7comm.data.plccontrol.block_number", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_dest_filesys, + { "Destination filesystem", "s7comm.data.plccontrol.dest_filesys", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_plccontrol_part2_len, + { "Length part 2", "s7comm.data.plccontrol.part2_len", FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of part 2 in bytes", HFILL }}, + { &hf_s7comm_data_plccontrol_pi_service, + { "PI (program invocation) Service", "s7comm.data.plccontrol.pi_service", FT_STRING, BASE_NONE, NULL, 0x0, + "Known: _INSE = Activate a module, _DELE = Delete a passive module, _PROGRAM = Start/Stop the PLC, _PLC_MEMORYRESET = Reset the PLC memory" , HFILL }}, + + /* block control functions */ + { &hf_s7comm_data_blockcontrol_unknown1, + { "Unknown byte(s) in blockcontrol", "s7comm.data.blockcontrol.unknown1", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_errorcode, + { "Errorcode", "s7comm.data.blockcontrol.errorcode", FT_UINT16, BASE_HEX, NULL, 0x0, + "Errorcode, 0 on success", HFILL }}, + { &hf_s7comm_data_blockcontrol_part1_len, + { "Length part 1", "s7comm.data.blockcontrol.part1_len", FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of part 1 in bytes", HFILL }}, + { &hf_s7comm_data_blockcontrol_file_ident, + { "File identifier", "s7comm.data.blockcontrol.file_identifier", FT_STRING, BASE_NONE, NULL, 0x0, + "File identifier: '_'=complete module; '$'=Module header for up-loading", HFILL }}, + { &hf_s7comm_data_blockcontrol_block_unknown, + { "Unknown char before Block type", "s7comm.data.blockcontrol.block_unknown", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_block_type, + { "Block type", "s7comm.data.blockcontrol.block_type", FT_UINT8, BASE_DEC, VALS(blocktype_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_block_num, + { "Block number", "s7comm.data.blockcontrol.block_number", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_dest_filesys, + { "Destination filesystem", "s7comm.data.blockcontrol.dest_filesys", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_part2_len, + { "Length part 2", "s7comm.data.blockcontrol.part2_len", FT_UINT8, BASE_DEC, NULL, 0x0, + "Length of part 2 in bytes", HFILL }}, + { &hf_s7comm_data_blockcontrol_part2_unknown, + { "Unknown char before load mem", "s7comm.data.blockcontrol.part2_unknown", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_data_blockcontrol_loadmem_len, + { "Length of load memory", "s7comm.data.blockcontrol.loadmem_len", FT_STRING, BASE_NONE, NULL, 0x0, + "Length of load memory in bytes", HFILL }}, + { &hf_s7comm_data_blockcontrol_mc7code_len, + { "Length of MC7 code", "s7comm.data.blockcontrol.mc7code_len", FT_STRING, BASE_NONE, NULL, 0x0, + "Length of MC7 code in bytes", HFILL }}, + + /* Variable table */ + { &hf_s7comm_vartab_data_type, + { "Type of data", "s7comm.vartab.data_type", FT_UINT8, BASE_DEC, VALS(userdata_prog_vartab_type_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_byte_count, + { "Byte count", "s7comm.vartab.byte_count", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_unknown, + { "Unknown byte(s) vartab", "s7comm.vartab.unknown", FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_item_count, + { "Item count", "s7comm.vartab.item_count", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_req_memory_area, + { "Memory area", "s7comm.vartab.req.memory_area", FT_UINT8, BASE_DEC, VALS(userdata_prog_vartab_area_names), 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_req_repetition_factor, + { "Repetition factor", "s7comm.vartab.req.repetition_factor", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_vartab_req_db_number, + { "DB number", "s7comm.vartab.req.db_number", FT_UINT16, BASE_DEC, NULL, 0x0, + "DB number, when area is DB", HFILL }}, + { &hf_s7comm_vartab_req_startaddress, + { "Startaddress", "s7comm.vartab.req.startaddress", FT_UINT16, BASE_DEC, NULL, 0x0, + "Startaddress / byteoffset", HFILL }}, + + /* cyclic data */ + { &hf_s7comm_cycl_interval_timebase, + { "Interval timebase", "s7comm.cyclic.interval_timebase", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_cycl_interval_time, + { "Interval time", "s7comm.cyclic.interval_time", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + + /* TIA Portal stuff */ + { &hf_s7comm_tia1200_item_reserved1, + { "1200 sym Reserved", "s7comm.tiap.item.reserved1", FT_UINT8, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_tia1200_item_area1, + { "1200 sym root area 1", "s7comm.tiap.item.area1", FT_UINT16, BASE_HEX, VALS(tia1200_var_item_area1_names), 0x0, + "Area from where to read: DB or Inputs, Outputs, etc.", HFILL }}, + { &hf_s7comm_tia1200_item_area2, + { "1200 sym root area 2", "s7comm.tiap.item.area2", FT_UINT16, BASE_HEX, VALS(tia1200_var_item_area2_names), 0x0, + "Specifies the area from where to read", HFILL }}, + { &hf_s7comm_tia1200_item_area2unknown, + { "1200 sym root area 2 unknown", "s7comm.tiap.item.area2unknown", FT_UINT16, BASE_HEX, NULL, 0x0, + "For current unknown areas", HFILL }}, + { &hf_s7comm_tia1200_item_dbnumber, + { "1200 sym root DB number", "s7comm.tiap.item.dbnumber", FT_UINT16, BASE_DEC, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_tia1200_item_crc, + { "1200 sym CRC", "s7comm.tiap.item.crc", FT_UINT32, BASE_HEX, NULL, 0x0, + "CRC generated out of symbolic name with (x^32+x^31+x^30+x^29+x^28+x^26+x^23+x^21+x^19+x^18+x^15+x^14+x^13+x^12+x^9+x^8+x^4+x+1)", HFILL }}, + { &hf_s7comm_tia1200_var_lid_flags, + { "LID flags", "s7comm.tiap.item.lid_flags", FT_UINT8, BASE_DEC, VALS(tia1200_var_lid_flag_names), 0xf0, + NULL, HFILL }}, + { &hf_s7comm_tia1200_substructure_item, + { "Substructure", "s7comm.tiap.item.substructure", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_s7comm_tia1200_item_value, + { "Value", "s7comm.tiap.item.value", FT_UINT32, BASE_DEC, NULL, 0x0fffffff, + NULL, HFILL }}, + }; + + static gint *ett[] = { + &ett_s7comm, + &ett_s7comm_header, + &ett_s7comm_param, + &ett_s7comm_param_item, + &ett_s7comm_param_subitem, + &ett_s7comm_data, + &ett_s7comm_data_item, + &ett_s7comm_diagdata_registerflag, + &ett_s7comm_userdata_blockinfo_flags, + }; + + proto_s7comm = proto_register_protocol ( + "S7 Communication", /* name */ + "S7COMM", /* short name */ + "s7comm" /* abbrev */ + ); + + proto_register_field_array(proto_s7comm, hf, array_length (hf)); + + s7comm_register_szl_types(proto_s7comm); + + proto_register_subtree_array(ett, array_length (ett)); +} + +/* Register this protocol */ +void +proto_reg_handoff_s7comm(void) +{ + /* register ourself as an heuristic cotp (ISO 8073) payload dissector */ + heur_dissector_add("cotp", dissect_s7comm, proto_s7comm); +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ -- cgit v1.2.3