summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-cemi.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-cemi.c')
-rw-r--r--epan/dissectors/packet-cemi.c3485
1 files changed, 3485 insertions, 0 deletions
diff --git a/epan/dissectors/packet-cemi.c b/epan/dissectors/packet-cemi.c
new file mode 100644
index 00000000..2717c943
--- /dev/null
+++ b/epan/dissectors/packet-cemi.c
@@ -0,0 +1,3485 @@
+/* packet-cemi.c
+ * Routines for cEMI (Common External Message Interface) dissection
+ * By Jan Kessler <kessler@ise.de>
+ * Copyright 2004, Jan Kessler <kessler@ise.de>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <epan/packet.h>
+#include "packet-knxip.h"
+
+void proto_register_cemi(void);
+void proto_reg_handoff_cemi(void);
+
+/* cEMI Message Codes
+*/
+#define CEMI_L_BUSMON_IND 0x2B
+#define CEMI_L_RAW_IND 0x2D
+#define CEMI_L_RAW_REQ 0x10
+#define CEMI_L_RAW_CON 0x2F
+#define CEMI_L_DATA_REQ 0x11
+#define CEMI_L_DATA_CON 0x2E
+#define CEMI_L_DATA_IND 0x29
+#define CEMI_L_POLL_DATA_REQ 0x13
+#define CEMI_L_POLL_DATA_CON 0x25
+#define CEMI_T_DATA_INDIVIDUAL_REQ 0x4A
+#define CEMI_T_DATA_INDIVIDUAL_IND 0x94
+#define CEMI_T_DATA_CONNECTED_REQ 0x41
+#define CEMI_T_DATA_CONNECTED_IND 0x89
+#define CEMI_M_PROPREAD_REQ 0xFC
+#define CEMI_M_PROPREAD_CON 0xFB
+#define CEMI_M_PROPWRITE_REQ 0xF6
+#define CEMI_M_PROPWRITE_CON 0xF5
+#define CEMI_M_PROPINFO_IND 0xF7
+#define CEMI_M_FUNCPROPCMD_REQ 0xF8
+#define CEMI_M_FUNCPROPREAD_REQ 0xF9
+#define CEMI_M_FUNCPROP_CON 0xFA
+#define CEMI_M_RESET_REQ 0xF1
+#define CEMI_M_RESET_IND 0xF0
+
+/* Additional Information Types
+*/
+ /* 0x00 Reserved. */
+#define CEMI_PL_MEDIUM_INFORMATION 0x01 /*!< (2 octets) Domain Address used by PL medium; Client <-> Server */
+#define CEMI_RF_MEDIUM_INFORMATION 0x02 /*!< (7 octet) RF-Control byte and serial number/DoA;
+ Client <-> Server Busmonitor */
+#define CEMI_STATUS_INFO 0x03 /*!< (1 octet) Busmonitor Error Flags; see clause 2.5.5.5; Client <- Server */
+#define CEMI_TIMESTAMP_RELATIVE 0x04 /*!< (2 octets) Relative timestamp; e.g. for L_Raw.ind; Client <- Server */
+#define CEMI_TIME_DELAY_UNTIL_SENDING 0x05 /*!< (4 octets) Time delay (L_Raw.req, see clause 2.5.5.3); Client <- Server */
+ /* 0x06-0xFE Not used. */
+ /* 0xFF For future system extension (ESC Code). */
+
+/* Error Codes
+*/
+#define CEMI_UNSPECIFIED_ERROR 0x00 /*!< Unknown error (R/W). */
+#define CEMI_OUT_OF_RANGE 0x01 /*!< Write value not allowed (general, if not error 2 or 3) (W). */
+#define CEMI_OUT_OF_MAXRANGE 0x02 /*!< Write value to high (W). */
+#define CEMI_OUT_OF_MINRANGE 0x03 /*!< Write value to low (W). */
+#define CEMI_MEMORY_ERROR 0x04 /*!< Memory can not be written or only with fault(s) (W). */
+#define CEMI_READ_ONLY 0x05 /*!< Write access to a 'read only' or a write protected property (W). */
+#define CEMI_ILLEGAL_COMMAND 0x06 /*!< COMMAND not valid or not supported (W). */
+#define CEMI_VOID_DP 0x07 /*!< Read or write access to an non existing property (R/W). */
+#define CEMI_TYPE_CONFLICT 0x08 /*!< Write access with a wrong data type (datapoint length) (W). */
+#define CEMI_PROP_INDEX_RANGE_ERROR 0x09 /* Read or write access to a non existing property array index (R/W). */
+
+/* Common EMI specific device server properties
+*/
+#define CEMI_PID_DOMAIN_ADDRESS 0x70 /*!< Domain Address of a PL medium (cEMI server) device.
+ PDT_UNSIGNED_INT O - r/w */
+#define CEMI_PID_IO_LIST 0x71 /*!< List of Interface Objects in the (cEMI server) device.
+ PDT_UNSIGNED_INT O - r/w */
+
+#define CEMI_PID_MEDIUM_TYPE 0x51 /*!< Media Type(s) supported by cEMI server.
+ DPT_Media M - read only */
+#define CEMI_PID_COMM_MODE 0x52 /*!< Link Layer / Raw (Busmonitor) / Transport L.
+ DPT_CommMode O - r/w */
+#define CEMI_PID_MEDIUM_AVAILABILITY 0x53 /*!< Bus available (1) or not (0) ?
+ DPT_Media O - read only */
+#define CEMI_PID_ADD_INFO_TYPES 0x54 /*!< cEMI supported Additional Information Types.
+ DPT_AddInfoTypes O - read only */
+#define CEMI_PID_TRANSP_ENABLE 0x56 /*!< LL Transparency Mode of cEMI server.
+ DPT_Enable O - r/w */
+ /* 0x57 Reserved for cEMI client's subnetwork address.
+ PDT_UNSIGNED_CHAR O - r/w */
+ /* 0x58 Reserved for cEMI client's device address.
+ PDT_UNSIGNED_CHAR O - r/w */
+ /* 0x59 t.b.d.*/
+ /* 0x60 t.b.d.*/
+ /* 0x61 DoA Filter. t.b.d. O - read only */
+
+#define CEMI_PID_MEDIUM_TYPE_TP0 0x0001 /*!< TP 0 */
+#define CEMI_PID_MEDIUM_TYPE_TP1 0x0002 /*!< TP 1 */
+#define CEMI_PID_MEDIUM_TYPE_PL110 0x0004 /*!< PL 110 */
+#define CEMI_PID_MEDIUM_TYPE_PL132 0x0008 /*!< PL 132 */
+#define CEMI_PID_MEDIUM_TYPE_RF 0x0010 /*!< RF */
+
+#define CEMI_PID_COMM_MODE_LL 0x00 /*!< Link Layer = default comm. mode. */
+#define CEMI_PID_COMM_MODE_LLB 0x01 /*!< Link Layer Busmonitor. */
+#define CEMI_PID_COMM_MODE_LLR 0x02 /*!< Link Layer Raw Frames. */
+ /* 0x03 Reserved for Network Layer. */
+ /* 0x04 Reserved for TL group oriented. */
+ /* 0x05 Reserved for TL connection oriented. */
+ /* 0x05-0xFF Reserved for other 'destination layers'. */
+
+/* - - - - - - - T R E E V I E W I D E N T I F I E R - - - - - - - -
+*/
+
+/* Initialize the protocol identifier that is needed for the
+ protocol hook and to register the fields in the protocol tree
+*/
+static gint proto_cemi = -1;
+
+/* Initialize the registered fields identifiers. These fields
+ will be registered with the protocol during initialization.
+ Protocol fields are like type definitions. The protocol dissector
+ later on adds items of these types to the protocol tree.
+*/
+static gint hf_bytes = -1;
+static gint hf_folder = -1;
+static gint hf_cemi_mc = -1;
+static gint hf_cemi_error = -1;
+static gint hf_cemi_ai_length = -1;
+static gint hf_cemi_aie_type = -1;
+static gint hf_cemi_aie_length = -1;
+static gint hf_cemi_ot = -1;
+static gint hf_cemi_oi = -1;
+static gint hf_cemi_ox = -1;
+static gint hf_cemi_px = -1;
+static gint hf_cemi_pid = -1;
+static gint hf_cemi_ne = -1;
+static gint hf_cemi_sx = -1;
+static gint hf_cemi_ft = -1;
+static gint hf_cemi_rep = -1;
+static gint hf_cemi_bt = -1;
+static gint hf_cemi_prio = -1;
+static gint hf_cemi_ack = -1;
+static gint hf_cemi_ce = -1;
+static gint hf_cemi_at = -1;
+static gint hf_cemi_hc = -1;
+static gint hf_cemi_eff = -1;
+static gint hf_cemi_sa = -1;
+static gint hf_cemi_da = -1;
+static gint hf_cemi_len = -1;
+static gint hf_cemi_tpt = -1;
+static gint hf_cemi_tst = -1;
+static gint hf_cemi_num = -1;
+static gint hf_cemi_tc = -1;
+static gint hf_cemi_ac = -1;
+static gint hf_cemi_ad = -1;
+static gint hf_cemi_ad_memory_length = -1;
+static gint hf_cemi_ad_channel = -1;
+static gint hf_cemi_ad_type = -1;
+static gint hf_cemi_ax = -1;
+static gint hf_cemi_pw = -1;
+static gint hf_cemi_pdt = -1;
+static gint hf_cemi_me = -1;
+static gint hf_cemi_ra = -1;
+static gint hf_cemi_wa = -1;
+static gint hf_cemi_ext_oi = -1;
+static gint hf_cemi_ext_pid = -1;
+static gint hf_cemi_ext_ne = -1;
+static gint hf_cemi_ext_sx = -1;
+static gint hf_cemi_ext_dt = -1;
+static gint hf_cemi_ext_px = -1;
+static gint hf_cemi_ext_memory_length = -1;
+static gint hf_cemi_ext_memory_address = -1;
+static gint hf_cemi_memory_length = -1;
+static gint hf_cemi_memory_address = -1;
+static gint hf_cemi_memory_address_ext = -1;
+static gint hf_cemi_level = -1;
+static gint hf_cemi_snp_pid = -1;
+static gint hf_cemi_snp_reserved = -1;
+static gint hf_cemi_dpt_major = -1;
+static gint hf_cemi_dpt_minor = -1;
+static gint hf_cemi_scf = -1;
+static gint hf_cemi_scf_t = -1;
+static gint hf_cemi_scf_sai = -1;
+static gint hf_cemi_scf_sbc = -1;
+static gint hf_cemi_scf_svc = -1;
+static gint hf_cemi_adc_count = -1;
+
+/* Initialize the subtree pointers. These pointers are needed to
+ display the protocol in a structured tree. Subtrees hook on
+ already defined fields or (the topmost) on the protocol itself
+*/
+static gint ett_cemi = -1;
+static gint ett_cemi_ai = -1;
+static gint ett_cemi_aie = -1;
+static gint ett_cemi_ctrl1 = -1;
+static gint ett_cemi_ctrl2 = -1;
+static gint ett_cemi_tpci = -1;
+static gint ett_cemi_apci = -1;
+static gint ett_cemi_range = -1;
+static gint ett_cemi_pd = -1;
+static gint ett_cemi_dpt = -1;
+static gint ett_cemi_scf = -1;
+static gint ett_cemi_decrypted = -1;
+
+/* - - - - - - - - - - - V A L U E T A B L E S - - - - - - - - - - - -
+*/
+
+/* See following docs:
+
+ "AN033 v03 cEMI.pdf",
+ "AN057 v01 System B RfV.pdf",
+ "KSG259 2004.02.03 Identifiers.pdf",
+ "03_07_03 Standardized Identifier Tables.pdf"
+*/
+
+/* Message Code
+*/
+static const value_string mc_vals[] = {
+ { CEMI_L_BUSMON_IND, "L_Busmon.ind" },
+ { CEMI_L_RAW_IND, "L_Raw.ind" },
+ { CEMI_L_RAW_REQ, "L_Raw.req" },
+ { CEMI_L_RAW_CON, "L_Raw.con" },
+ { CEMI_L_DATA_REQ, "L_Data.req" },
+ { CEMI_L_DATA_CON, "L_Data.con" },
+ { CEMI_L_DATA_IND, "L_Data.ind" },
+ { CEMI_L_POLL_DATA_REQ, "L_PollData.req" },
+ { CEMI_L_POLL_DATA_CON, "L_PollData.con" },
+ { CEMI_T_DATA_INDIVIDUAL_REQ, "T_Data_Individual.req" },
+ { CEMI_T_DATA_INDIVIDUAL_IND, "T_Data_Individual.ind" },
+ { CEMI_T_DATA_CONNECTED_REQ, "T_Data_Connected.req" },
+ { CEMI_T_DATA_CONNECTED_IND, "T_Data_Connected.ind" },
+ { CEMI_M_PROPREAD_REQ, "M_PropRead.req" },
+ { CEMI_M_PROPREAD_CON, "M_PropRead.con" },
+ { CEMI_M_PROPWRITE_REQ, "M_PropWrite.req" },
+ { CEMI_M_PROPWRITE_CON, "M_PropWrite.con" },
+ { CEMI_M_PROPINFO_IND, "M_PropInfo.ind" },
+ { CEMI_M_FUNCPROPCMD_REQ, "M_FuncPropCmd.req" },
+ { CEMI_M_FUNCPROPREAD_REQ, "M_FuncPropRead.req" },
+ { CEMI_M_FUNCPROP_CON, "M_FuncProp.con" },
+ { CEMI_M_RESET_REQ, "M_Reset.req" },
+ { CEMI_M_RESET_IND, "M_Reset.ind" },
+ { 0, NULL }
+};
+
+/* Property access flags
+*/
+#define PA_RESPONSE 0x01
+#define PA_DATA 0x02
+
+/* Additional Info Element Type
+*/
+static const value_string aiet_vals[] = {
+ { 1, "PL Medium Info" },
+ { 2, "RF Medium Info" },
+ { 3, "BusMonitor Status Info" },
+ { 4, "Timestamp Relative" },
+ { 5, "Time Delay Until Sending" },
+ { 6, "Extended Relative Timestamp" },
+ { 7, "BiBat Info" },
+ { 0, NULL }
+};
+
+/* Frame Type
+*/
+static const value_string ft_vals[] = {
+ { 0, "Extended" },
+ { 1, "Standard" },
+ { 0, NULL }
+};
+
+/* Broadcast Type
+*/
+static const value_string bt_vals[] = {
+ { 0, "System" },
+ { 1, "Domain" },
+ { 0, NULL }
+};
+
+/* Priority
+*/
+static const value_string prio_vals[] = {
+ { 0, "System" },
+ { 2, "Urgent" },
+ { 1, "Normal" },
+ { 3, "Low" },
+ { 0, NULL }
+};
+
+/* Address Type
+*/
+static const value_string at_vals[] = {
+ { 0, "Individual" },
+ { 1, "Group" },
+ { 0, NULL }
+};
+
+/* Packet Type
+*/
+static const value_string pt_vals[] = {
+ { 0, "Data" },
+ { 1, "Control" },
+ { 0, NULL }
+};
+
+/* Sequence Type
+*/
+static const value_string st_vals[] = {
+ { 0, "Unnumbered" },
+ { 1, "Numbered" },
+ { 0, NULL }
+};
+
+/* Transport Layer Code
+*/
+static const value_string tc_vals[] = {
+ { 0, "Connect" },
+ { 1, "Disconnect" },
+ { 2, "ACK" },
+ { 3, "NAK" },
+ { 0, NULL }
+};
+
+/* Application Layer Code
+*/
+#define AC_GroupValueRead 0
+#define AC_GroupValueResp 1
+#define AC_GroupValueWrite 2
+#define AC_IndAddrWrite 3
+#define AC_IndAddrRead 4
+#define AC_IndAddrResp 5
+#define AC_AdcRead 6
+#define AC_AdcResp 7
+#define AC_MemRead 8
+#define AC_MemResp 9
+#define AC_MemWrite 10
+#define AC_UserMsg 11
+#define AC_DevDescrRead 12
+#define AC_DevDescrResp 13
+#define AC_Restart 14
+#define AC_Escape 15
+
+static const value_string ac_vals[] =
+{
+ { AC_GroupValueRead, "GroupValueRead" },
+ { AC_GroupValueResp, "GroupValueResp" },
+ { AC_GroupValueWrite, "GroupValueWrite" },
+ { AC_IndAddrWrite, "IndAddrWrite" },
+ { AC_IndAddrRead, "IndAddrRead" },
+ { AC_IndAddrResp, "IndAddrResp" },
+ { AC_AdcRead, "AdcRead" },
+ { AC_AdcResp, "AdcResp" },
+ { AC_MemRead, "MemRead" },
+ { AC_MemResp, "MemResp" },
+ { AC_MemWrite, "MemWrite" },
+ { AC_UserMsg, "UserMsg" },
+ { AC_DevDescrRead, "DevDescrRead" },
+ { AC_DevDescrResp, "DevDescrResp" },
+ { AC_Restart, "Restart" },
+ { AC_Escape, "Escape" },
+ { 0, NULL }
+};
+
+/* Extended AL codes
+*/
+#define AX_SysNwkParamRead 0x1C8
+#define AX_SysNwkParamResp 0x1C9
+#define AX_SysNwkParamWrite 0x1CA
+#define AX_PropExtValueRead 0x1CC
+#define AX_PropExtValueResp 0x1CD
+#define AX_PropExtValueWriteCon 0x1CE
+#define AX_PropExtValueWriteConRes 0x1CF
+#define AX_PropExtValueWriteUnCon 0x1D0
+#define AX_PropExtValueInfoReport 0x1D1
+#define AX_PropExtDescrRead 0x1D2
+#define AX_PropExtDescrResp 0x1D3
+#define AX_FuncPropExtCmd 0x1D4
+#define AX_FuncPropExtRead 0x1D5
+#define AX_FuncPropExtResp 0x1D6
+#define AX_MemExtWrite 0x1FB
+#define AX_MemExtWriteResp 0x1FC
+#define AX_MemExtRead 0x1FD
+#define AX_MemExtReadResp 0x1FE
+#define AX_UserMemRead 0x2C0
+#define AX_UserMemResp 0x2C1
+#define AX_UserMemWrite 0x2C2
+#define AX_UserMemBitWrite 0x2C4
+#define AX_UserMfrInfoRead 0x2C5
+#define AX_UserMfrInfoResp 0x2C6
+#define AX_FuncPropCmd 0x2C7
+#define AX_FuncPropRead 0x2C8
+#define AX_FuncPropResp 0x2C9
+#define AX_Restart 0x380
+#define AX_RestartReq 0x381
+#define AX_RestartResp 0x3A1
+#define AX_RoutingTableOpen 0x3C0
+#define AX_RoutingTableRead 0x3C1
+#define AX_RoutingTableResp 0x3C2
+#define AX_RoutingTableWrite 0x3C3
+#define AX_RouterMemRead 0x3C8
+#define AX_RouterMemResp 0x3C9
+#define AX_RouterMemWrite 0x3CA
+#define AX_RouterStatusRead 0x3CD
+#define AX_RouterStatusResp 0x3CE
+#define AX_RouterStatusWrite 0x3CF
+#define AX_MemBitWrite 0x3D0
+#define AX_AuthReq 0x3D1
+#define AX_AuthResp 0x3D2
+#define AX_KeyWrite 0x3D3
+#define AX_KeyResp 0x3D4
+#define AX_PropValueRead 0x3D5
+#define AX_PropValueResp 0x3D6
+#define AX_PropValueWrite 0x3D7
+#define AX_PropDescrRead 0x3D8
+#define AX_PropDescrResp 0x3D9
+#define AX_NwkParamRead 0x3DA
+#define AX_NwkParamResp 0x3DB
+#define AX_IndAddrSerNumRead 0x3DC
+#define AX_IndAddrSerNumResp 0x3DD
+#define AX_IndAddrSerNumWrite 0x3DE
+#define AX_DomAddrWrite 0x3E0
+#define AX_DomAddrRead 0x3E1
+#define AX_DomAddrResp 0x3E2
+#define AX_DomAddrSelRead 0x3E3
+#define AX_NwkParamWrite 0x3E4
+#define AX_LinkRead 0x3E5
+#define AX_LinkResp 0x3E6
+#define AX_LinkWrite 0x3E7
+#define AX_GroupPropValueRead 0x3E8
+#define AX_GroupPropValueResp 0x3E9
+#define AX_GroupPropValueWrite 0x3EA
+#define AX_GroupPropValueInfo 0x3EB
+#define AX_DomAddrSerNumRead 0x3EC
+#define AX_DomAddrSerNumResp 0x3ED
+#define AX_DomAddrSerNumWrite 0x3EE
+#define AX_FileStreamInfo 0x3F0
+#define AX_DataSec 0x3F1
+
+static const value_string ax_vals[] =
+{
+ { AX_SysNwkParamRead, "SysNwkParamRead" },
+ { AX_SysNwkParamResp, "SysNwkParamResp" },
+ { AX_SysNwkParamWrite, "SysNwkParamWrite" },
+ { AX_PropExtValueRead, "PropExtValueRead" },
+ { AX_PropExtValueResp, "PropExtValueResp" },
+ { AX_PropExtValueWriteCon, "PropExtValueWriteCon" },
+ { AX_PropExtValueWriteConRes, "PropExtValueWriteConRes" },
+ { AX_PropExtValueWriteUnCon, "PropExtValueWriteUnCon" },
+ { AX_PropExtDescrRead, "PropExtDescrRead" },
+ { AX_PropExtDescrResp, "PropExtDescrResp" },
+ { AX_FuncPropExtCmd, "FuncPropExtCmd" },
+ { AX_FuncPropExtRead, "FuncPropExtRead" },
+ { AX_FuncPropExtResp, "FuncPropExtResp" },
+ { AX_MemExtWrite, "MemExtWrite" },
+ { AX_MemExtWriteResp, "MemExtWriteResp" },
+ { AX_MemExtRead, "MemExtRead" },
+ { AX_MemExtReadResp, "MemExtReadResp" },
+ { AX_UserMemRead, "UserMemRead" },
+ { AX_UserMemResp, "UserMemResp" },
+ { AX_UserMemWrite, "UserMemWrite" },
+ { AX_UserMemBitWrite, "UserMemBitWrite" },
+ { AX_UserMfrInfoRead, "UserMfrInfoRead" },
+ { AX_UserMfrInfoResp, "UserMfrInfoResp" },
+ { AX_FuncPropCmd, "FuncPropCmd" },
+ { AX_FuncPropRead, "FuncPropRead" },
+ { AX_FuncPropResp, "FuncPropResp" },
+ { AX_Restart, "Restart" },
+ { AX_RestartReq, "RestartReq" },
+ { AX_RestartResp, "RestartResp" },
+ { AX_RoutingTableOpen, "RoutingTableOpen" },
+ { AX_RoutingTableRead, "RoutingTableRead" },
+ { AX_RoutingTableResp, "RoutingTableResp" },
+ { AX_RoutingTableWrite, "RoutingTableWrite" },
+ { AX_RouterMemRead, "RouterMemRead" },
+ { AX_RouterMemResp, "RouterMemResp" },
+ { AX_RouterMemWrite, "RouterMemWrite" },
+ { AX_RouterStatusRead, "RouterStatusRead" },
+ { AX_RouterStatusResp, "RouterStatusResp" },
+ { AX_RouterStatusWrite, "RouterStatusWrite" },
+ { AX_MemBitWrite, "MemBitWrite" },
+ { AX_AuthReq, "AuthReq" },
+ { AX_AuthResp, "AuthResp" },
+ { AX_KeyWrite, "KeyWrite" },
+ { AX_KeyResp, "KeyResp" },
+ { AX_PropValueRead, "PropValueRead" },
+ { AX_PropValueResp, "PropValueResp" },
+ { AX_PropValueWrite, "PropValueWrite" },
+ { AX_PropDescrRead, "PropDescrRead" },
+ { AX_PropDescrResp, "PropDescrResp" },
+ { AX_NwkParamRead, "NwkParamRead" },
+ { AX_NwkParamResp, "NwkParamResp" },
+ { AX_IndAddrSerNumRead, "IndAddrSerNumRead" },
+ { AX_IndAddrSerNumResp, "IndAddrSerNumResp" },
+ { AX_IndAddrSerNumWrite, "IndAddrSerNumWrite" },
+ { AX_DomAddrWrite, "DomAddrWrite" },
+ { AX_DomAddrRead, "DomAddrRead" },
+ { AX_DomAddrResp, "DomAddrResp" },
+ { AX_DomAddrSelRead, "DomAddrSelRead" },
+ { AX_NwkParamWrite, "NwkParamWrite" },
+ { AX_LinkRead, "LinkRead" },
+ { AX_LinkResp, "LinkResp" },
+ { AX_LinkWrite, "LinkWrite" },
+ { AX_GroupPropValueRead, "GroupPropValueRead" },
+ { AX_GroupPropValueResp, "GroupPropValueResp" },
+ { AX_GroupPropValueWrite, "GroupPropValueWrite" },
+ { AX_GroupPropValueInfo, "GroupPropValueInfo" },
+ { AX_DomAddrSerNumRead, "DomAddrSerNumRead" },
+ { AX_DomAddrSerNumResp, "DomAddrSerNumResp" },
+ { AX_DomAddrSerNumWrite, "DomAddrSerNumWrite" },
+ { AX_FileStreamInfo, "FileStreamInfo" },
+ { AX_DataSec, "DataSec" },
+ { 0, NULL }
+};
+
+/* SCF (Security Control Field)
+*/
+static const value_string scf_vals[] =
+{
+ { 0x00, "CCM S-A_Data with Authentication-only" },
+ { 0x10, "CCM S-A_Data with Authentication+Confidentiality" },
+ { 0x12, "CCM S-A_Sync_Req with Authentication+Confidentiality" },
+ { 0x13, "CCM S-A_Sync_Res with Authentication+Confidentiality" },
+ { 0x08, "CCM S-A_Data with Authentication-only, System Broadcast" },
+ { 0x18, "CCM S-A_Data with Authentication+Confidentiality, System Broadcast" },
+ { 0x1a, "CCM S-A_Sync_Req with Authentication+Confidentiality, System Broadcast" },
+ { 0x1b, "CCM S-A_Sync_Res with Authentication+Confidentiality, System Broadcast" },
+ { 0x80, "CCM S-A_Data with Authentication-only, Tool Access" },
+ { 0x90, "CCM S-A_Data with Authentication+Confidentiality, Tool Access" },
+ { 0x92, "CCM S-A_Sync_Req with Authentication+Confidentiality, Tool Access" },
+ { 0x93, "CCM S-A_Sync_Res with Authentication+Confidentiality, Tool Access" },
+ { 0x88, "CCM S-A_Data with Authentication-only, System Broadcast, Tool Access" },
+ { 0x98, "CCM S-A_Data with Authentication+Confidentiality, Tool Access, System Broadcast" },
+ { 0x9a, "CCM S-A_Sync_Req with Authentication+Confidentiality, Tool Access, System Broadcast" },
+ { 0x9b, "CCM S-A_Sync_Res with Authentication+Confidentiality, Tool Access, System Broadcast" },
+ { 0, NULL }
+};
+
+/* SCF (Security Control Field).
+*/
+static const value_string scf_short_vals[] =
+{
+ { 0x00, "Data+A" },
+ { 0x10, "Data+A+C" },
+ { 0x12, "SyncReq" },
+ { 0x13, "SyncRes" },
+ { 0x08, "Data+A+SBC" },
+ { 0x18, "Data+A+C+SBC" },
+ { 0x1a, "SyncReq+SBC" },
+ { 0x1b, "SyncRes+SBC" },
+ { 0x80, "Data+A+T" },
+ { 0x90, "Data+A+C+T" },
+ { 0x92, "SyncReq+T" },
+ { 0x93, "SyncRes+T" },
+ { 0x88, "Data+A+T+SBC" },
+ { 0x98, "Data+A+C+T+SBC" },
+ { 0x9a, "SyncReq+T+SBC" },
+ { 0x9b, "SyncRes+T+SBC" },
+ { 0, NULL }
+};
+
+/* SCF.SAI (Security Algorithm Identifier)
+*/
+static const value_string scf_sai_vals[] =
+{
+ { 0, "CCM A" },
+ { 1, "CCM A+S" },
+ { 0, NULL }
+};
+
+/* SCF.Service
+*/
+static const value_string scf_svc_vals[] =
+{
+ { 0, "Data" },
+ { 2, "Sync_Req" },
+ { 3, "Sync_Res" },
+ { 0, NULL }
+};
+
+/* See KNX documents:
+* "03_07_03 Standardized Identifier Tables v01.03.01 AS"
+* "03_05_01 Resources v01.09.03 AS"
+*/
+
+/* Property Data Types
+* See "4 Property Datatypes Identifiers" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
+*/
+static const value_string pdt_vals[] = {
+ { 0x00, "PDT_CONTROL" },
+ { 0x01, "PDT_CHAR" },
+ { 0x02, "PDT_UNSIGNED_CHAR" },
+ { 0x03, "PDT_INT" },
+ { 0x04, "PDT_UNSIGNED_INT" },
+ { 0x05, "PDT_KNX_FLOAT" },
+ { 0x06, "PDT_DATE" },
+ { 0x07, "PDT_TIME" },
+ { 0x08, "PDT_LONG" },
+ { 0x09, "PDT_UNSIGNED_LONG" },
+ { 0x0A, "PDT_FLOAT" },
+ { 0x0B, "PDT_DOUBLE" },
+ { 0x0C, "PDT_CHAR_BLOCK" },
+ { 0x0D, "PDT_POLL_GROUP_SETTINGS" },
+ { 0x0E, "PDT_SHORT_CHAR_BLOCK" },
+ { 0x0F, "PDT_DATE_TIME" },
+ { 0x10, "PDT_VARIABLE_LENGTH" },
+ { 0x11, "PDT_GENERIC_01" },
+ { 0x12, "PDT_GENERIC_02" },
+ { 0x13, "PDT_GENERIC_03" },
+ { 0x14, "PDT_GENERIC_04" },
+ { 0x15, "PDT_GENERIC_05" },
+ { 0x16, "PDT_GENERIC_06" },
+ { 0x17, "PDT_GENERIC_07" },
+ { 0x18, "PDT_GENERIC_08" },
+ { 0x19, "PDT_GENERIC_09" },
+ { 0x1A, "PDT_GENERIC_10" },
+ { 0x1B, "PDT_GENERIC_11" },
+ { 0x1C, "PDT_GENERIC_12" },
+ { 0x1D, "PDT_GENERIC_13" },
+ { 0x1E, "PDT_GENERIC_14" },
+ { 0x1F, "PDT_GENERIC_15" },
+ { 0x20, "PDT_GENERIC_16" },
+ { 0x21, "PDT_GENERIC_17" },
+ { 0x22, "PDT_GENERIC_18" },
+ { 0x23, "PDT_GENERIC_19" },
+ { 0x24, "PDT_GENERIC_20" },
+ { 0x2F, "PDT_UTF-8" },
+ { 0x30, "PDT_VERSION" },
+ { 0x31, "PDT_ALARM_INFO" },
+ { 0x32, "PDT_BINARY_INFORMATION" },
+ { 0x33, "PDT_BITSET8" },
+ { 0x34, "PDT_BITSET16" },
+ { 0x35, "PDT_ENUM8" },
+ { 0x36, "PDT_SCALING" },
+ { 0x3C, "PDT_NE_VL" },
+ { 0x3D, "PDT_NE_FL" },
+ { 0x3E, "PDT_FUNCTION" },
+ { 0x3F, "PDT_ESCAPE" },
+ { 0, NULL }
+};
+
+/* Interface Object Types
+* See "2 Interface Object Types" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
+*/
+static const value_string ot_vals[] = {
+ { 0, "Device" },
+ { 1, "Address Table" },
+ { 2, "Association Table" },
+ { 3, "Application Program" },
+ { 4, "Interface Program" },
+ { 5, "KNX-Object Association Table" },
+ { 6, "Router" },
+ { 7, "LTE Address Routing Table" },
+ { 8, "cEMI Server" },
+ { 9, "Group Object Table" },
+ { 10, "Polling Master" },
+ { 11, "KNXnet/IP Parameter" },
+ { 13, "File Server" },
+ { 17, "Data Security" },
+ { 0, NULL }
+};
+
+/* IOT independent PIDs
+* See "3.2 Interface Object Type independent standard Properties" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
+* See "4.2 Interface Object Type independent Properties" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid_vals[] = {
+ { 1, "PID_OBJECT_TYPE" },
+ { 2, "PID_OBJECT_NAME" },
+ { 3, "PID_SEMAPHOR" },
+ { 4, "PID_GROUP_OBJECT_REFERENCE" },
+ { 5, "PID_LOAD_STATE_CONTROL" },
+ { 6, "PID_RUN_STATE_CONTROL" },
+ { 7, "PID_TABLE_REFERENCE" },
+ { 8, "PID_SERVICE_CONTROL" },
+ { 9, "PID_FIRMWARE_REVISION" },
+ { 10, "PID_SERVICES_SUPPORTED" },
+ { 11, "PID_SERIAL_NUMBER" },
+ { 12, "PID_MANUFACTURER_ID" },
+ { 13, "PID_PROGRAM_VERSION" },
+ { 14, "PID_DEVICE_CONTROL" },
+ { 15, "PID_ORDER_INFO" },
+ { 16, "PID_PEI_TYPE" },
+ { 17, "PID_PORT_CONFIGURATION" },
+ { 18, "PID_POLL_GROUP_SETTINGS" },
+ { 19, "PID_MANUFACTURER_DATA" },
+ { 21, "PID_DESCRIPTION" },
+ { 23, "PID_TABLE" },
+ { 24, "PID_ENROL" },
+ { 25, "PID_VERSION" },
+ { 26, "PID_GROUP_OBJECT_LINK" },
+ { 27, "PID_MCB_TABLE" },
+ { 28, "PID_ERROR_CODE" },
+ { 29, "PID_OBJECT_INDEX" },
+ { 30, "PID_DOWNLOAD_COUNTER" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 0 (Device)
+* See "3.3.1 Device Object Interface Object (Object Type = 0)" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
+* See "4.3 Device Object (Object Type 0)" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid0_vals[] = {
+ { 51, "PID_ROUTING_COUNT" },
+ { 52, "PID_MAX_RETRY_COUNT" },
+ { 53, "PID_ERROR_FLAGS" },
+ { 54, "PID_PROGMODE" },
+ { 55, "PID_PRODUCT_ID" },
+ { 56, "PID_MAX_APDULENGTH" },
+ { 57, "PID_SUBNET_ADDR" },
+ { 58, "PID_DEVICE_ADDR" },
+ { 59, "PID_PB_CONFIG" },
+ { 60, "PID_ADDR_REPORT" },
+ { 61, "PID_ADDR_CHECK" },
+ { 62, "PID_OBJECT_VALUE" },
+ { 63, "PID_OBJECTLINK" },
+ { 64, "PID_APPLICATION" },
+ { 65, "PID_PARAMETER" },
+ { 66, "PID_OBJECTADDRESS" },
+ { 67, "PID_PSU_TYPE" },
+ { 68, "PID_PSU_STATUS" },
+ { 69, "PID_PSU_ENABLE" },
+ { 70, "PID_DOMAIN_ADDRESS" },
+ { 71, "PID_IO_LIST" },
+ { 72, "PID_MGT_DESCRIPTOR_01" },
+ { 73, "PID_PL110_PARAM" },
+ { 74, "PID_RF_REPEAT_COUNTER" },
+ { 75, "PID_RECEIVE_BLOCK_TABLE" },
+ { 76, "PID_RANDOM_PAUSE_TABLE" },
+ { 77, "PID_RECEIVE_BLOCK_NR" },
+ { 78, "PID_HARDWARE_TYPE" },
+ { 79, "PID_RETRANSMITTER_NUMBER" },
+ { 80, "PID_SERIAL_NR_TABLE" },
+ { 81, "PID_BIBATMASTER_ADDRESS" },
+ { 82, "PID_RF_DOMAIN_ADDRESS" },
+ { 83, "PID_DEVICE_DESCRIPTOR" },
+ { 84, "PID_METERING_FILTER_TABLE" },
+ { 85, "PID_GROUP_TELEGR_RATE_LIMIT_TIME_BASE" },
+ { 86, "PID_GROUP_TELEGR_RATE_LIMIT_NO_OF_TELEGR" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 1 (Address Table)
+* See "4.10.6 Group Address Table - Realisation Type 6" in "03_05_01 Resources v01.09.03 AS"
+* See "4.10.7 Group Address Table - Realisation Type 7" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid1_vals[] = {
+ { 51, "PID_EXT_FRAMEFORMAT" },
+ { 52, "PID_ADDRTAB1" },
+ { 53, "PID_GROUP_RESPONSER_TABLE" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 6 (Router)
+* See "4.4 Router Object (Object Type 6)" in "03_05_01 Resources v01.09.03 AS"
+* See "2.4.4 Router Object" in "AN161 v05 Coupler Model 2.0 AS"
+*/
+static const value_string pid6_vals[] = {
+ { 51, "PID_MEDIUM_STATUS" }, /* alias "PID_LINE_STATUS" */
+ { 52, "PID_MAIN_LCCONFIG" },
+ { 53, "PID_SUB_LCCONFIG" },
+ { 54, "PID_MAIN_LCGRPCONFIG" },
+ { 55, "PID_SUB_LCGRPCONFIG" },
+ { 56, "PID_ROUTETABLE_CONTROL" },
+ { 57, "PID_COUPL_SERV_CONTROL" },
+ { 58, "PID_MAX_APDU_LENGTH" },
+ { 59, "PID_L2_COUPLER_TYPE" },
+ { 61, "PID_HOP_COUNT" },
+ { 63, "PID_MEDIUM" },
+ { 67, "PID_FILTER_TABLE_USE" },
+ { 104, "PID_PL110_SBC_CONTROL" },
+ { 105, "PID_PL110_DOA" },
+ { 112, "PID_RF_SBC_CONTROL" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 7 (LTE Address Routing Table)
+* See "4.5 LTE Address Routing Table Object (Object Type 7)" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid7_vals[] = {
+ { 51, "PID_LTE_ROUTESELECT" },
+ { 52, "PID_LTE_ROUTETABLE" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 8 (cEMI Server)
+* See "4.6 cEMI Server Object (Object Type 8)" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid8_vals[] = {
+ { 51, "PID_MEDIUM_TYPE" },
+ { 52, "PID_COMM_MODE" },
+ { 53, "PID_MEDIUM_AVAILABILITY" },
+ { 54, "PID_ADD_INFO_TYPES" },
+ { 55, "PID_TIME_BASE" },
+ { 56, "PID_TRANSP_ENABLE" },
+ { 59, "PID_BIBAT_NEXTBLOCK" },
+ { 60, "PID_RF_MODE_SELECT" },
+ { 61, "PID_RF_MODE_SUPPORT" },
+ { 62, "PID_RF_FILTERING_MODE_SELECT" },
+ { 63, "PID_RF_FILTERING_MODE_SUPPORT" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 9 (Group Object Table)
+* See "4.12.4 Group Object Table - Realisation Type 6" in "03_05_01 Resources v01.09.03 AS"
+*/
+static const value_string pid9_vals[] = {
+ { 51, "PID_GRPOBJTABLE" },
+ { 52, "PID_EXT_GRPOBJREFERENCE" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 11 (KNXnet/IP Parameter),
+* See "2.5 KNXnet/IP Parameter Object" in "03_08_03 Management v01.06.02 AS"
+* See "2.3.1 KNXnet/IP Parameter Object" in "AN159 v06 KNXnet-IP Secure AS"
+*/
+static const value_string pid11_vals[] = {
+ { 51, "PID_PROJECT_INSTALLATION_ID" },
+ { 52, "PID_KNX_INDIVIDUAL_ADDRESS" },
+ { 53, "PID_ADDITIONAL_INDIVIDUAL_ADDRESSES" },
+ { 54, "PID_CURRENT_IP_ASSIGNMENT_METHOD" },
+ { 55, "PID_IP_ASSIGNMENT_METHOD" },
+ { 56, "PID_IP_CAPABILITIES" },
+ { 57, "PID_CURRENT_IP_ADDRESS" },
+ { 58, "PID_CURRENT_SUBNET_MASK" },
+ { 59, "PID_CURRENT_DEFAULT_GATEWAY" },
+ { 60, "PID_IP_ADDRESS" },
+ { 61, "PID_SUBNET_MASK" },
+ { 62, "PID_DEFAULT_GATEWAY" },
+ { 63, "PID_DHCP_BOOTP_SERVER" },
+ { 64, "PID_MAC_ADDRESS" },
+ { 65, "PID_SYSTEM_SETUP_MULTICAST_ADDRESS" },
+ { 66, "PID_ROUTING_MULTICAST_ADDRESS" },
+ { 67, "PID_TTL" },
+ { 68, "PID_KNXNETIP_DEVICE_CAPABILITIES" },
+ { 69, "PID_KNXNETIP_DEVICE_STATE" },
+ { 70, "PID_KNXNETIP_ROUTING_CAPABILITIES" },
+ { 71, "PID_PRIORITY_FIFO_ENABLED" },
+ { 72, "PID_QUEUE_OVERFLOW_TO_IP" },
+ { 73, "PID_QUEUE_OVERFLOW_TO_KNX" },
+ { 74, "PID_MSG_TRANSMIT_TO_IP" },
+ { 75, "PID_MSG_TRANSMIT_TO_KNX" },
+ { 76, "PID_FRIENDLY_NAME" },
+ { 78, "PID_ROUTING_BUSY_WAIT_TIME" },
+ { 91, "PID_BACKBONE_KEY" },
+ { 92, "PID_DEVICE_AUTHENTICATION_CODE" },
+ { 93, "PID_PASSWORD_HASHES" },
+ { 94, "PID_SECURED_SERVICE_FAMILIES" },
+ { 95, "PID_MULTICAST_LATENCY_TOLERANCE" },
+ { 96, "PID_SYNC_LATENCY_FRACTION" },
+ { 97, "PID_TUNNELLING_USERS" },
+ { 0, NULL }
+};
+
+/* PIDs for IOT = 17 (Security)
+* See "2.3.5 Security Interface Object" in "KSG638-26.03 KNX Data Security"
+*/
+static const value_string pid17_vals[] = {
+ { 51, "PID_SECURITY_MODE" },
+ { 52, "PID_P2P_KEY_TABLE" },
+ { 53, "PID_GRP_KEY_TABLE" },
+ { 54, "PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE" },
+ { 55, "PID_SECURITY_FAILURES_LOG" },
+ { 56, "PID_TOOL_KEY" },
+ { 57, "PID_SECURITY_REPORT" },
+ { 58, "PID_SECURITY_REPORT_CONTROL" },
+ { 59, "PID_SEQUENCE_NUMBER_SENDING" },
+ { 60, "PID_ZONE_KEY_TABLE" },
+ { 61, "PID_GO_SECURITY_FLAGS" },
+ { 62, "PID_ROLE_TABLE" },
+ { 0, NULL }
+};
+
+/* - - - - - - - - - H E L P E R F U N C T I O N S - - - - - - - - - -
+*/
+
+/* Add raw data to list view, tree view, and parent folder
+*/
+static proto_item* proto_tree_add_data( proto_tree* tree, tvbuff_t* tvb, gint offset, gint length, column_info* cinfo, proto_item* item,
+ const gchar* name, const gchar* text1, const gchar* text2 )
+{
+ proto_item* new_item = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, length, NULL, "%s: $", name );
+ if( text1 ) col_append_str( cinfo, COL_INFO, text1 );
+ if( text2 ) proto_item_append_text( item, "%s", text2 );
+
+ while( length > 0 )
+ {
+ guint8 value = tvb_get_guint8( tvb, offset );
+ if( text1 ) col_append_fstr( cinfo, COL_INFO, "%02X", value );
+ if( text2 ) proto_item_append_text( item, "%02X", value );
+ proto_item_append_text( new_item, " %02X", value );
+ offset++;
+ length--;
+ }
+
+ return new_item;
+}
+
+static const gchar* get_pid_name( gint ot, gint pid )
+{
+ if( pid <= 50 )
+ {
+ return try_val_to_str( pid, pid_vals );
+ }
+ {
+ const value_string* vals = NULL;
+ switch( ot )
+ {
+ case 0:
+ vals = pid0_vals;
+ break;
+ case 1:
+ vals = pid1_vals;
+ break;
+ case 6:
+ vals = pid6_vals;
+ break;
+ case 7:
+ vals = pid7_vals;
+ break;
+ case 8:
+ vals = pid8_vals;
+ break;
+ case 9:
+ vals = pid9_vals;
+ break;
+ case 11:
+ vals = pid11_vals;
+ break;
+ case 17:
+ vals = pid17_vals;
+ break;
+ }
+ if( vals )
+ {
+ return try_val_to_str( pid, vals );
+ }
+ }
+ return NULL;
+}
+
+/* Decrypt data security APDU with a specific key.
+*/
+static const guint8* decrypt_data_security_data_with_key( wmem_allocator_t *pool, const guint8* key, const guint8* encrypted, gint encrypted_size, const guint8* cemi, gint cemi_size )
+{
+ guint8 ctr_0[ KNX_KEY_LENGTH ];
+ guint8 b_0[ KNX_KEY_LENGTH ];
+ guint8 mac[ KNX_KEY_LENGTH ];
+ guint8* a_bytes = 0;
+ const guint8* p_bytes = NULL;
+ gint a_length = 0;
+ gint p_length = 0;
+
+ guint8* decrypted = NULL;
+
+ if( encrypted_size > 4 ) // contains 4 bytes MAC
+ {
+ if( cemi_size >= 2 )
+ {
+ gint additionalInfoLength = cemi[ 1 ];
+ gint offsetToData = additionalInfoLength + 11;
+ if( offsetToData + 6 <= cemi_size )
+ {
+ /* 1 byte Security Control Field */
+ guint8 scf = cemi[ offsetToData ];
+
+ // Get A and P.
+ if( (scf & 0x30) == 0x10 ) // A+C
+ {
+ p_bytes = encrypted;
+ p_length = encrypted_size - 4;
+ }
+
+ // Build b_0.
+ b_0[ 0 ] = cemi[ offsetToData + 1 ]; // SeqNr
+ b_0[ 1 ] = cemi[ offsetToData + 2 ];
+ b_0[ 2 ] = cemi[ offsetToData + 3 ];
+ b_0[ 3 ] = cemi[ offsetToData + 4 ];
+ b_0[ 4 ] = cemi[ offsetToData + 5 ];
+ b_0[ 5 ] = cemi[ offsetToData + 6 ];
+ b_0[ 6 ] = cemi[ additionalInfoLength + 4 ]; // SA
+ b_0[ 7 ] = cemi[ additionalInfoLength + 5 ];
+ b_0[ 8 ] = cemi[ additionalInfoLength + 6 ]; // DA
+ b_0[ 9 ] = cemi[ additionalInfoLength + 7 ];
+ b_0[ 10 ] = 0; // cemi[additionalInfoLength + 2] & 0x80; // FT
+ b_0[ 11 ] = cemi[ additionalInfoLength + 3 ] & 0x8F; // AT (AT+EFF)
+ b_0[ 12 ] = cemi[ additionalInfoLength + 9 ]; // TPCI + ApciSec
+ b_0[ 13 ] = cemi[ additionalInfoLength + 10 ];
+ b_0[ 14 ] = 0;
+ b_0[ 15 ] = (guint8) p_length;
+
+ // Build ctr_0.
+ ctr_0[ 0 ] = cemi[ offsetToData + 1 ]; // SeqNr
+ ctr_0[ 1 ] = cemi[ offsetToData + 2 ];
+ ctr_0[ 2 ] = cemi[ offsetToData + 3 ];
+ ctr_0[ 3 ] = cemi[ offsetToData + 4 ];
+ ctr_0[ 4 ] = cemi[ offsetToData + 5 ];
+ ctr_0[ 5 ] = cemi[ offsetToData + 6 ];
+ ctr_0[ 6 ] = cemi[ additionalInfoLength + 4 ]; // SA
+ ctr_0[ 7 ] = cemi[ additionalInfoLength + 5 ];
+ ctr_0[ 8 ] = cemi[ additionalInfoLength + 6 ]; // DA
+ ctr_0[ 9 ] = cemi[ additionalInfoLength + 7 ];
+ ctr_0[ 10 ] = 0;
+ ctr_0[ 11 ] = 0;
+ ctr_0[ 12 ] = 0;
+ ctr_0[ 13 ] = 0;
+ ctr_0[ 14 ] = 0x01;
+ ctr_0[ 15 ] = 0;
+
+ decrypted = knx_ccm_encrypt( 0, key, p_bytes, p_length, encrypted + encrypted_size - 4, 4, ctr_0, 4 );
+
+ a_bytes = (guint8*) wmem_alloc( pool, encrypted_size );
+ if( (scf & 0x30) == 0x10 ) // A+C
+ {
+ a_bytes[ 0 ] = scf;
+ a_length = 1;
+ p_bytes = decrypted;
+ p_length = encrypted_size - 4;
+ }
+ else if( (scf & 0x30) == 0x00 ) // A
+ {
+ a_bytes[ 0 ] = scf;
+ memcpy( a_bytes + 1, decrypted, encrypted_size - 4 );
+ a_length = encrypted_size - 3;
+ }
+
+ knx_ccm_calc_cbc_mac( mac, key, a_bytes, a_length, p_bytes, p_length, b_0 );
+ wmem_free( pool, a_bytes );
+
+ if( memcmp( mac, decrypted + p_length, 4 ) != 0 )
+ {
+ // Wrong mac. Return 0.
+ wmem_free( pool, decrypted );
+ decrypted = NULL;
+ }
+ }
+ }
+ }
+
+ return decrypted;
+}
+
+/* Context info for decrypt_data_security_data
+*/
+struct data_security_info
+{
+ guint16 source; // KNX source address
+ guint16 dest; // KNX source address
+ guint8 multicast; // KNX multicast (group addressed)?
+ guint64 seq_nr; // 6-byte data security sequence number
+ gchar output_text[ 128 ]; // buffer for diagnostic output text
+};
+
+/* Decrypt data security APDU.
+*/
+static const guint8* decrypt_data_security_data( wmem_allocator_t *pool, const guint8* encrypted, gint encrypted_size, const guint8* cemi, gint cemi_size, struct data_security_info* info )
+{
+ const guint8* key = NULL;
+ const guint8* decrypted = NULL;
+ guint8 keys_found = 0;
+
+ // Get context info
+ guint16 source = info->source;
+ guint16 dest = info->dest;
+ guint8 multicast = info->multicast;
+
+ gchar* output = info->output_text;
+ gint output_max = sizeof info->output_text;
+ snprintf( output, output_max, "with " );
+ while( *output ) { ++output; --output_max; }
+
+ // Try keys from keyring.XML
+ if( multicast )
+ {
+ // Try keys associated with GA
+ struct knx_keyring_ga_keys* ga_key;
+
+ for( ga_key = knx_keyring_ga_keys; ga_key; ga_key = ga_key->next )
+ {
+ if( ga_key->ga == dest )
+ {
+ keys_found = 1;
+ key = ga_key->key;
+ decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ snprintf( output, output_max, "GA " );
+ while( *output ) { ++output; --output_max; }
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Try keys associated with dest IA
+ struct knx_keyring_ia_keys* ia_key;
+
+ for( ia_key = knx_keyring_ia_keys; ia_key; ia_key = ia_key->next )
+ {
+ if( ia_key->ia == dest )
+ {
+ keys_found = 1;
+ key = ia_key->key;
+ decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ snprintf( output, output_max, "dest IA " );
+ while( *output ) { ++output; --output_max; }
+ break;
+ }
+ }
+ }
+ }
+
+ if( !decrypted )
+ {
+ // Try keys associated with source IA
+ struct knx_keyring_ia_keys* ia_key;
+
+ for( ia_key = knx_keyring_ia_keys; ia_key; ia_key = ia_key->next )
+ {
+ if( ia_key->ia == source )
+ {
+ keys_found = 1;
+ key = ia_key->key;
+ decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ snprintf( output, output_max, "source IA " );
+ while( *output ) { ++output; --output_max; }
+ break;
+ }
+ }
+ }
+ }
+
+ if( !decrypted && knx_decryption_key_count )
+ {
+ // Try all explicitly specified keys
+ guint8 key_index;
+
+ for( key_index = 0; key_index < knx_decryption_key_count; ++key_index )
+ {
+ keys_found = 1;
+ key = knx_decryption_keys[ key_index ];
+ decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ break;
+ }
+ }
+ }
+
+ if( decrypted )
+ {
+ guint8 count;
+
+ snprintf( output, output_max, "key" );
+
+ for( count = 16; count; --count )
+ {
+ while( *output ) { ++output; --output_max; }
+ snprintf( output, output_max, " %02X", *key++ );
+ }
+ }
+ else
+ {
+ snprintf( info->output_text, sizeof info->output_text, keys_found ? "failed" : "no keys found" );
+ }
+
+ return decrypted;
+}
+
+/* Dissect Object Index (1 byte)
+*/
+static guint8 dissect_ox( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 *p_error )
+{
+ gint offset = *p_offset;
+
+ if( offset < end_pos )
+ {
+ guint8 ox = tvb_get_guint8( tvb, offset );
+ column_info *cinfo = pinfo->cinfo;
+
+ col_append_fstr( cinfo, COL_INFO, " OX=%u", ox );
+ proto_item_append_text( node, ", OX=%u", ox );
+ proto_tree_add_item( list, hf_cemi_ox, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ *p_offset = ++offset;
+ return ox;
+ }
+
+ proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Object Index: expected 1 byte" );
+
+ if( p_error )
+ {
+ *p_error = 1;
+ }
+
+ return 0;
+}
+
+/* Dissect Object Type (2 bytes)
+*/
+static guint16 dissect_ot( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 *p_error )
+{
+ gint offset = *p_offset;
+
+ if( offset + 1 < end_pos )
+ {
+ guint16 ot = tvb_get_ntohs( tvb, offset );
+ column_info *cinfo = pinfo->cinfo;
+
+ col_append_fstr( cinfo, COL_INFO, " OT=%u", ot );
+ proto_item_append_text( node, ", OT=%u", ot );
+
+ proto_tree_add_item( list, hf_cemi_ot, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+
+ *p_offset = offset;
+ return ot;
+ }
+
+ node = proto_tree_add_bytes_format( list, hf_bytes, tvb, offset, end_pos - offset, NULL, "? Object Type" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+
+ if( p_error )
+ {
+ *p_error = 1;
+ }
+
+ *p_offset = end_pos;
+ return 0;
+}
+
+/* Dissect Property Identifier (1 byte)
+*/
+static guint8 dissect_pid( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, gint ot, guint8 show, guint8 *p_error )
+{
+ gint offset = *p_offset;
+
+ if( offset < end_pos )
+ {
+ guint8 pid = tvb_get_guint8( tvb, offset );
+ column_info *cinfo = pinfo->cinfo;
+ const gchar* name;
+
+ if( pid || show )
+ {
+ col_append_fstr( cinfo, COL_INFO, " P=%u", pid );
+ proto_item_append_text( node, ", PID=%u", pid );
+ }
+
+ if( list )
+ {
+ node = proto_tree_add_item( list, hf_cemi_pid, tvb, offset, 1, ENC_BIG_ENDIAN );
+ name = get_pid_name( ot, pid );
+ if( name )
+ {
+ proto_item_append_text( node, " = %s", name );
+ }
+ }
+
+ *p_offset = ++offset;
+ return pid;
+ }
+
+ proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Property ID: expected 1 byte" );
+
+ if( p_error )
+ {
+ *p_error = 1;
+ }
+
+ return 0;
+}
+
+/* Dissect Property Index (1 byte)
+*/
+static guint8 dissect_px( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 show, guint8 *p_error )
+{
+ gint offset = *p_offset;
+
+ if( offset < end_pos )
+ {
+ guint8 px = tvb_get_guint8( tvb, offset );
+
+ if( show )
+ {
+ column_info *cinfo = pinfo->cinfo;
+ col_append_fstr( cinfo, COL_INFO, " PX=%u", px );
+ proto_item_append_text( node, ", PX=%u", px );
+ }
+
+ proto_tree_add_item( list, hf_cemi_px, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ *p_offset = ++offset;
+ return px;
+ }
+
+ proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Property Index: expected 1 byte" );
+
+ if( p_error )
+ {
+ *p_error = 1;
+ }
+
+ return 0;
+}
+
+/* Dissect Property Range (2 bytes: Number Of Elements (4 bits), Start Index (12 bits))
+ and subsequent Data
+*/
+static void dissect_range( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 pa_flags, guint8 *p_error )
+{
+ gint offset = *p_offset;
+ guint8 error = 0;
+
+ if( offset + 1 >= end_pos )
+ {
+ node = proto_tree_add_bytes_format( list, hf_bytes, tvb, offset, end_pos - offset, NULL, "? Range" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+
+ *p_offset = end_pos;
+ error = 1;
+ }
+ else
+ {
+ column_info *cinfo = pinfo->cinfo;
+ proto_item *cemi_node = node;
+ guint16 sx = tvb_get_ntohs( tvb, offset );
+ guint8 ne = sx >> 12;
+ sx &= 0x0FFF;
+
+ /* 4 bits Number Of Elements */
+ if( ne != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " N=%u", ne );
+ proto_item_append_text( node, ", N=%u", ne );
+
+ if( ne == 0 && !(pa_flags & PA_RESPONSE) )
+ {
+ error = 1;
+ }
+ else if( sx == 0 )
+ {
+ error = 2;
+ }
+ }
+
+ /* 12 bits Start Index */
+ if( sx != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " X=%u", sx );
+ proto_item_append_text( node, ", X=%u", sx );
+ }
+
+ if( list )
+ {
+ proto_item *range_node = proto_tree_add_none_format( list, hf_folder, tvb, offset, 2, "Range: %u element%s at position %u", ne, (ne == 1) ? "" : "s", sx );
+ proto_tree *range_list = proto_item_add_subtree( range_node, ett_cemi_range );
+
+ /* 4 bits Number Of Elements */
+ node = proto_tree_add_item( range_list, hf_cemi_ne, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( error )
+ {
+ proto_item_prepend_text( range_node, "? " );
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, (error == 1) ? "Expected: >= 1 element(s)" : "Expected: 1 element" );
+ }
+
+ /* 12 bits Start Index */
+ proto_tree_add_item( range_list, hf_cemi_sx, tvb, offset, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset += 2;
+
+ /* Data */
+ {
+ gint length = end_pos - offset;
+ if( length > 0 )
+ {
+ node = proto_tree_add_data( list, tvb, offset, length, cinfo, cemi_node, "Data", " $", ", $" );
+ if( !pa_flags )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Unexpected" );
+ error = 1;
+ }
+ else if( (pa_flags & PA_RESPONSE) && (!(pa_flags & PA_DATA) || ne == 0) && length != 1 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: max 1 byte" );
+ error = 1;
+ }
+ else if( pa_flags & PA_DATA )
+ {
+ if( ne == 1 && sx == 0 && length != 2 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ }
+ else if( ne >= 2 && (length % ne) != 0 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: multiple of %u bytes", ne );
+ error = 1;
+ }
+ }
+ }
+ }
+
+ *p_offset = end_pos;
+ }
+
+ if( error && p_error )
+ {
+ *p_error = 1;
+ }
+}
+
+/* Dissect Property Description: PDT (1 byte), Max Count (2 bytes), Access Levels (1 byte)
+*/
+static void dissect_prop_descr( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list, gint* p_offset, gint size, guint8* p_error )
+{
+ gint offset = *p_offset;
+ column_info* cinfo = pinfo->cinfo;
+ guint8 error = 0;
+
+ /* 4 bytes Property Description */
+ if( offset + 4 > size )
+ {
+ proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property Description" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
+
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 1 bit Writability, 1 bit reserved, 6 bits Property Data Type */
+ guint8 pdt = tvb_get_guint8( tvb, offset );
+ guint8 writable = (pdt & 0x80) != 0;
+ pdt &= 0x3F;
+ col_append_fstr( cinfo, COL_INFO, " T=%u", pdt );
+ proto_item_append_text( cemi_node, ", T=%u", pdt );
+
+ /* 4 bits reserved, 12 bits Max Elements */
+ guint16 me = tvb_get_ntohs( tvb, offset + 1) & 0x0FFF;
+ if( me != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " N=%u", me );
+ proto_item_append_text( cemi_node, ", N=%u", me );
+ }
+
+ /* 4 bits Read Access, 4 bits Write Access */
+ guint8 wa = tvb_get_guint8( tvb, offset + 3 );
+ guint8 ra = (wa & 0xF0) >> 4;
+ wa &= 0x0F;
+ col_append_fstr( cinfo, COL_INFO, " R=%u", ra );
+ if( writable )
+ col_append_fstr( cinfo, COL_INFO, " W=%u", wa );
+ proto_item_append_text( cemi_node, ", R=%u", ra );
+ if( writable )
+ proto_item_append_text( cemi_node, ", W=%u", wa );
+
+ if( cemi_list )
+ {
+ proto_item *pd_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 4, "Property Description: " );
+ proto_tree *pd_list = proto_item_add_subtree( pd_node, ett_cemi_pd );
+
+ const gchar* pdt_name = try_val_to_str( pdt, pdt_vals );
+ if( pdt_name )
+ proto_item_append_text( pd_node, "%s", pdt_name );
+ else
+ proto_item_append_text( pd_node, "PDT = 0x%02X", pdt );
+
+ if( me != 1 )
+ proto_item_append_text( pd_node, ", Max Elements = %u", me );
+
+ proto_item_append_text( pd_node, ", Read = %u", ra );
+ if( writable )
+ proto_item_append_text( pd_node, ", Write = %u", wa );
+
+ proto_tree_add_item( pd_list, hf_cemi_pw, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( pd_list, hf_cemi_pdt, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( pd_list, hf_cemi_me, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
+ proto_tree_add_item( pd_list, hf_cemi_ra, tvb, offset + 3, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( pd_list, hf_cemi_wa, tvb, offset + 3, 1, ENC_BIG_ENDIAN );
+ }
+
+ offset += 4;
+ }
+
+ if( error && p_error )
+ {
+ *p_error = 1;
+ }
+
+ *p_offset = offset;
+}
+
+/* Dissect OT (2 bytes), OI (12 bits), and PID (12 bits) for PropertyExt services
+*/
+static void dissect_pid_ext( tvbuff_t *tvb, packet_info *pinfo, proto_item *cemi_node, proto_tree *cemi_list, gint *p_offset, gint size, guint8 *p_error )
+{
+ gint offset = *p_offset;
+ column_info* cinfo = pinfo->cinfo;
+ guint8 error = 0;
+
+ /* 2 bytes Object Type */
+ guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+
+ if( offset + 3 > size )
+ {
+ proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Object Instance, PID" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
+
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 12 bits Object Instance */
+ guint16 cc = tvb_get_ntohs( tvb, offset ) >> 4;
+ col_append_fstr( cinfo, COL_INFO, " OI=%u", cc );
+ proto_item_append_text( cemi_node, ", OI=%u", cc );
+ proto_tree_add_item( cemi_list, hf_cemi_ext_oi, tvb, offset, 2, ENC_BIG_ENDIAN );
+ ++offset;
+
+ /* 12 bits Property ID */
+ cc = tvb_get_ntohs( tvb, offset ) & 0x0FFF;
+ col_append_fstr( cinfo, COL_INFO, " P=%u", cc );
+ proto_item_append_text( cemi_node, ", PID=%u", cc );
+
+ if( cemi_list )
+ {
+ proto_item* node = proto_tree_add_item( cemi_list, hf_cemi_ext_pid, tvb, offset, 2, ENC_BIG_ENDIAN );
+ const gchar* name = get_pid_name( ot, cc );
+ if( name )
+ {
+ proto_item_append_text( node, " = %s", name );
+ }
+ }
+
+ offset += 2;
+ }
+
+ if( error && p_error )
+ {
+ *p_error = 1;
+ }
+
+ *p_offset = offset;
+}
+
+/* Dissect cEMI Management packet
+ (M_PropRead.req, M_PropRead.con, M_PropWrite.req, M_PropWrite.con, M_PropInfo.ind, M_Reset.req, M_Reset.ind)
+*/
+static void dissect_cemi_mgmt_packet( tvbuff_t* tvb, packet_info* pinfo, proto_item *cemi_node, proto_tree *cemi_list, guint8 mc, gint *p_offset, gint size, guint8* p_pa_flags, guint8 *p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ guint8 min_size = 1;
+
+ switch( mc )
+ {
+ case CEMI_M_PROPREAD_REQ:
+ pa_flags = 0;
+ goto case_CEMI_M_PROP;
+
+ case CEMI_M_PROPWRITE_CON:
+ pa_flags = PA_RESPONSE;
+ goto case_CEMI_M_PROP;
+
+ case CEMI_M_PROPREAD_CON:
+ pa_flags = PA_RESPONSE | PA_DATA;
+ goto case_CEMI_M_PROP;
+
+ case CEMI_M_PROPWRITE_REQ:
+ case CEMI_M_PROPINFO_IND:
+ //pa_flags = PA_DATA;
+
+ case_CEMI_M_PROP:
+ min_size = 7;
+ break;
+
+ case CEMI_M_FUNCPROPCMD_REQ:
+ case CEMI_M_FUNCPROPREAD_REQ:
+ case CEMI_M_FUNCPROP_CON:
+ //pa_flags = PA_DATA;
+ min_size = 5;
+ break;
+
+ case CEMI_M_RESET_REQ:
+ case CEMI_M_RESET_IND:
+ pa_flags = 0;
+ break;
+ }
+
+ if( min_size >= 5 )
+ {
+ /* 2 bytes Object Type */
+ guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+
+ /* 1 byte Object Instance */
+ if( size < 4 )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Object Instance: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ guint8 oi = tvb_get_guint8( tvb, 3 );
+ if( oi != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " OI=%u", oi );
+ proto_item_append_text( cemi_node, ", OI=%u", oi );
+ }
+ proto_tree_add_item( cemi_list, hf_cemi_oi, tvb, 3, 1, ENC_BIG_ENDIAN );
+ offset = 4;
+ }
+
+ /* 1 byte Property ID
+ */
+ dissect_pid( tvb, pinfo, cemi_node, cemi_list, &offset, size, ot, 1, &error );
+
+ if( min_size >= 7 )
+ {
+ /* Range (Start Index, Number Of Elements) and Data
+ */
+ dissect_range( tvb, pinfo, cemi_node, cemi_list, &offset, size, pa_flags, &error );
+ pa_flags = 0;
+ }
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_MemoryExt service */
+static void dissect_memory_ext_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ guint16 ax, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ /* 4 bytes Range (1 byte Memory Length, 3 bytes Memory Address) */
+ if( offset + 4 > size )
+ {
+ proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 1 byte Memory Length or Error Code */
+ guint8 is_response = (ax == AX_MemExtReadResp || ax == AX_MemExtWriteResp);
+ guint8 n = tvb_get_guint8( tvb, offset );
+ if( is_response )
+ {
+ if( n != 0 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " E=$%02X", n );
+ proto_item_append_text( cemi_node, ", E=$%02X", n );
+ }
+ }
+ else
+ {
+ if( n != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " N=%u", n );
+ proto_item_append_text( cemi_node, ", N=%u", n );
+ }
+ }
+
+ /* 3 bytes Memory Address */
+ guint32 x = tvb_get_guint24( tvb, offset + 1, ENC_BIG_ENDIAN );
+ col_append_fstr( cinfo, COL_INFO, " X=$%06" PRIX32, x );
+ proto_item_append_text( cemi_node, ", X=$%06" PRIX32, x );
+
+ if( is_response )
+ {
+ proto_tree_add_item( cemi_list, hf_cemi_error, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ else
+ {
+ proto_tree_add_item( cemi_list, hf_cemi_ext_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+
+ proto_tree_add_item( cemi_list, hf_cemi_ext_memory_address, tvb, offset + 1, 3, ENC_BIG_ENDIAN );
+
+ offset += 4;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_UserMemory service */
+static void dissect_user_memory_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ proto_tree* list;
+
+ /* 3 bytes Range (Memory Length, Memory Address) */
+ if( offset + 3 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ guint8 c2 = tvb_get_guint8( tvb, offset );
+ guint8 c1 = c2 >> 4;
+ guint32 c3 = tvb_get_ntohs( tvb, offset + 1 );
+ c2 &= 0x0F;
+ c3 |= c1 << 16UL;
+ if( c2 != 1 )
+ col_append_fstr( cinfo, COL_INFO, " N=%u", c2 );
+ col_append_fstr( cinfo, COL_INFO, " X=$%05X", c3 );
+ if( tree )
+ {
+ if( c2 != 1 )
+ proto_item_append_text( cemi_node, ", N=%u", c2 );
+ proto_item_append_text( cemi_node, ", X=$%05X", c3 );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1,
+ "Range: %u byte%s at address $%05X", c2, (c2 == 1) ? "" : "s", c3 );
+ list = proto_item_add_subtree( node, ett_cemi_range );
+ proto_tree_add_item( list, hf_cemi_memory_address_ext, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
+ }
+ offset += 3;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_FunctionProperty service */
+static void dissect_function_property_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_error )
+{
+ /* 1 byte Object Index */
+ dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
+
+ /* 1 byte Property ID */
+ dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, 1, p_error );
+}
+
+/* Dissect (obsolete) A_Router service */
+static void dissect_router_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ proto_tree* list;
+
+ /* 3 bytes Range (1 byte Memory Length, 2 bytes Memory Address) */
+ if( offset + 3 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ guint8 c = tvb_get_guint8( tvb, offset );
+ guint16 cc = tvb_get_ntohs( tvb, offset + 1 );
+ if( c != 1 )
+ col_append_fstr( cinfo, COL_INFO, " N=%u", c );
+ col_append_fstr( cinfo, COL_INFO, " X=$%04X", cc );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", N=%u, X=$%04X", c, cc );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3,
+ "Range: %u byte%s at address $%04X", c, (c == 1) ? "" : "s", cc );
+ list = proto_item_add_subtree( node, ett_cemi_range );
+ proto_tree_add_item( list, hf_cemi_ext_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_ext_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
+ }
+ offset += 3;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_Authenticate or A_Key service */
+static void dissect_authenticate_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint16 ax, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ /* 1 byte Level */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Level: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ guint8 c = tvb_get_guint8( tvb, offset );
+ if( ax != AX_AuthReq || c != 0 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " L=%u", c );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", L=%u", c );
+ }
+ }
+ if( tree )
+ {
+ proto_tree_add_item( cemi_list, hf_cemi_level, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ offset++;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_PropertyValue service */
+static void dissect_property_value_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ /* 1 byte Object Index */
+ dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
+
+ /* 1 byte Property ID */
+ dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, 1, p_error );
+
+ /* 2 bytes Range */
+ dissect_range( tvb, pinfo, cemi_node, cemi_list, p_offset, size, *p_pa_flags, p_error );
+}
+
+/* Dissect A_PropertyDescription service */
+static void dissect_property_description_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ /* 1 byte Object Index */
+ dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
+
+ /* 1 byte Property ID */
+ {
+ guint8 pa_flags = *p_pa_flags;
+ guint8 pid = dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, pa_flags, p_error );
+
+ /* 1 byte Property Index */
+ dissect_px( tvb, pinfo, cemi_node, cemi_list, p_offset, size, pa_flags || !pid, p_error );
+
+ if( pa_flags ) /* A_PropertyDescription_Response */
+ {
+ /* 1 byte PDT, 2 bytes Max Elements, 1 byte Access Levels */
+ dissect_prop_descr( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
+
+ /* No further trailing data */
+ *p_pa_flags = 0;
+ }
+ }
+}
+
+/* Dissect A_NetworkParameter or A_GroupPropertyValue service */
+static void dissect_network_parameter_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_error )
+{
+ /* 2 bytes Object Type */
+ guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
+
+ /* 1 byte Property ID */
+ dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, ot, 1, p_error );
+}
+
+/* Dissect A_IndividualAddressSerialNumber or A_DomainAddressSerialNumber service */
+static void dissect_ia_serial_number_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+
+ /* 6 bytes Serial Nr */
+ if( offset + 6 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Serial Number" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, "Serial Number", " SN=$", ", SerNr=$" );
+ offset += 6;
+ }
+
+ if( pa_flags )
+ {
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Data: missing" );
+ error = 1;
+ }
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_SystemNetworkParameter service */
+static void dissect_system_network_parameter_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ const gchar* name;
+ guint16 ot;
+ guint16 cc;
+ guint8 c;
+
+ /* 2 bytes Object Type */
+ if( offset + 1 >= size )
+ {
+ ot = 0;
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Object Type" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ ot = cc = tvb_get_ntohs( tvb, offset );
+
+ if( cc )
+ {
+ col_append_fstr( cinfo, COL_INFO, " OT=%u", cc );
+ proto_item_append_text( cemi_node, ", OT=%u", cc );
+ }
+
+ if( cemi_list )
+ {
+ node = proto_tree_add_item( cemi_list, hf_cemi_ot, tvb, offset, 2, ENC_BIG_ENDIAN );
+ name = try_val_to_str( cc, ot_vals );
+ if( name )
+ {
+ proto_item_append_text( node, " = %s", name );
+ }
+ }
+
+ offset += 2;
+ }
+
+ /* 2 bytes Property ID (12 bits) and Reserved (4 bits) */
+ if( offset + 1 >= size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property ID" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 12 bits Property ID */
+ cc = tvb_get_ntohs( tvb, offset );
+ c = cc & 0x000F;
+ cc >>= 4;
+
+ col_append_fstr( cinfo, COL_INFO, " P=%u", cc );
+ proto_item_append_text( cemi_node, ", PID=%u", cc );
+
+ if( cemi_list )
+ {
+ node = proto_tree_add_item( cemi_list, hf_cemi_snp_pid, tvb, offset, 2, ENC_BIG_ENDIAN );
+ name = get_pid_name( ot, cc );
+ if( name )
+ {
+ proto_item_append_text( node, " = %s", name );
+ }
+ }
+
+ ++offset;
+
+ /* 4 bits Reserved */
+ if( c )
+ {
+ col_append_fstr( cinfo, COL_INFO, " $%X", c );
+ proto_item_append_text( cemi_node, ", $%X", c );
+ node = proto_tree_add_item( cemi_list, hf_cemi_snp_reserved, tvb, offset, 1, ENC_BIG_ENDIAN );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
+ error = 1;
+ }
+
+ ++offset;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_PropertyExtValue service */
+static void dissect_property_ext_value_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+
+ /* 2 bytes OT, 12 bits OI, 12 bits PID */
+ dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+
+ /* 3 bytes Range (1 byte Count, 2 bytes Index) */
+ if( offset + 3 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 1 byte Count */
+ guint8 ne = tvb_get_guint8( tvb, offset );
+ if( ne != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " N=%u", ne );
+ proto_item_append_text( cemi_node, ", N=%u", ne );
+ }
+
+ /* 2 bytes Index */
+ guint16 sx = tvb_get_ntohs( tvb, offset + 1 );
+ if( sx != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " X=%u", sx );
+ proto_item_append_text( cemi_node, ", X=%u", sx );
+ }
+
+ if( cemi_list )
+ {
+ proto_item *range_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3, "Range: %u element%s at position %u", ne, (ne == 1) ? "" : "s", sx );
+ proto_tree *range_list = proto_item_add_subtree( range_node, ett_cemi_range );
+ proto_tree_add_item( range_list, hf_cemi_ext_ne, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( range_list, hf_cemi_ext_sx, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset += 3;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_PropertyExtDescription service */
+static void dissect_property_ext_description_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ guint16 cc;
+ guint8 c;
+
+ /* 2 bytes OT, 12 bits OI, 12 bits PID */
+ dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+
+ /* 4 bits Description Type */
+ if( offset >= size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Description Type" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bits" );
+ error = 1;
+ }
+ else
+ {
+ c = tvb_get_guint8( tvb, offset ) >> 4;
+ col_append_fstr( cinfo, COL_INFO, " D=%u", c );
+ proto_item_append_text( cemi_node, ", D=%u", c );
+ proto_tree_add_item( cemi_list, hf_cemi_ext_dt, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+
+ /* 12 bits Property Index */
+ if( offset + 2 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property Index" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 12 bits" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ cc = tvb_get_ntohs( tvb, offset ) & 0x0FFF;
+ col_append_fstr( cinfo, COL_INFO, " PX=%u", cc );
+ proto_item_append_text( cemi_node, ", PX=%u", cc );
+ proto_tree_add_item( cemi_list, hf_cemi_ext_px, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+ }
+
+ if( pa_flags ) /* AX_PropExtDescrResp */
+ {
+ /* 4 bytes DPT (2 bytes DPT Major, 2 bytes DPT Minor) */
+ if( offset + 4 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Data Point Type" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ guint16 dpt_major = tvb_get_ntohs( tvb, offset );
+ guint16 dpt_minor = tvb_get_ntohs( tvb, offset + 2 );
+
+ if( cemi_list )
+ {
+ proto_item *dpt_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "Data Point Type: %u.%u", dpt_major, dpt_minor );
+ proto_tree *dpt_list = proto_item_add_subtree( dpt_node, ett_cemi_dpt );
+ proto_tree_add_item( dpt_list, hf_cemi_dpt_major, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_tree_add_item( dpt_list, hf_cemi_dpt_minor, tvb, offset + 2, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset += 4;
+
+ if( dpt_major || dpt_minor )
+ {
+ col_append_fstr( cinfo, COL_INFO, " DPT=%u.%u", dpt_major, dpt_minor );
+ proto_item_append_text( cemi_node, ", DPT=%u.%u", dpt_major, dpt_minor );
+ }
+ }
+
+ /* 1 byte PDT, 2 bytes Max Elements, 1 byte Access Levels */
+ dissect_prop_descr( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+
+ /* No further trailing data */
+ pa_flags = 0;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect A_DataSecurity service */
+static void dissect_data_security_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
+ const gchar* name, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ proto_tree* root_tree = tree;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ proto_tree* list;
+
+ // 1 byte SCF, 6 bytes SeqNr, ...
+ // and either another SeqNr for sync or Apci+Mac (2+4 bytes) for data.
+ if( offset + 13 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? SCF, SeqNr, ..." );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: min 13 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* 1 byte SCF */
+ guint8 scf = tvb_get_guint8( tvb, offset );
+ guint8 is_sync = (scf & 6) == 0x02;
+ guint8 is_sync_req = is_sync && (scf & 1) == 0;
+ guint8 is_sync_res = is_sync && !is_sync_req;
+ guint64 seq_nr;
+
+ name = try_val_to_str( scf, scf_short_vals );
+ if( !name ) name = "?";
+ col_append_fstr( cinfo, COL_INFO, " %s", name );
+ proto_item_append_text( cemi_node, ", %s", name );
+
+ node = proto_tree_add_item( cemi_list, hf_cemi_scf, tvb, offset, 1, ENC_BIG_ENDIAN );
+ list = proto_item_add_subtree( node, ett_cemi_scf );
+ proto_tree_add_item( list, hf_cemi_scf_t, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_scf_sai, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_scf_sbc, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_scf_svc, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ ++offset;
+
+ /* 6 bytes SeqNr */
+ name = is_sync_req ? "SeqNrLocal" : is_sync_res ? "Challenge" : "SeqNr";
+ seq_nr = tvb_get_ntoh48( tvb, offset );
+ proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, name, NULL, is_sync_res ? NULL : ", SeqNrLocal=$" );
+ offset += 6;
+
+ if( is_sync )
+ {
+ /* 6 bytes SyncReq SerNr or SyncRes SeqNrRemote */
+ name = is_sync_req ? "SerNr" : "SeqNrRemote";
+ proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, name, NULL, is_sync_res ? ", SeqNrRemote=$" : NULL );
+ offset += 6;
+
+ /* 6 bytes SyncReq Challenge or SyncRes SeqNrLocal */
+ name = is_sync_req ? "Challenge" : "SeqNrLocal";
+ if( offset + 6 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "%s", name );
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ proto_tree_add_data( cemi_list, tvb, offset, 6, NULL, NULL, name, NULL, NULL );
+ offset += 6;
+
+ if( offset < size )
+ {
+ /* 4 bytes MAC */
+ node = proto_tree_add_data( cemi_list, tvb, offset, size - offset, NULL, NULL, "Message Authentication Code", NULL, NULL );
+ if( offset + 4 != size )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
+ error = 1;
+ }
+ offset = size;
+ }
+ }
+ }
+ else // Data
+ {
+ struct data_security_info info;
+ struct knx_keyring_ia_seqs* ia_seq;
+ const guint8* cemi;
+ const guint8* encrypted;
+ gint encrypted_size;
+ const guint8* decrypted;
+ proto_item* item;
+
+ info.source = source_addr;
+ info.dest = dest_addr;
+ info.multicast = !unicast;
+ info.seq_nr = seq_nr;
+ *info.output_text = '\0';
+
+ if( !unicast ) // multicast or broadcast
+ {
+ // Check sending IA
+ guint8 ga_found = 0;
+ guint8 ia_ok = 0;
+ struct knx_keyring_ga_senders* ga_sender = knx_keyring_ga_senders;
+ for( ; ga_sender; ga_sender = ga_sender->next )
+ {
+ if( ga_sender->ga == dest_addr )
+ {
+ ga_found = 1;
+
+ if( ga_sender->ia == source_addr )
+ {
+ ia_ok = 1;
+ break;
+ }
+ }
+ }
+
+ if( !ia_ok )
+ {
+ if( ga_found )
+ {
+ expert_add_info_format( pinfo, source_node, KIP_ERROR, "Unknown sender" );
+ error = 1;
+ }
+ else
+ {
+ expert_add_info_format( pinfo, dest_node, KIP_WARNING, "Unknown group address" );
+ }
+ }
+ }
+
+ // Check SeqNr
+ for( ia_seq = knx_keyring_ia_seqs; ia_seq; ia_seq = ia_seq->next )
+ {
+ if( ia_seq->ia == source_addr )
+ {
+ if( ia_seq->seq > seq_nr )
+ {
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: min $%012" PRIX64, ia_seq->seq );
+ break;
+ }
+ }
+ }
+
+ // Get encrypted data.
+ cemi = tvb_get_ptr( tvb, 0, size );
+ encrypted = cemi + offset;
+ encrypted_size = size - offset;
+
+ // Decrypt.
+ decrypted = decrypt_data_security_data( pinfo->pool, encrypted, encrypted_size, cemi, size, &info );
+
+ if( decrypted )
+ {
+ tvbuff_t* tvb2 = tvb_new_child_real_data( tvb, decrypted, encrypted_size, encrypted_size );
+ gint size2 = encrypted_size - 4; // > 0, guaranteed by decrypt_data_security_data
+ proto_item_append_text( cemi_node, ", MAC OK" );
+ //tvb_set_free_cb(tvb2, wmem_free);
+ add_new_data_source( pinfo, tvb2, "Decrypted" );
+
+ item = proto_tree_add_none_format( cemi_list, hf_folder, tvb2, 0, encrypted_size, "Decrypted" );
+ tree = proto_item_add_subtree( item, ett_cemi_decrypted );
+
+ if( *info.output_text )
+ {
+ proto_item_append_text( item, " (%s)", info.output_text );
+ }
+
+ proto_tree_add_data( tree, tvb2, 0, size2, NULL, NULL, "Embedded APDU", NULL, NULL );
+ proto_tree_add_data( tree, tvb2, size2, 4, NULL, NULL, "Message Authentication Code", NULL, NULL );
+
+ /* Dissect embedded APDU */
+ {
+ // Hack: To save us from splitting another sub dissector which only
+ // dissects the Apci+Apdu
+ // we synthesize a telegram from the outer ApciSec telegram fields and the inner
+ // decrypted apci+apdu and then we dissect this as a new cEMI frame.
+ gint innerTelegramSize = size - 13; // > 0, already checked above
+ gint additionalInfoLength = cemi[ 1 ]; // cemi size > 13, already checked above
+ gint offsetToApci = additionalInfoLength + 9;
+ if( offsetToApci < size )
+ {
+ if( offsetToApci + size2 <= innerTelegramSize )
+ {
+ guint8* innerTelegram = (guint8*) wmem_alloc( pinfo->pool, innerTelegramSize );
+
+ memcpy( innerTelegram, cemi, offsetToApci );
+ memcpy( innerTelegram + offsetToApci, decrypted, size2 );
+ innerTelegram[ additionalInfoLength + 8 ] = (guint8) (size2 - 1);
+
+ tvbuff_t* tvb3 = tvb_new_child_real_data( tvb, innerTelegram, innerTelegramSize, innerTelegramSize );
+ //tvb_set_free_cb(tvb3, wmem_free);
+ add_new_data_source( pinfo, tvb3, "Inner Decrypted Telegram" );
+
+ dissector_handle_t cemi_handle = find_dissector( "cemi" );
+ if( cemi_handle )
+ {
+ call_dissector( cemi_handle, tvb3, pinfo, root_tree );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Could not be decrypted.
+ proto_item_append_text( cemi_node, ", Could not be decrypted" );
+
+ if( *info.output_text )
+ {
+ proto_item_append_text( cemi_node, " (%s)", info.output_text );
+ }
+ }
+
+ offset = size;
+ }
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect extended AL service (10 bit AL service code)
+*/
+static void dissect_extended_app_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
+ guint16 ax, const gchar* name,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node = NULL;
+ proto_tree* list = NULL;
+
+ col_append_fstr( cinfo, COL_INFO, " %s", name );
+
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", %s", name );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "APCI: %s", name );
+ list = proto_item_add_subtree( node, ett_cemi_apci );
+ proto_tree_add_item( list, hf_cemi_ax, tvb, offset, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset += 2;
+
+ pa_flags = PA_RESPONSE | PA_DATA;
+
+ switch( ax )
+ {
+ case AX_UserMemRead:
+ case AX_MemExtRead:
+ case AX_RoutingTableRead:
+ case AX_RouterMemRead:
+ case AX_PropValueRead:
+ case AX_PropDescrRead:
+ case AX_IndAddrSerNumRead:
+ case AX_DomAddrSerNumRead:
+ case AX_PropExtValueRead:
+ case AX_PropExtDescrRead:
+ pa_flags = 0;
+ break;
+ }
+
+ switch( ax )
+ {
+ case AX_MemExtRead:
+ case AX_MemExtReadResp:
+ case AX_MemExtWrite:
+ case AX_MemExtWriteResp:
+ dissect_memory_ext_service( tvb, pinfo, cemi_node, cemi_list, ax, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_UserMemRead:
+ case AX_UserMemResp:
+ case AX_UserMemWrite:
+ case AX_UserMemBitWrite:
+ dissect_user_memory_service( tvb, pinfo, tree, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_FuncPropCmd:
+ case AX_FuncPropRead:
+ case AX_FuncPropResp:
+ dissect_function_property_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+ break;
+
+ case AX_RoutingTableRead:
+ case AX_RouterMemRead:
+ case AX_RoutingTableResp:
+ case AX_RoutingTableWrite:
+ case AX_RouterMemResp:
+ case AX_RouterMemWrite:
+ case AX_MemBitWrite:
+ dissect_router_service( tvb, pinfo, tree, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_AuthReq:
+ case AX_AuthResp:
+ case AX_KeyWrite:
+ case AX_KeyResp:
+ dissect_authenticate_service( tvb, pinfo, tree, cemi_node, cemi_list, ax, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_PropValueRead:
+ case AX_PropValueResp:
+ case AX_PropValueWrite:
+ dissect_property_value_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_PropDescrRead:
+ case AX_PropDescrResp:
+ dissect_property_description_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_NwkParamRead:
+ case AX_NwkParamResp:
+ case AX_NwkParamWrite:
+ case AX_GroupPropValueRead:
+ case AX_GroupPropValueResp:
+ case AX_GroupPropValueWrite:
+ case AX_GroupPropValueInfo:
+ dissect_network_parameter_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+ break;
+
+ case AX_IndAddrSerNumRead:
+ case AX_DomAddrSerNumRead:
+ case AX_IndAddrSerNumResp:
+ case AX_IndAddrSerNumWrite:
+ case AX_DomAddrSerNumResp:
+ case AX_DomAddrSerNumWrite:
+ dissect_ia_serial_number_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_SysNwkParamRead:
+ case AX_SysNwkParamResp:
+ case AX_SysNwkParamWrite:
+ dissect_system_network_parameter_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_PropExtValueRead:
+ case AX_PropExtValueResp:
+ case AX_PropExtValueWriteCon:
+ case AX_PropExtValueWriteConRes:
+ case AX_PropExtValueWriteUnCon:
+ dissect_property_ext_value_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_PropExtDescrRead:
+ case AX_PropExtDescrResp:
+ dissect_property_ext_description_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
+ break;
+
+ case AX_FuncPropExtCmd:
+ case AX_FuncPropExtRead:
+ case AX_FuncPropExtResp:
+
+ /* 2 bytes OT, 12 bits OI, 12 bits PID */
+ dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
+ break;
+
+ case AX_DataSec:
+ dissect_data_security_service( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast,
+ name, &offset, size, &pa_flags, &error );
+ break;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect simple AL service (4 bit AL service code)
+*/
+static void dissect_simple_app_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint8 ac, guint8 ad, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node = NULL;
+ proto_tree* list = NULL;
+
+ guint8 c;
+ guint16 cc;
+
+ const gchar* name = val_to_str( ac, ac_vals, "AC=%u" );
+ col_append_fstr( cinfo, COL_INFO, " %s", name );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", %s", name );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "APCI: %s", name );
+ list = proto_item_add_subtree( node, ett_cemi_apci );
+ proto_tree_add_item( list, hf_cemi_ac, tvb, offset, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset++;
+
+ switch( ac )
+ {
+ case AC_GroupValueRead:
+ case AC_MemRead:
+ case AC_AdcRead:
+ case AC_DevDescrRead:
+ pa_flags = 0;
+ break;
+ }
+
+ switch( ac )
+ {
+ case AC_GroupValueRead:
+ case AC_GroupValueResp:
+ case AC_GroupValueWrite:
+ case AC_Restart:
+ {
+ guint8 expected = ((pa_flags && offset + 1 >= size) || ac == AC_Restart);
+
+ if( expected || ad != 0 )
+ {
+ /* Show APCI 6-bit data
+ */
+ if( !expected )
+ {
+ error = 1;
+ }
+ else if( ad != 0 || ac != AC_Restart || offset + 1 < size )
+ {
+ col_append_fstr( cinfo, COL_INFO, " $%02X", ad );
+ proto_item_append_text( cemi_node, " $%02X", ad );
+ }
+
+ if( tree )
+ {
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Data: %02X", ad );
+ list = proto_item_add_subtree( node, ett_cemi_apci );
+ proto_tree_add_item( list, hf_cemi_ad, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( !expected )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 0x00" );
+ }
+ }
+ }
+ }
+ break;
+
+ case AC_MemRead:
+ case AC_MemResp:
+ case AC_MemWrite:
+
+ /* 6 bits Memory Length, 2 bytes Memory Address */
+ if( offset + 3 > size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset + 1, size - offset - 1, NULL, "? Memory Address" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size - 1;
+ }
+ else
+ {
+ cc = tvb_get_ntohs( tvb, offset + 1 );
+ if( ad != 1 )
+ col_append_fstr( cinfo, COL_INFO, " N=%u", ad );
+ col_append_fstr( cinfo, COL_INFO, " X=$%04X", cc );
+ if( tree )
+ {
+ if( ad != 1 )
+ proto_item_append_text( cemi_node, ", N=%u", ad );
+ proto_item_append_text( cemi_node, ", X=$%04X", cc );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3, "Range: %u byte%s at address $%04X", ad, (ad == 1) ? "" : "s", cc );
+ list = proto_item_add_subtree( node, ett_cemi_range );
+ proto_tree_add_item( list, hf_cemi_ad_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
+ }
+ offset += 2;
+ }
+ break;
+
+ case AC_AdcRead:
+ case AC_AdcResp:
+
+ /* 6 bits Channel */
+ col_append_fstr( cinfo, COL_INFO, " #%u", ad );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, " #%u", ad );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Channel: %u", ad );
+ list = proto_item_add_subtree( node, ett_cemi_apci );
+ proto_tree_add_item( list, hf_cemi_ad_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ ++offset;
+
+ /* 1 byte Count */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Count: expected 1 byte" );
+ error = 1;
+ --offset;
+ }
+ else
+ {
+ c = tvb_get_guint8( tvb, offset );
+ if( c != 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " N=%u", c );
+ proto_item_append_text( cemi_node, ", N=%u", c );
+ }
+ proto_tree_add_item( cemi_list, hf_cemi_adc_count, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ break;
+
+ case AC_DevDescrRead:
+ case AC_DevDescrResp:
+
+ /* 6 bits Descriptor Type */
+ if( ad != 0 )
+ col_append_fstr( cinfo, COL_INFO, " #%u", ad );
+ if( tree )
+ {
+ if( ad != 0 )
+ proto_item_append_text( cemi_node, " #%u", ad );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Descriptor Type: %u", ad );
+ list = proto_item_add_subtree( node, ett_cemi_apci );
+ proto_tree_add_item( list, hf_cemi_ad_type, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ break;
+
+ case AC_UserMsg:
+ case AC_Escape:
+
+ /* 6 bits Data */
+ col_append_fstr( cinfo, COL_INFO, " #%u", ad );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, " #%u", ad );
+ proto_item_append_text( node, " $%02X", ad );
+ proto_tree_add_item( list, hf_cemi_ad, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ break;
+ }
+
+ offset++;
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect cEMI Application Layer
+*/
+static void dissect_cemi_app_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ /* 10 bits APCI
+ */
+ if( offset + 1 >= size )
+ {
+ proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? APCI" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ /* Extract and split AL service code */
+ guint8 tb = tvb_get_guint8( tvb, offset );
+ guint8 ab = tvb_get_guint8( tvb, offset + 1 );
+
+ /* 4 bits simple AL service code */
+ guint8 ac = ((tb & 0x03) << 2) | ((ab & 0xC0) >> 6);
+
+ /* 6 bits data */
+ guint8 ad = ab & 0x3F;
+
+ /* 10 = 4 + 6 bits extended AL service code */
+ guint16 ax = (ac << 6) | ad;
+
+ const gchar* name = try_val_to_str( ax, ax_vals );
+
+ if( name ) /* Extended AL code (10 bits) */
+ {
+ dissect_extended_app_service( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast,
+ ax, name, &offset, size, &pa_flags, &error );
+ }
+ else /* Simple AL code (4 bits) followed by data (6 bits) */
+ {
+ dissect_simple_app_service( tvb, pinfo, tree, cemi_node, cemi_list, ac, ad, &offset, size, &pa_flags, &error );
+ }
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect cEMI Transport Layer
+*/
+static void dissect_cemi_transport_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
+ guint8 is_tdata, guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
+ gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node;
+ const gchar* name;
+ gchar text[ 128 ];
+ guint8 c;
+
+ /* 6 bits TPCI */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? TPCI: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ guint8 tb = tvb_get_guint8( tvb, offset );
+ proto_item *tpci_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "TPCI" );
+ proto_tree *tpci_list = proto_item_add_subtree( tpci_node, ett_cemi_tpci );
+ guint8 tpci_error = 0;
+
+ node = proto_tree_add_item( tpci_list, hf_cemi_tpt, tvb, offset, 1, ENC_BIG_ENDIAN );
+ if( is_tdata && (tb & 0x80) )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
+ tpci_error = 1;
+ }
+
+ node = proto_tree_add_item( tpci_list, hf_cemi_tst, tvb, offset, 1, ENC_BIG_ENDIAN );
+ if( is_tdata && (tb & 0x40) )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
+ tpci_error = 1;
+ }
+
+ c = (tb & 0x3C) >> 2;
+
+ if( c || tb & 0x40 ) /* Numbered Packet? */
+ {
+ node = proto_tree_add_item( tpci_list, hf_cemi_num, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_item_append_text( tpci_node, ", SeqNum = %u", c );
+ if( !(tb & 0x40) )
+ {
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
+ tpci_error = 1;
+ }
+ }
+
+ if( tb & 0x80 ) /* Control Packet */
+ {
+ /* 2 bits TPCI Code */
+ guint8 tc = tb & 0x03;
+ name = try_val_to_str( tc, tc_vals );
+ if( !name )
+ {
+ snprintf( text, sizeof text, "TC=%u", tc );
+ name = text;
+ }
+ col_append_fstr( cinfo, COL_INFO, " %s", name );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", %s", name );
+ proto_item_append_text( tpci_node, ": %s", name );
+ proto_tree_add_item( tpci_list, hf_cemi_tc, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+ }
+
+ if( tpci_error )
+ {
+ proto_item_prepend_text( tpci_node, "? " );
+ error = 1;
+ }
+
+ if( tb & 0x80 ) /* Control Packet */
+ {
+ pa_flags = 0;
+ offset++;
+ }
+ else /* Data Packet */
+ {
+ /* APCI etc */
+ dissect_cemi_app_layer( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast, &offset, size, &pa_flags, &error );
+ }
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+/* Dissect cEMI Link Layer
+ (typically L_Data or T_Data)
+*/
+static void dissect_cemi_link_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list, guint8 mc, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ guint8 pa_flags = *p_pa_flags;
+ guint8 error = *p_error;
+
+ proto_item* node = NULL;
+ proto_tree* list = NULL;
+
+ const gchar* name;
+ gchar text[ 128 ];
+ guint8 c;
+
+ guint8 is_tdata = 0;
+ guint8 is_ldata = 0;
+ guint16 source_addr = 0;
+ guint16 dest_addr = 0;
+ guint8 unicast = 0;
+
+ proto_item* source_node = NULL;
+ proto_item* dest_node = NULL;
+
+ proto_item* ai_node;
+ proto_tree* ai_list;
+
+ if( size < 2 )
+ {
+ ai_list = proto_tree_add_subtree( cemi_list, tvb, offset, 0, ett_cemi_ai, &ai_node, "? Additional Info" );
+ proto_tree_add_expert_format( ai_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
+ offset = size;
+ error = 1;
+ }
+ else
+ {
+ /* Additional Information */
+ guint8 ai_len = tvb_get_guint8( tvb, 1 );
+ gint ai_end = 2 + ai_len;
+ gint ai_size = ai_len;
+
+ if( ai_end > size )
+ {
+ error = 2;
+ ai_size = size - 2;
+ ai_end = size;
+ }
+
+ ai_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, 1, ai_size + 1, "Additional Info (%u bytes)", ai_len );
+ ai_list = proto_item_add_subtree( ai_node, ett_cemi_ai );
+ node = proto_tree_add_item( ai_list, hf_cemi_ai_length, tvb, 1, 1, ENC_BIG_ENDIAN );
+
+ if( error == 2 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", ai_size );
+ }
+
+ offset = 2;
+ while( offset < ai_end )
+ {
+ /* Additional Information Element */
+ guint8 aie_type = tvb_get_guint8( tvb, offset );
+ guint8 aie_len;
+ gint aie_size;
+ proto_item *aie_node;
+ proto_tree *aie_list;
+
+ name = try_val_to_str( aie_type, aiet_vals );
+
+ if( offset + 1 >= ai_end )
+ {
+ error = 3;
+ aie_len = 0;
+ aie_size = 1;
+ }
+ else
+ {
+ aie_len = tvb_get_guint8( tvb, offset + 1 );
+ aie_size = ai_end - offset - 2;
+ if( aie_size < aie_len )
+ {
+ error = 4;
+ }
+ else
+ {
+ aie_size = aie_len;
+ }
+ aie_size += 2;
+ }
+
+ aie_node = proto_tree_add_none_format( ai_list, hf_folder, tvb, offset, aie_size, "Additional Info: %s", name ? name : "?" );
+ aie_list = proto_item_add_subtree( aie_node, ett_cemi_aie );
+ node = proto_tree_add_item( aie_list, hf_cemi_aie_type, tvb, offset, 1, ENC_BIG_ENDIAN );
+ if( name ) proto_item_append_text( node, " = %s", name );
+ offset++;
+
+ if( error == 3 )
+ {
+ proto_item_prepend_text( aie_node, "? " );
+ proto_tree_add_expert_format( aie_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
+ break;
+ }
+
+ proto_item_append_text( aie_node, " (%u bytes)", aie_len );
+ node = proto_tree_add_item( aie_list, hf_cemi_aie_length, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ if( error == 4 )
+ {
+ proto_item_prepend_text( aie_node, "? " );
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", aie_size - 2 );
+ break;
+ }
+
+ if( aie_len > 0 )
+ {
+ proto_tree_add_data( aie_list, tvb, offset, aie_len, NULL, NULL, "Data", NULL, NULL );
+ offset += aie_len;
+ }
+ else
+ {
+ proto_item_prepend_text( aie_node, "? " );
+ proto_item_append_text( node, " (?)" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: >= 1 byte(s)" );
+ error = 5;
+ }
+ }
+
+ if( error >= 2 )
+ {
+ proto_item_prepend_text( ai_node, "? " );
+ }
+
+ offset = ai_end;
+ }
+
+ switch( mc )
+ {
+ case CEMI_L_BUSMON_IND:
+ case CEMI_L_RAW_IND:
+ case CEMI_L_RAW_REQ:
+ case CEMI_L_RAW_CON:
+ break;
+
+ default:
+
+ switch( mc )
+ {
+ case CEMI_L_DATA_REQ:
+ case CEMI_L_DATA_CON:
+ case CEMI_L_DATA_IND:
+ is_ldata = 1;
+ break;
+
+ case CEMI_T_DATA_INDIVIDUAL_REQ:
+ case CEMI_T_DATA_INDIVIDUAL_IND:
+ case CEMI_T_DATA_CONNECTED_REQ:
+ case CEMI_T_DATA_CONNECTED_IND:
+ is_tdata = 1;
+ break;
+ }
+
+ if( is_tdata )
+ {
+ gint length = (size >= offset + 6) ? 6 : size - offset;
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, length, NULL, "Reserved" );
+ if( length < 6 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ error = 1;
+ }
+ else
+ {
+ gint pos = 0;
+ for( ; pos < 6; pos++ )
+ {
+ if( tvb_get_guint8( tvb, offset + pos ) != 0 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
+ error = 1;
+ break;
+ }
+ }
+ }
+
+ is_tdata = 1;
+ offset += length;
+ }
+ else
+ {
+ /* 1 byte Control Field 1 */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Ctrl1: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ if( tree )
+ {
+ c = tvb_get_guint8( tvb, offset );
+ proto_item_append_text( cemi_node, ", " );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Ctrl1: " );
+ if( !(c & 0x80) )
+ {
+ proto_item_append_text( cemi_node, "X " );
+ proto_item_append_text( node, "Extended, " );
+ }
+ if( !(c & 0x20) )
+ {
+ proto_item_append_text( cemi_node, "R " );
+ proto_item_append_text( node, "Repeat On Error, " );
+ }
+ if( !(c & 0x10) )
+ {
+ proto_item_append_text( cemi_node, "B " );
+ proto_item_append_text( node, "System Broadcast, " );
+ }
+ if( c & 0x02 )
+ {
+ proto_item_append_text( cemi_node, "A " );
+ proto_item_append_text( node, "Ack Wanted, " );
+ }
+ if( c & 0x01 )
+ {
+ proto_item_append_text( cemi_node, "C " );
+ proto_item_append_text( node, "Unconfirmed, " );
+ }
+
+ name = try_val_to_str( (c & 0x0C) >> 2, prio_vals );
+ if( !name )
+ name = "?";
+ proto_item_append_text( cemi_node, "P=%s", name );
+ proto_item_append_text( node, "Prio = %s", name );
+ list = proto_item_add_subtree( node, ett_cemi_ctrl1 );
+ proto_tree_add_item( list, hf_cemi_ft, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_rep, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_bt, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_prio, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_ack, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_ce, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+
+ offset++;
+ }
+
+ /* 1 byte Control Field 2 */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Ctrl2: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ c = tvb_get_guint8( tvb, offset );
+
+ unicast = !(c & 0x80); /* Address Type (IA or GA) */
+
+ if( tree )
+ {
+ guint8 hc = (c & 0x70) >> 4; /* Hop Count */
+ guint8 eff = c & 0x0F; /* Extended Frame Format (0 = standard) */
+
+ snprintf( text, sizeof text, "%u", (c & 0x70) >> 4 ); /* hop count */
+ proto_item_append_text( cemi_node, ", H=%u", hc );
+ node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Ctrl2: Hops = %u", hc );
+ if( eff )
+ {
+ proto_item_append_text( cemi_node, " F=%u", eff );
+ proto_item_append_text( cemi_node, " Frame = %u", eff );
+ }
+ list = proto_item_add_subtree( node, ett_cemi_ctrl2 );
+ proto_tree_add_item( list, hf_cemi_at, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_hc, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( list, hf_cemi_eff, tvb, offset, 1, ENC_BIG_ENDIAN );
+ }
+
+ offset++;
+ }
+
+ /* 2 bytes Source Address */
+ if( offset + 1 >= size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Source" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ source_addr = tvb_get_ntohs( tvb, offset );
+ snprintf( text, sizeof text, "%u.%u.%u", (source_addr >> 12) & 0xF, (source_addr >> 8) & 0xF, source_addr & 0xFF );
+ col_append_fstr( cinfo, COL_INFO, " %s", text );
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", Src=%s", text );
+ source_node = proto_tree_add_item( cemi_list, hf_cemi_sa, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_item_append_text( source_node, " = %s", text );
+ }
+
+ offset += 2;
+ }
+
+ /* 2 bytes Destination Address */
+ if( offset + 1 >= size )
+ {
+ node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Destination" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ error = 1;
+ offset = size;
+ }
+ else
+ {
+ dest_addr = tvb_get_ntohs( tvb, offset );
+
+ if( unicast )
+ {
+ /* Individual Address */
+ snprintf( text, sizeof text, "%u.%u.%u", (dest_addr >> 12) & 0xF, (dest_addr >> 8) & 0xF, dest_addr & 0xFF );
+ }
+ else
+ {
+ /* Group Address */
+ snprintf( text, sizeof text, "%u/%u/%u", (dest_addr >> 11) & 0x1F, (dest_addr >> 8) & 0x7, dest_addr & 0xFF );
+ }
+
+ col_append_fstr( cinfo, COL_INFO, "->%s", text );
+
+ if( tree )
+ {
+ proto_item_append_text( cemi_node, ", Dst=%s", text );
+ dest_node = proto_tree_add_item( cemi_list, hf_cemi_da, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_item_append_text( dest_node, " = %s", text );
+ }
+
+ offset += 2;
+ }
+ }
+
+ if( is_ldata || is_tdata )
+ {
+ /* 1 byte NPDU Length */
+ if( offset >= size )
+ {
+ proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ guint8 data_len = tvb_get_guint8( tvb, offset );
+ node = proto_tree_add_item( cemi_list, hf_cemi_len, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( offset + 2 + data_len != size )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", size - offset - 2 );
+ error = 1;
+ }
+
+ offset++;
+ }
+
+ /* TPCI etc */
+ dissect_cemi_transport_layer( tvb, pinfo, tree, cemi_node, cemi_list, is_tdata, source_addr, source_node, dest_addr, dest_node, unicast, &offset, size, &pa_flags, &error );
+ }
+
+ break;
+ }
+
+ *p_offset = offset;
+ *p_pa_flags = pa_flags;
+ *p_error = error;
+}
+
+static gint dissect_cemi( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_ )
+{
+ gint offset = 0;
+ gint size = tvb_captured_length_remaining( tvb, 0 );
+ guint8 error = 0;
+ column_info* cinfo = pinfo->cinfo;
+
+ /* cEMI node in tree view */
+ proto_item* cemi_node = proto_tree_add_item( tree, proto_cemi, tvb, 0, size, ENC_BIG_ENDIAN );
+
+ /* Subnodes of cEMI node */
+ proto_tree* cemi_list = proto_item_add_subtree( cemi_node, ett_cemi );
+
+ guint8 pa_flags = PA_DATA;
+
+ /* Only add cEMI information to the info column (not replacing it).
+ This means that we do not have to clear that column here, but
+ are adding a seperator here.
+ */
+ col_append_str( cinfo, COL_INFO, " " );
+
+ /* Replace long name "Common External Message Interface" by short name "cEMI" */
+ proto_item_set_text( cemi_node, "cEMI" );
+
+ if( size <= 0 )
+ {
+ expert_add_info_format( pinfo, cemi_node, KIP_ERROR, "Expected: min 1 byte" );
+ error = 1;
+ }
+ else
+ {
+ /* 1 byte cEMI Message Code */
+ guint8 mc = tvb_get_guint8( tvb, 0 );
+ const gchar* name = try_val_to_str( mc, mc_vals );
+
+ if( !name )
+ {
+ /* Unknown Message Code */
+ col_append_str( cinfo, COL_INFO, "cEMI" );
+ pa_flags = 0;
+ }
+ else
+ {
+ /* Add cEMI message code to info column */
+ col_append_str( cinfo, COL_INFO, name );
+
+ /* Show MC in cEMI node, and more detailed in a subnode */
+ proto_item_append_text( cemi_node, " %s", name );
+ proto_tree_add_item( cemi_list, hf_cemi_mc, tvb, 0, 1, ENC_BIG_ENDIAN );
+
+ offset = 1;
+
+ if( mc >= 0xF0 )
+ {
+ /* cEMI Management packet */
+ dissect_cemi_mgmt_packet( tvb, pinfo, cemi_node, cemi_list, mc, &offset, size, &pa_flags, &error );
+ }
+ else
+ {
+ /* cEMI Link Layer packet */
+ dissect_cemi_link_layer( tvb, pinfo, tree, cemi_node, cemi_list, mc, &offset, size, &pa_flags, &error );
+ }
+ }
+ }
+
+ if( offset < size )
+ {
+ /* Trailing data */
+ proto_item* node = proto_tree_add_data( cemi_list, tvb, offset, size - offset, cinfo, cemi_node, "Data", " $", ", $" );
+
+ if( !pa_flags )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Unexpected" );
+ error = 1;
+ }
+
+ offset = size;
+ }
+
+ if( error )
+ {
+ /* If not already done */
+ if( !knxip_error )
+ {
+ knxip_error = 1;
+ col_prepend_fstr( cinfo, COL_INFO, "? " );
+ }
+
+ proto_item_prepend_text( cemi_node, "? " );
+ }
+
+ return size;
+}
+
+void proto_register_cemi( void )
+{
+ /* Header fields */
+ static hf_register_info hf[] = {
+ { &hf_bytes, { "Data", "cemi.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_folder, { "Folder", "cemi.folder", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_mc, { "Message Code", "cemi.mc", FT_UINT8, BASE_HEX, VALS( mc_vals ), 0, NULL, HFILL } },
+ { &hf_cemi_error, { "Error", "cemi.e", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ai_length, { "Additional Information Length", "cemi.ai.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_aie_type, { "Additional Information Element Type", "cemi.ait.n", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_aie_length, { "Additional Information Element Length", "cemi.aie.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ot, { "Object Type", "cemi.ot", FT_UINT16, BASE_DEC, VALS( ot_vals ), 0, NULL, HFILL } },
+ { &hf_cemi_oi, { "Object Instance", "cemi.oi", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ox, { "Object Index", "cemi.ox", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_px, { "Property Index", "cemi.px",FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_pid, { "Property ID", "cemi.pid", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ne, { "Count", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
+ { &hf_cemi_sx, { "Index", "cemi.x", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
+ { &hf_cemi_ft, { "Frame Type", "cemi.ft", FT_UINT8, BASE_DEC, VALS( ft_vals ), 0x80, NULL, HFILL } },
+ { &hf_cemi_rep, { "Repeat On Error", "cemi.rep", FT_BOOLEAN, 8, TFS(&tfs_no_yes), 0x20, NULL, HFILL } },
+ { &hf_cemi_bt, { "Broadcast Type", "cemi.bt", FT_UINT8, BASE_DEC, VALS( bt_vals ), 0x10, NULL, HFILL } },
+ { &hf_cemi_prio, { "Priority", "cemi.prio", FT_UINT8, BASE_DEC, VALS( prio_vals ), 0x0C, NULL, HFILL } },
+ { &hf_cemi_ack, { "Ack Wanted", "cemi.ack", FT_BOOLEAN, 8, TFS(&tfs_no_yes), 0x02, NULL, HFILL } },
+ { &hf_cemi_ce, { "Confirmation Error", "cemi.ce", FT_BOOLEAN, 8, TFS(&tfs_no_yes), 0x01, NULL, HFILL } },
+ { &hf_cemi_at, { "Address Type", "cemi.at", FT_UINT8, BASE_DEC, VALS( at_vals ), 0x80, NULL, HFILL } },
+ { &hf_cemi_hc, { "Hop Count", "cemi.hc", FT_UINT8, BASE_DEC, NULL, 0x70, NULL, HFILL } },
+ { &hf_cemi_eff, { "Extended Frame Format", "cemi.eff", FT_UINT8, BASE_HEX, NULL, 0x0F, NULL, HFILL } },
+ { &hf_cemi_sa, { "Source", "cemi.sa", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_da, { "Destination", "cemi.da", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_len, { "Length", "cemi.len", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_tpt, { "Packet Type", "cemi.tpt", FT_UINT8, BASE_DEC, VALS( pt_vals ), 0x80, NULL, HFILL } },
+ { &hf_cemi_tst, { "Sequence Type", "cemi.st", FT_UINT8, BASE_DEC, VALS( st_vals ), 0x40, NULL, HFILL } },
+ { &hf_cemi_num, { "Sequence Number", "cemi.num", FT_UINT8, BASE_DEC, NULL, 0x3C, NULL, HFILL } },
+ { &hf_cemi_tc, { "Service", "cemi.tc", FT_UINT8, BASE_HEX, VALS( tc_vals ), 0x03, NULL, HFILL } },
+ { &hf_cemi_ac, { "Service", "cemi.ac", FT_UINT16, BASE_HEX, VALS( ac_vals ), 0x03C0, NULL, HFILL } },
+ { &hf_cemi_ad, { "Data", "cemi.ad", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ad_memory_length, { "Memory Length", "cemi.ad.ml", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ad_channel, { "Channel", "cemi.ad.ch", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ad_type, { "Data", "cemi.ad.type", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ax, { "Service", "cemi.ax", FT_UINT16, BASE_HEX, VALS( ax_vals ), 0x03FF, NULL, HFILL } },
+ { &hf_cemi_pw, { "Writable", "cemi.pw", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
+ { &hf_cemi_pdt, { "Property Data Type", "cemi.pdt", FT_UINT8, BASE_HEX, VALS( pdt_vals ), 0x3F, NULL, HFILL } },
+ { &hf_cemi_me, { "Max Elements", "cemi.me", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
+ { &hf_cemi_ra, { "Read Access", "cemi.ra", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
+ { &hf_cemi_wa, { "Write Access", "cemi.wa", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
+ { &hf_cemi_ext_oi, { "Object Instance", "cemi.oi", FT_UINT16, BASE_DEC, NULL, 0xFFF0, NULL, HFILL } },
+ { &hf_cemi_ext_pid, { "Property ID", "cemi.pid", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
+ { &hf_cemi_ext_ne, { "Count", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ext_sx, { "Index", "cemi.x", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ext_dt, { "Description Type", "cemi.dt", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
+ { &hf_cemi_ext_px, { "Property Index", "cemi.px", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
+ { &hf_cemi_ext_memory_length, { "Memory Length", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_ext_memory_address, { "Memory Address", "cemi.x", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_memory_length, { "Memory Length", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
+ { &hf_cemi_memory_address, { "Memory Address", "cemi.x", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_memory_address_ext, { "Memory Address Extension", "cemi.xx", FT_UINT8, BASE_HEX, NULL, 0xF0, NULL, HFILL } },
+ { &hf_cemi_level, { "Level", "cemi.level", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_snp_pid, { "Property ID", "cemi.pid", FT_UINT16, BASE_DEC, NULL, 0xFFF0, NULL, HFILL } },
+ { &hf_cemi_snp_reserved, { "Reserved", "cemi.reserved", FT_UINT16, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
+ { &hf_cemi_dpt_major, { "Data Point Type Major", "cemi.pdt.major", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_dpt_minor, { "Data Point Type Minor", "cemi.pdt.minor", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_cemi_scf, { "Security Control Field", "cemi.scf", FT_UINT8, BASE_HEX, VALS( scf_vals ), 0, NULL, HFILL } },
+ { &hf_cemi_scf_t, { "Tool Access", "cemi.scf.t", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
+ { &hf_cemi_scf_sai, { "Security Algorithm Identifier", "cemi.scf.sai", FT_UINT8, BASE_HEX, VALS( scf_sai_vals ), 0x70, NULL, HFILL } },
+ { &hf_cemi_scf_sbc, { "System Broadcast", "cemi.scf.sbc", FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL } },
+ { &hf_cemi_scf_svc, { "Service", "cemi.scf.sai", FT_UINT8, BASE_HEX, VALS( scf_svc_vals ), 0x07, NULL, HFILL } },
+ { &hf_cemi_adc_count, { "Count", "cemi.adc.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ };
+
+ /* Subtrees */
+ static gint *ett[] = {
+ &ett_cemi,
+ &ett_cemi_ai,
+ &ett_cemi_aie,
+ &ett_cemi_ctrl1,
+ &ett_cemi_ctrl2,
+ &ett_cemi_tpci,
+ &ett_cemi_apci,
+ &ett_cemi_range,
+ &ett_cemi_pd,
+ &ett_cemi_dpt,
+ &ett_cemi_scf,
+ &ett_cemi_decrypted
+ };
+
+ proto_cemi = proto_register_protocol( "Common External Message Interface", "cEMI", "cemi" );
+
+ proto_register_field_array( proto_cemi, hf, array_length( hf ) );
+ proto_register_subtree_array( ett, array_length( ett ) );
+
+ register_dissector( "cemi", dissect_cemi, proto_cemi );
+}
+
+void proto_reg_handoff_cemi( void )
+{
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */