aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/CMakeLists.txt6
-rw-r--r--epan/dissectors/packet-cemi.c3515
-rw-r--r--epan/dissectors/packet-knxip.c4136
-rw-r--r--epan/dissectors/packet-knxip.h56
-rw-r--r--epan/dissectors/packet-knxip_decrypt.c811
-rw-r--r--epan/dissectors/packet-knxip_decrypt.h97
-rw-r--r--epan/dissectors/packet-knxnetip.c1842
-rw-r--r--test/captures/knxip_DataSec.pcapbin0 -> 125 bytes
-rw-r--r--test/captures/knxip_SecureWrapper.pcapbin0 -> 137 bytes
-rw-r--r--test/captures/knxip_TimerNotify.pcapbin0 -> 118 bytes
-rw-r--r--test/keys/knx_keyring.xml58
-rw-r--r--test/suite_decryption.py115
12 files changed, 8793 insertions, 1843 deletions
diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt
index 86ae83c3b9..5e099e1b17 100644
--- a/epan/dissectors/CMakeLists.txt
+++ b/epan/dissectors/CMakeLists.txt
@@ -405,6 +405,8 @@ set(DISSECTOR_PUBLIC_HEADERS
packet-juniper.h
packet-jxta.h
packet-kerberos.h
+ packet-knxip.h
+ packet-knxip_decrypt.h
packet-l2tp.h
packet-lapdm.h
packet-lbm.h
@@ -762,6 +764,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-ccsds.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-cdp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-cell_broadcast.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-cemi.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-ceph.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-cfdp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-cfm.c
@@ -1243,7 +1246,8 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-kismet.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-klm.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-knet.c
- ${CMAKE_CURRENT_SOURCE_DIR}/packet-knxnetip.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-knxip.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/packet-knxip_decrypt.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-kpasswd.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-kt.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-l1-events.c
diff --git a/epan/dissectors/packet-cemi.c b/epan/dissectors/packet-cemi.c
new file mode 100644
index 0000000000..490476ad34
--- /dev/null
+++ b/epan/dissectors/packet-cemi.c
@@ -0,0 +1,3515 @@
+/* packet-cemi.c
+ * Routines for cEMI (Common External Message Interface) dissection
+ * By Jan Kessler <kessler@ise.de>
+ * Copyright 2004, Jan Kessler <kessler@ise.de>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "packet-knxip.h"
+
+/* 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 }
+};
+
+/* Repeat on error?
+*/
+static const value_string rep_vals[] = {
+ { 0, "Yes" },
+ { 1, "No" },
+ { 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 }
+};
+
+/* Ack requested?
+*/
+static const value_string ack_vals[] = {
+ { 0, "No" },
+ { 1, "Yes" },
+ { 0, NULL }
+};
+
+/* Confirmation Error?
+*/
+static const value_string ce_vals[] = {
+ { 0, "No" },
+ { 1, "Yes" },
+ { 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( const guint8* key, const guint8* encrypted, gint encrypted_size, const guint8* cemi, gint cemi_size )
+{
+ guint8 ctr_0[ 16 ];
+ guint8 b_0[ 16 ];
+ guint8 mac[ 16 ];
+ 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( wmem_packet_scope(), 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( wmem_packet_scope(), a_bytes );
+
+ if( memcmp( mac, decrypted + p_length, 4 ) != 0 )
+ {
+ // Wrong mac. Return 0.
+ wmem_free( wmem_packet_scope(), 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( 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;
+ g_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( key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ g_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( key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ g_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( key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ g_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( key, encrypted, encrypted_size, cemi, cemi_size );
+
+ if( decrypted )
+ {
+ break;
+ }
+ }
+ }
+
+ if( decrypted )
+ {
+ guint8 count;
+
+ g_snprintf( output, output_max, "key" );
+
+ for( count = 16; count; --count )
+ {
+ while( *output ) { ++output; --output_max; }
+ g_snprintf( output, output_max, " %02X", *key++ );
+ }
+ }
+ else
+ {
+ g_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;
+ }
+
+ node = proto_tree_add_debug_text( list, "? Object Index" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 );
+
+ *p_offset = offset += 2;
+ 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;
+ }
+
+ node = proto_tree_add_debug_text( list, "? Property ID" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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;
+ }
+
+ node = proto_tree_add_debug_text( list, "? Property Index" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 */
+ node = 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 ? pinfo->cinfo : NULL;
+ 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 ? pinfo->cinfo : NULL;
+ 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_item* node = proto_tree_add_debug_text( cemi_list, "? Object Instance" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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" G_GINT32_MODIFIER "X", x );
+ proto_item_append_text( cemi_node, ", X=$%06" G_GINT32_MODIFIER "X", 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_item* node = proto_tree_add_debug_text( cemi_list, "? Level" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? Data" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 );
+ node = proto_tree_add_item( list, hf_cemi_scf_t, tvb, offset, 1, ENC_BIG_ENDIAN );
+ node = proto_tree_add_item( list, hf_cemi_scf_sai, tvb, offset, 1, ENC_BIG_ENDIAN );
+ node = proto_tree_add_item( list, hf_cemi_scf_sbc, tvb, offset, 1, ENC_BIG_ENDIAN );
+ node = 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" G_GINT64_MODIFIER "X", 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( 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 );
+ }
+
+ node = proto_tree_add_data( tree, tvb2, 0, size2, NULL, NULL, "Embedded APDU", NULL, NULL );
+ node = 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( wmem_packet_scope(), 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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? Count" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 );
+ }
+ node = 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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? TPCI" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 )
+ {
+ g_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_node = proto_tree_add_debug_text( cemi_list, "? Additional Info" );
+ ai_list = proto_item_add_subtree( ai_node, ett_cemi_ai );
+ node = proto_tree_add_debug_text( ai_list, "? Length" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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, "? " );
+ node = proto_tree_add_debug_text( aie_list, "? Length" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? Ctrl1" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? Ctrl2" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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) */
+
+ g_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 );
+ g_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 */
+ g_snprintf( text, sizeof text, "%u.%u.%u", (dest_addr >> 12) & 0xF, (dest_addr >> 8) & 0xF, dest_addr & 0xFF );
+ }
+ else
+ {
+ /* Group Address */
+ g_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 )
+ {
+ node = proto_tree_add_debug_text( cemi_list, "? Length" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "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_UINT8, BASE_DEC, VALS( rep_vals ), 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_UINT8, BASE_DEC, VALS( ack_vals ), 0x02, NULL, HFILL } },
+ { &hf_cemi_ce, { "Confirmation Error", "cemi.ce", FT_UINT8, BASE_DEC, VALS( ce_vals ), 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", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ad_channel, { "Channel", "cemi.ad", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
+ { &hf_cemi_ad_type, { "Data", "cemi.ad", 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:
+ */
diff --git a/epan/dissectors/packet-knxip.c b/epan/dissectors/packet-knxip.c
new file mode 100644
index 0000000000..21f9e86b69
--- /dev/null
+++ b/epan/dissectors/packet-knxip.c
@@ -0,0 +1,4136 @@
+/* packet-knxip.c
+ * Routines for KNXnet/IP dissection
+ * By Jan Kessler <kessler@ise.de>
+ * Copyright 2004, Jan Kessler <kessler@ise.de>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "packet-knxip.h"
+#include <epan/strutil.h>
+
+#define ECDH_PUBLIC_VALUE_SIZE 32
+
+ /* The following service families are defined for the
+ version 1.0 KNXnet/IP implementation of the eFCP protocol
+ */
+#define KIP_SERVICE_CORE 0x02
+#define KIP_SERVICE_MANAGEMENT 0x03
+#define KIP_SERVICE_TUNNELING 0x04
+#define KIP_SERVICE_ROUTING 0x05
+#define KIP_SERVICE_REMOTE_LOGGING 0x06
+#define KIP_SERVICE_REMOTE_DIAG_AND_CONFIG 0x07
+#define KIP_SERVICE_OBJECT_SERVER 0x08
+#define KIP_SERVICE_SECURITY 0x09
+
+ /* The service codes for the core services (device discovery,
+ self description and connection management) as defined in
+ chapter 2 of the KNXnet/IP system specification
+ */
+#define KIP_SEARCH_REQUEST 0x0201
+#define KIP_SEARCH_RESPONSE 0x0202
+#define KIP_DESCRIPTION_REQUEST 0x0203
+#define KIP_DESCRIPTION_RESPONSE 0x0204
+#define KIP_CONNECT_REQUEST 0x0205
+#define KIP_CONNECT_RESPONSE 0x0206
+#define KIP_CONNECTIONSTATE_REQUEST 0x0207
+#define KIP_CONNECTIONSTATE_RESPONSE 0x0208
+#define KIP_DISCONNECT_REQUEST 0x0209
+#define KIP_DISCONNECT_RESPONSE 0x020A
+#define KIP_SEARCH_REQUEST_EXT 0x020B
+#define KIP_SEARCH_RESPONSE_EXT 0x020C
+
+ /* The service codes for the device management services
+ (tunneling of cEMI local management procedures) as
+ defined in chapter 3 of the KNXnet/IP system specification
+ */
+#define KIP_CONFIGURATION_REQUEST 0x0310
+#define KIP_CONFIGURATION_ACK 0x0311
+
+ /* The service codes for the tunneling services
+ (transport of cEMI frames from service interface) as
+ defined in chapter 4 of the KNXnet/IP system specification
+ */
+#define KIP_TUNNELING_REQUEST 0x0420
+#define KIP_TUNNELING_ACK 0x0421
+#define KIP_TUNNELING_FEATURE_GET 0x0422
+#define KIP_TUNNELING_FEATURE_RESPONSE 0x0423
+#define KIP_TUNNELING_FEATURE_SET 0x0424
+#define KIP_TUNNELING_FEATURE_INFO 0x0425
+
+ /* The service codes for the routing services
+ (transport of cEMI frames between EIB couplers) as
+ defined in chapter 5 of the KNXnet/IP system specification
+ */
+#define KIP_ROUTING_INDICATION 0x0530
+#define KIP_ROUTING_LOST_MESSAGE 0x0531
+#define KIP_ROUTING_BUSY 0x0532
+#define KIP_ROUTING_SYSTEM_BROADCAST 0x0533
+
+ /* The service codes for RemoteDiagAndConfig
+ */
+#define KIP_REMOTE_DIAG_REQUEST 0x0740
+#define KIP_REMOTE_DIAG_RESPONSE 0x0741
+#define KIP_REMOTE_CONFIG_REQUEST 0x0742
+#define KIP_REMOTE_RESET_REQUEST 0x0743
+
+ /* The service codes for KNX-IP Secure
+ */
+#define KIP_SECURE_WRAPPER 0x0950
+#define KIP_SESSION_REQUEST 0x0951
+#define KIP_SESSION_RESPONSE 0x0952
+#define KIP_SESSION_AUTHENTICATE 0x0953
+#define KIP_SESSION_STATUS 0x0954
+#define KIP_TIMER_NOTIFY 0x0955
+
+ /* KNXnet/IP host protocols */
+#define KIP_IPV4_UDP 0x01
+#define KIP_IPV4_TCP 0x02
+
+ /* The different types of DIBs (Description Information Blocks)
+ for the KNXnet/IP Core Discovery and Description services
+ as defined in chapter 1 of the KNXnet/IP system specification
+ */
+#define KIP_DIB_DEVICE_INFO 0x01
+#define KIP_DIB_SUPP_SVC_FAMILIES 0x02
+#define KIP_DIB_IP_CONFIG 0x03
+#define KIP_DIB_CUR_CONFIG 0x04
+#define KIP_DIB_KNX_ADDRESSES 0x05
+#define KIP_DIB_SECURED_SERVICE_FAMILIES 0x06
+#define KIP_DIB_TUNNELING_INFO 0x07
+#define KIP_DIB_EXTENDED_DEVICE_INFO 0x08
+#define KIP_DIB_MFR_DATA 0xFE
+
+ /* The different types of SRPs (Search Request Parameter Blocks)
+ for the KNXnet/IP Core Discovery and Description services
+ */
+#define KIP_SRP_BY_PROGMODE 0x01
+#define KIP_SRP_BY_MACADDR 0x02
+#define KIP_SRP_BY_SERVICE 0x03
+#define KIP_SRP_REQUEST_DIBS 0x04
+
+ /* The different KNX medium types for the hardware (device info)
+ DIB as defined in AN033 Common EMI Specification
+ */
+#define KIP_KNXTYPE_TP0 0x01
+#define KIP_KNXTYPE_TP1 0x02
+#define KIP_KNXTYPE_PL110 0x04
+#define KIP_KNXTYPE_PL132 0x08
+#define KIP_KNXTYPE_RF 0x10
+#define KIP_KNXTYPE_IP 0x20
+
+ /* KNXnet/IP connection types */
+#define KIP_DEVICE_MGMT_CONNECTION 0x03
+#define KIP_TUNNEL_CONNECTION 0x04
+#define KIP_REMLOG_CONNECTION 0x06
+#define KIP_REMCONF_CONNECTION 0x07
+#define KIP_OBJSVR_CONNECTION 0x08
+
+ /* Tunneling v2 feature ids */
+#define KIP_TUNNELING_FEATURE_ID_SUPPORTED_EMI_TYPE 0x01
+#define KIP_TUNNELING_FEATURE_ID_HOST_DEVICE_DEVICE_DESCRIPTOR_TYPE_0 0x02
+#define KIP_TUNNELING_FEATURE_ID_BUS_CONNECTION_STATUS 0x03
+#define KIP_TUNNELING_FEATURE_ID_KNX_MANUFACTURER_CODE 0x04
+#define KIP_TUNNELING_FEATURE_ID_ACTIVE_EMI_TYPE 0x05
+#define KIP_TUNNELING_FEATURE_ID_INDIVIDUAL_ADDRESS 0x06
+#define KIP_TUNNELING_FEATURE_ID_MAX_APDU_LENGTH 0x07
+#define KIP_TUNNELING_FEATURE_ID_INFO_SERVICE_ENABLE 0x08
+
+ /* KNXnet/IP tunnel types */
+#define TUNNEL_LINKLAYER 0x02
+#define TUNNEL_RAW 0x04
+#define TUNNEL_BUSMONITOR 0x80
+
+ /* KNXnet/IP error codes */
+#define KIP_E_NO_ERROR 0x00
+#define KIP_E_CONNECTION_ID 0x21
+#define KIP_E_CONNECTION_TYPE 0x22
+#define KIP_E_CONNECTION_OPTION 0x23
+#define KIP_E_NO_MORE_CONNECTIONS 0x24
+#define KIP_E_NO_MORE_UNIQUE_CONNECTIONS 0x25
+#define KIP_E_DATA_CONNECTION 0x26
+#define KIP_E_KNX_CONNECTION 0x27
+#define KIP_E_TUNNELING_LAYER 0x29
+
+/* KNXnet/IP remote selection types */
+#define SELECT_PROGMODE 0x01
+#define SELECT_MACADDRESS 0x02
+
+/* SESSION_STATUS codes */
+#define SESSION_STATUS_AUTHENTICATION_SUCCESS 0x00
+#define SESSION_STATUS_AUTHENTICATION_FAILED 0x01
+#define SESSION_STATUS_UNAUTHENTICATED 0x02
+#define SESSION_STATUS_TIMEOUT 0x03
+#define SESSION_STATUS_KEEPALIVE 0x04
+#define SESSION_STATUS_CLOSE 0x05
+
+/* Initialize the protocol identifier that is needed for the
+ protocol hook and to register the fields in the protocol tree
+*/
+static gint proto_knxip = -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_knxip_header_length = -1;
+static gint hf_knxip_protocol_version = -1;
+static gint hf_knxip_service_id = -1;
+static gint hf_knxip_service_family = -1;
+static gint hf_knxip_service_type = -1;
+static gint hf_knxip_total_length = -1;
+static gint hf_knxip_structure_length = -1;
+static gint hf_knxip_host_protocol = -1;
+static gint hf_knxip_ip_address = -1;
+static gint hf_knxip_port = -1;
+static gint hf_knxip_description_type = -1;
+static gint hf_knxip_knx_medium = -1;
+static gint hf_knxip_device_status = -1;
+static gint hf_knxip_program_mode = -1;
+static gint hf_knxip_knx_address = -1;
+static gint hf_knxip_project_id = -1;
+static gint hf_knxip_project_number = -1;
+static gint hf_knxip_installation_number = -1;
+static gint hf_knxip_serial_number = -1;
+static gint hf_knxip_multicast_address = -1;
+static gint hf_knxip_mac_address = -1;
+static gint hf_knxip_friendly_name = -1;
+static gint hf_knxip_service_version = -1;
+static gint hf_knxip_security_version = -1;
+static gint hf_knxip_manufacturer_code = -1;
+static gint hf_knxip_connection_type = -1;
+static gint hf_knxip_knx_layer = -1;
+static gint hf_knxip_reserved = -1;
+static gint hf_knxip_channel = -1;
+static gint hf_knxip_status = -1;
+static gint hf_knxip_seq_counter = -1;
+static gint hf_knxip_ip_subnet = -1;
+static gint hf_knxip_ip_gateway = -1;
+static gint hf_knxip_ip_assign = -1;
+static gint hf_knxip_ip_caps = -1;
+static gint hf_knxip_ip_dhcp = -1;
+static gint hf_knxip_tunnel_feature = -1;
+static gint hf_knxip_routing_loss = -1;
+static gint hf_knxip_busy_time = -1;
+static gint hf_knxip_busy_control = -1;
+static gint hf_knxip_selector = -1;
+static gint hf_knxip_max_apdu_length = -1;
+static gint hf_knxip_medium_status = -1;
+static gint hf_knxip_mask_version = -1;
+static gint hf_knxip_srp_mandatory = -1;
+static gint hf_knxip_srp_type = -1;
+static gint hf_knxip_reset_command = -1;
+static gint hf_knxip_session = -1;
+static gint hf_knxip_tag = -1;
+static gint hf_knxip_user = -1;
+static gint hf_knxip_session_status = -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_kip = -1;
+static gint ett_efcp = -1;
+static gint ett_service = -1;
+static gint ett_hpai = -1;
+static gint ett_dib = -1;
+static gint ett_medium = -1;
+static gint ett_status = -1;
+static gint ett_projectid = -1;
+static gint ett_service_family = -1;
+static gint ett_ip_assignment = -1;
+static gint ett_cri = -1;
+static gint ett_crd = -1;
+static gint ett_cnhdr = -1;
+static gint ett_loss = -1;
+static gint ett_busy = -1;
+static gint ett_selector = -1;
+static gint ett_decrypted = -1;
+static gint ett_tunnel = -1;
+
+/* Set up the value_string tables for the service families
+ and the service types (note that the service types in KNXnet/IP
+ version 1.0 are unique even across service families...)
+*/
+static const value_string knxip_service_family_vals[] = {
+ { KIP_SERVICE_CORE, "Core" },
+ { KIP_SERVICE_MANAGEMENT, "Device Management" },
+ { KIP_SERVICE_TUNNELING, "Tunneling" },
+ { KIP_SERVICE_ROUTING, "Routing" },
+ { KIP_SERVICE_REMOTE_LOGGING, "Remote Logging" },
+ { KIP_SERVICE_REMOTE_DIAG_AND_CONFIG, "Remote Diag And Config" },
+ { KIP_SERVICE_OBJECT_SERVER, "Object Server" },
+ { KIP_SERVICE_SECURITY, "Security" },
+ { 0, NULL}
+};
+static const value_string knxip_service_type_vals[] = {
+ { KIP_SEARCH_REQUEST, "Search Request" },
+ { KIP_SEARCH_RESPONSE, "Search Response" },
+ { KIP_DESCRIPTION_REQUEST, "Description Request" },
+ { KIP_DESCRIPTION_RESPONSE, "Description Response" },
+ { KIP_CONNECT_REQUEST, "Connect Request" },
+ { KIP_CONNECT_RESPONSE, "Connect Response" },
+ { KIP_CONNECTIONSTATE_REQUEST, "Connection State Request" },
+ { KIP_CONNECTIONSTATE_RESPONSE, "Connection State Response" },
+ { KIP_DISCONNECT_REQUEST, "Disconnect Request" },
+ { KIP_DISCONNECT_RESPONSE, "Disconnect Response" },
+ { KIP_SEARCH_REQUEST_EXT, "Search Request Extended" },
+ { KIP_SEARCH_RESPONSE_EXT, "Search Response Extended" },
+ { KIP_CONFIGURATION_REQUEST, "Configuration Request" },
+ { KIP_CONFIGURATION_ACK, "Configuration Acknowledgement" },
+ { KIP_TUNNELING_REQUEST, "Tunneling Request" },
+ { KIP_TUNNELING_ACK, "Tunneling Acknowledgement" },
+ { KIP_TUNNELING_FEATURE_GET, "Tunneling Feature Get" },
+ { KIP_TUNNELING_FEATURE_RESPONSE, "Tunneling Feature Response" },
+ { KIP_TUNNELING_FEATURE_SET, "Tunneling Feature Set" },
+ { KIP_TUNNELING_FEATURE_INFO, "Tunneling Feature Info" },
+ { KIP_ROUTING_INDICATION, "Routing Indication" },
+ { KIP_ROUTING_LOST_MESSAGE, "Routing Loss" },
+ { KIP_ROUTING_BUSY, "Routing Busy" },
+ { KIP_ROUTING_SYSTEM_BROADCAST, "Routing System Broadcast" },
+ { KIP_REMOTE_DIAG_REQUEST, "Remote Diagnostic Request" },
+ { KIP_REMOTE_DIAG_RESPONSE, "Remote Diagnostic Response" },
+ { KIP_REMOTE_CONFIG_REQUEST, "Remote Configuration Request" },
+ { KIP_REMOTE_RESET_REQUEST, "Remote Reset Request" },
+ { KIP_SECURE_WRAPPER, "Secure Wrapper" },
+ { KIP_SESSION_REQUEST, "Session Request" },
+ { KIP_SESSION_RESPONSE, "Session Response" },
+ { KIP_SESSION_AUTHENTICATE, "Session Authenticate" },
+ { KIP_SESSION_STATUS, "Session Status" },
+ { KIP_TIMER_NOTIFY, "Timer Notify" },
+ { 0, NULL}
+};
+static const value_string svc_vals[] = { /* abbreviated service names */
+ { KIP_SEARCH_REQUEST, "SearchReq" },
+ { KIP_SEARCH_RESPONSE, "SearchResp" },
+ { KIP_DESCRIPTION_REQUEST, "DescrReq" },
+ { KIP_DESCRIPTION_RESPONSE, "DescrResp" },
+ { KIP_CONNECT_REQUEST, "ConnectReq" },
+ { KIP_CONNECT_RESPONSE, "ConnectResp" },
+ { KIP_CONNECTIONSTATE_REQUEST, "ConnStateReq" },
+ { KIP_CONNECTIONSTATE_RESPONSE, "ConnStateResp" },
+ { KIP_DISCONNECT_REQUEST, "DisconnectReq" },
+ { KIP_DISCONNECT_RESPONSE, "DisconnectResp" },
+ { KIP_SEARCH_REQUEST_EXT, "SearchReqExt" },
+ { KIP_SEARCH_RESPONSE_EXT, "SearchRespExt" },
+ { KIP_CONFIGURATION_REQUEST, "ConfigReq" },
+ { KIP_CONFIGURATION_ACK, "ConfigAck" },
+ { KIP_TUNNELING_REQUEST, "TunnelReq" },
+ { KIP_TUNNELING_ACK, "TunnelAck" },
+ { KIP_TUNNELING_FEATURE_GET, "TunnelFeatureGet" },
+ { KIP_TUNNELING_FEATURE_RESPONSE, "TunnelFeatureResp" },
+ { KIP_TUNNELING_FEATURE_SET, "TunnelFeatureSet" },
+ { KIP_TUNNELING_FEATURE_INFO, "TunnelFeatureInfo" },
+ { KIP_ROUTING_INDICATION, "RoutingInd" },
+ { KIP_ROUTING_LOST_MESSAGE, "RoutingLoss" },
+ { KIP_ROUTING_BUSY, "RoutingBusy" },
+ { KIP_ROUTING_SYSTEM_BROADCAST, "RoutingSBC" },
+ { KIP_REMOTE_DIAG_REQUEST, "RemoteDiagReq" },
+ { KIP_REMOTE_DIAG_RESPONSE, "RemoteDiagResp" },
+ { KIP_REMOTE_CONFIG_REQUEST, "RemoteConfigReq" },
+ { KIP_REMOTE_RESET_REQUEST, "RemoteResetReq" },
+ { KIP_SECURE_WRAPPER, "SecureWrapper" },
+ { KIP_SESSION_REQUEST, "SessionReq" },
+ { KIP_SESSION_RESPONSE, "SessionResp" },
+ { KIP_SESSION_AUTHENTICATE, "SessionAuth" },
+ { KIP_SESSION_STATUS, "SessionStatus" },
+ { KIP_TIMER_NOTIFY, "TimerNotify" },
+ { 0, NULL}
+};
+static const value_string host_protocol_vals[] = {
+ { KIP_IPV4_UDP, "IPv4 UDP" },
+ { KIP_IPV4_TCP, "IPv4 TCP" },
+ { 0, NULL}
+};
+static const value_string description_type_vals[] = {
+ { KIP_DIB_DEVICE_INFO, "Device Information" },
+ { KIP_DIB_SUPP_SVC_FAMILIES, "Supported Service Families" },
+ { KIP_DIB_IP_CONFIG, "IP Configuration" },
+ { KIP_DIB_CUR_CONFIG, "Current Configuration" },
+ { KIP_DIB_KNX_ADDRESSES, "KNX Addresses" },
+ { KIP_DIB_SECURED_SERVICE_FAMILIES, "Secured Service Families" },
+ { KIP_DIB_TUNNELING_INFO, "Tunneling Information" },
+ { KIP_DIB_EXTENDED_DEVICE_INFO, "Extended Device Information" },
+ { KIP_DIB_MFR_DATA, "Manufacturer Data" },
+ { 0, NULL}
+};
+static const value_string descr_type_vals[] = { /* abbreviated DIB names */
+ { KIP_DIB_DEVICE_INFO, "DevInfo" },
+ { KIP_DIB_SUPP_SVC_FAMILIES, "SuppSvc" },
+ { KIP_DIB_IP_CONFIG, "IpConfig" },
+ { KIP_DIB_CUR_CONFIG, "CurConfig" },
+ { KIP_DIB_KNX_ADDRESSES, "KnxAddr" },
+ { KIP_DIB_SECURED_SERVICE_FAMILIES, "SecSvcFam" },
+ { KIP_DIB_TUNNELING_INFO, "TunnelInfo" },
+ { KIP_DIB_EXTENDED_DEVICE_INFO, "ExtDevInfo" },
+ { KIP_DIB_MFR_DATA, "MfrData" },
+ { 0, NULL}
+};
+#if 0
+static const value_string search_request_parameter_type_vals[] = {
+ { KIP_SRP_BY_PROGMODE, "By programming mode" },
+ { KIP_SRP_BY_MACADDR, "By MAC address" },
+ { KIP_SRP_BY_SERVICE, "By service" },
+ { KIP_SRP_REQUEST_DIBS, "Request DIBs" },
+ { 0, NULL }
+};
+#endif
+static const value_string srp_type_vals[] = { /* abbreviated SRP names */
+ { KIP_SRP_BY_PROGMODE, "ProgMode" },
+ { KIP_SRP_BY_MACADDR, "MacAddr" },
+ { KIP_SRP_BY_SERVICE, "Service" },
+ { KIP_SRP_REQUEST_DIBS, "Dibs" },
+ { 0, NULL }
+};
+static const value_string medium_type_vals[] = {
+ { KIP_KNXTYPE_TP0, "TP0" },
+ { KIP_KNXTYPE_TP1, "TP1" },
+ { KIP_KNXTYPE_PL110, "PL110" },
+ { KIP_KNXTYPE_PL132, "PL132" },
+ { KIP_KNXTYPE_RF, "RF" },
+ { KIP_KNXTYPE_IP, "IP" },
+ { 0, NULL}
+};
+static const value_string connection_type_vals[] = {
+ { KIP_DEVICE_MGMT_CONNECTION, "Device Management Connection" },
+ { KIP_TUNNEL_CONNECTION, "Tunneling Connection" },
+ { KIP_REMLOG_CONNECTION, "Remote Logging Connection" },
+ { KIP_REMCONF_CONNECTION, "Remote Configuration Connection" },
+ { KIP_OBJSVR_CONNECTION, "Object Server Connection" },
+ { 0, NULL}
+};
+static const value_string conn_type_vals[] = {
+ { KIP_DEVICE_MGMT_CONNECTION, "Config" },
+ { KIP_TUNNEL_CONNECTION, "Tunnel" },
+ { KIP_REMLOG_CONNECTION, "RemoteLogging" },
+ { KIP_REMCONF_CONNECTION, "RemoteConfig" },
+ { KIP_OBJSVR_CONNECTION, "ObjectServer" },
+ { 0, NULL }
+};
+static const value_string tunneling_feature_id_vals[] = {
+ { KIP_TUNNELING_FEATURE_ID_SUPPORTED_EMI_TYPE, "SupportedEmiType" },
+ { KIP_TUNNELING_FEATURE_ID_HOST_DEVICE_DEVICE_DESCRIPTOR_TYPE_0, "MaskVersion" },
+ { KIP_TUNNELING_FEATURE_ID_BUS_CONNECTION_STATUS, "BusStatus" },
+ { KIP_TUNNELING_FEATURE_ID_KNX_MANUFACTURER_CODE, "Manufacturer" },
+ { KIP_TUNNELING_FEATURE_ID_ACTIVE_EMI_TYPE, "ActiveEmiType" },
+ { KIP_TUNNELING_FEATURE_ID_INDIVIDUAL_ADDRESS, "IndividualAddress" },
+ { KIP_TUNNELING_FEATURE_ID_MAX_APDU_LENGTH, "MaxApduLength" },
+ { KIP_TUNNELING_FEATURE_ID_INFO_SERVICE_ENABLE, "InfoServiceEnable" },
+ { 0, NULL }
+};
+static const value_string knx_layer_vals[] = {
+ { TUNNEL_LINKLAYER, "LinkLayer" },
+ { TUNNEL_RAW, "Raw" },
+ { TUNNEL_BUSMONITOR, "Busmonitor" },
+ { 0, NULL}
+};
+static const value_string error_vals[] = {
+ { KIP_E_NO_ERROR, "OK" },
+ { KIP_E_CONNECTION_ID, "E_CONNECTION_ID" },
+ { KIP_E_CONNECTION_TYPE, "E_CONNECTION_TYPE" },
+ { KIP_E_CONNECTION_OPTION, "E_CONNECTION_OPTION" },
+ { KIP_E_NO_MORE_CONNECTIONS, "E_NO_MORE_CONNECTIONS" },
+ { KIP_E_NO_MORE_UNIQUE_CONNECTIONS, "E_NO_MORE_UNIQUE_CONNECTIONS" },
+ { KIP_E_DATA_CONNECTION, "E_DATA_CONNECTION" },
+ { KIP_E_KNX_CONNECTION, "E_KNX_CONNECTION" },
+ { KIP_E_TUNNELING_LAYER, "E_TUNNELING_LAYER" },
+ { 0, NULL}
+};
+static const value_string session_status_vals[] = {
+ { SESSION_STATUS_AUTHENTICATION_SUCCESS, "STATUS_AUTHENTICATION_SUCCESS" },
+ { SESSION_STATUS_AUTHENTICATION_FAILED, "STATUS_AUTHENTICATION_FAILED" },
+ { SESSION_STATUS_UNAUTHENTICATED, "STATUS_UNAUTHENTICATED" },
+ { SESSION_STATUS_TIMEOUT, "STATUS_TIMEOUT" },
+ { SESSION_STATUS_KEEPALIVE, "STATUS_KEEPALIVE" },
+ { SESSION_STATUS_CLOSE, "STATUS_CLOSE" },
+ { 0, NULL }
+};
+
+guint8 knxip_error;
+guint8 knxip_host_protocol;
+
+expert_field ei_knxip_error = EI_INIT;
+expert_field ei_knxip_warning = EI_INIT;
+
+static const gchar* pref_key_texts[ MAX_KNX_DECRYPTION_KEYS ];
+//static const gchar* authentication_code_text;
+//static const gchar* password_hash_text;
+static const gchar* pref_key_file_name;
+static const gchar* pref_key_file_pwd;
+static const gchar* pref_key_info_file_name;
+
+/* KNX decryption keys
+*/
+guint8 knx_decryption_keys[ MAX_KNX_DECRYPTION_KEYS ][ KNX_KEY_LENGTH ];
+guint8 knx_decryption_key_count;
+
+/* Forward declarations
+*/
+static void dissect_knxip( guint8 level, tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree );
+void proto_reg_handoff_knxip( void );
+
+/* Add raw data to list view, tree view, and parent folder
+*/
+static proto_item* knxip_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;
+}
+
+/* Show unknown or unexpected data
+*/
+static proto_item* knxip_tree_add_unknown_data( proto_tree* tree, tvbuff_t* tvb, gint offset, gint length )
+{
+ return proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, length, NULL, "? Unknown data (%d bytes)", length );
+}
+
+static guint8 hex_to_knx_key( const gchar* text, guint8 key[ KNX_KEY_LENGTH ] )
+{
+ size_t n_bytes = 0;
+ guint8* bytes = convert_string_to_hex( text, &n_bytes );
+ if( bytes == NULL )
+ {
+ n_bytes = 0;
+ }
+ else
+ {
+ if( n_bytes )
+ {
+ if( n_bytes > KNX_KEY_LENGTH ) n_bytes = KNX_KEY_LENGTH;
+ if( n_bytes ) memcpy( key, bytes, n_bytes );
+ while( n_bytes < KNX_KEY_LENGTH ) key[ n_bytes++ ] = 0;
+ }
+ g_free( bytes );
+ }
+ return n_bytes != 0;
+}
+
+static proto_item* knxip_tree_add_status( proto_tree* tree, tvbuff_t* tvb, gint offset )
+{
+ return proto_tree_add_item( tree, hf_knxip_status, tvb, offset, 1, ENC_BIG_ENDIAN );
+}
+
+static proto_item* knxip_tree_add_reserved( proto_tree* tree, tvbuff_t* tvb, gint offset, packet_info* pinfo, guint8* p_ok )
+{
+ proto_item* new_item = proto_tree_add_item( tree, hf_knxip_reserved, tvb, offset, 1, ENC_BIG_ENDIAN );
+ if( tvb_get_guint8( tvb, offset ) )
+ {
+ proto_item_prepend_text( new_item, "? " );
+ expert_add_info_format( pinfo, new_item, KIP_ERROR, "Expected: 0x00" );
+ if( p_ok ) *p_ok = 0;
+ }
+ return new_item;
+}
+
+static proto_item* knxip_tree_add_missing_reserved( proto_tree* tree, packet_info* pinfo )
+{
+ proto_item* new_item = proto_tree_add_debug_text( tree, "? Reserved" );
+ expert_add_info_format( pinfo, new_item, KIP_ERROR, "Expected: 1 byte" );
+ return new_item;
+}
+
+static proto_item* knxip_tree_add_length( proto_tree* tree, tvbuff_t* tvb, gint offset, gint value )
+{
+ return proto_tree_add_uint_format_value( tree, hf_knxip_structure_length, tvb, offset, 1, value, "%u bytes", value );
+}
+
+static void knxip_item_illegal_length( proto_item* length_item, packet_info* pinfo, const gchar* info )
+{
+ proto_item_prepend_text( length_item, "? " );
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "%s", info );
+}
+
+static proto_item* knxip_tree_add_ip_address( proto_tree* tree, tvbuff_t* tvb, gint offset, gchar* output, gint output_max )
+{
+ if( output )
+ {
+ const guint8* ipa = tvb_get_ptr( tvb, offset, 4 );
+ g_snprintf( output, output_max, "%u.%u.%u.%u", ipa[ 0 ], ipa[ 1 ], ipa[ 2 ], ipa[ 3 ] );
+ }
+ return proto_tree_add_item( tree, hf_knxip_ip_address, tvb, offset, 4, ENC_BIG_ENDIAN );
+}
+
+static proto_item* knxip_tree_add_knx_address( proto_tree* tree, gint hfindex, tvbuff_t* tvb, gint offset, gchar* output, gint output_max )
+{
+ guint16 value = tvb_get_ntohs( tvb, offset );
+ gchar text[ 32 ];
+ g_snprintf( text, sizeof text, "%u.%u.%u", (value >> 12) & 0xF, (value >> 8) & 0xF, value & 0xFF );
+ if( output ) g_snprintf( output, output_max, "%s", text );
+ proto_item* new_item = proto_tree_add_item( tree, hfindex, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_item_append_text( new_item, " = %s", text );
+ return new_item;
+}
+
+static proto_item* knxip_tree_add_bit( proto_tree* tree, tvbuff_t* tvb, gint offset, gint bitpos, const gchar* name, gchar* output, gint output_max )
+{
+ gchar format[ 32 ] = ".... .... = %s: %d";
+ guint8 value = (tvb_get_guint8( tvb, offset ) >> bitpos) & 1;
+ format[ 7 - bitpos + (bitpos < 4) ] = '0' + value;
+
+ if( value && output )
+ {
+ if( *output )
+ {
+ do { ++output; --output_max; } while( *output );
+ g_snprintf( output, output_max, " | " );
+ while( *output ) { ++output; --output_max; }
+ }
+
+ g_snprintf( output, output_max, "%s", name );
+ }
+
+ return proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, 1, NULL, format, name, value );
+}
+
+static proto_item* knxip_tree_add_ip_assignment( proto_tree* tree, gint hfindex, tvbuff_t* tvb, gint offset, guint8 manual )
+{
+ proto_item* node = proto_tree_add_item( tree, hfindex, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree* list = proto_item_add_subtree( node, ett_ip_assignment );
+ gchar output[ 128 ];
+ *output = '\0';
+ knxip_tree_add_bit( list, tvb, offset, 2 + manual, "AutoIP", output, sizeof output );
+ knxip_tree_add_bit( list, tvb, offset, 1 + manual, "DHCP", output, sizeof output );
+ knxip_tree_add_bit( list, tvb, offset, 0 + manual, "BootP", output, sizeof output );
+ if( manual ) knxip_tree_add_bit( list, tvb, offset, 0, "manual", output, sizeof output );
+ if( *output ) proto_item_append_text( node, " = %s", output );
+ return node;
+}
+
+/* Dissect HPAI field
+*/
+static guint8 dissect_hpai( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok, gchar* name, guint8 check_protocol )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+
+ proto_item* hpai_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "HPAI %s Endpoint", name );
+
+ gchar info[ 80 ];
+ gchar* output = info;
+ gint output_max = sizeof info;
+ g_snprintf( info, sizeof info, "???" );
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( hpai_item, "Missing " );
+ expert_add_info_format( pinfo, hpai_item, KIP_ERROR, "Expected: 8 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ /* 1 byte Structure Length */
+ proto_tree* hpai_tree = proto_item_add_subtree( hpai_item, ett_hpai );
+ proto_item* length_item = knxip_tree_add_length( hpai_tree, tvb, offset, struct_len );
+ proto_item* node;
+
+ gint end_pos = offset + eff_struct_len;
+ offset++;
+
+ if( struct_len != 8 )
+ {
+ knxip_item_illegal_length( length_item, pinfo, "Expected: 8 bytes" );
+ ok = 0;
+ }
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+
+ if( ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ ok = 0;
+ }
+ }
+ else if( struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, hpai_item, KIP_ERROR, "Missing 1 byte Host Protocol" );
+ ok = 0;
+ }
+ else
+ {
+ /* 1 byte Host Protocol */
+ guint8 host_protocol = tvb_get_guint8( tvb, offset );
+ const gchar* host_protocol_name = "???";
+ guint8 protocol_error = 0;
+
+ node = proto_tree_add_item( hpai_tree, hf_knxip_host_protocol, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( host_protocol == KIP_IPV4_UDP )
+ {
+ host_protocol_name = "UDP";
+ if( check_protocol )
+ {
+ if( knxip_host_protocol != IP_PROTO_UDP && knxip_host_protocol != IP_PROTO_UDPLITE )
+ {
+ protocol_error = 1;
+ }
+ }
+ }
+ else if( host_protocol == KIP_IPV4_TCP )
+ {
+ host_protocol_name = "TCP";
+ if( check_protocol )
+ {
+ if( knxip_host_protocol != IP_PROTO_TCP )
+ {
+ protocol_error = 1;
+ }
+ }
+ }
+ else
+ {
+ protocol_error = 2;
+ }
+
+ if( protocol_error )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, (protocol_error == 1) ? "Wrong Host Protocol" : "Expected: 0x01 or 0x02" );
+ ok = 0;
+ }
+
+ offset++;
+
+ if( struct_len < 6 )
+ {
+ expert_add_info_format( pinfo, hpai_item, KIP_ERROR, "Missing 4 bytes IP Address" );
+ ok = 0;
+ }
+ else
+ {
+ /* 4 bytes IP Address */
+ node = knxip_tree_add_ip_address( hpai_tree, tvb, offset, output, output_max );
+
+ if( host_protocol == KIP_IPV4_TCP && strcmp( output, "0.0.0.0" ) != 0 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 0.0.0.0" );
+ ok = 0;
+ }
+
+ offset += 4;
+
+ while( *output ) { ++output; --output_max; }
+ if( output_max > 1 ) { *output++ = ':'; --output_max; }
+ g_snprintf( output, output_max, "???" );
+
+ if( struct_len < 8 )
+ {
+ expert_add_info_format( pinfo, hpai_item, KIP_ERROR, "Missing 2 bytes Port Number" );
+ ok = 0;
+ }
+ else
+ {
+ /* 2 bytes Port Number */
+ guint16 port = tvb_get_ntohs( tvb, offset );
+
+ g_snprintf( output, output_max, "%u", port );
+ while( *output ) { ++output; --output_max; }
+
+ node = proto_tree_add_item( hpai_tree, hf_knxip_port, tvb, offset, 2, ENC_BIG_ENDIAN );
+
+ if( host_protocol == KIP_IPV4_TCP && port != 0 )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 0" );
+ ok = 0;
+ }
+
+ offset += 2;
+ }
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( hpai_tree, tvb, offset, end_pos - offset );
+ ok = 0;
+ }
+
+ proto_item_append_text( hpai_item, ": %s %s", info, host_protocol_name );
+ }
+ }
+
+ col_append_fstr( pinfo->cinfo, COL_INFO, " @%s", info );
+ proto_item_append_text( item, ", %s @ %s", name, info );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( hpai_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset += struct_len;
+ return struct_len;
+}
+
+/* Dissect CRI (= Connection Request Information)
+*/
+static guint8 dissect_cri( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+
+ proto_item* cri_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "CRI" );
+
+ guint8 conn_type = 0;
+ const gchar* conn_type_name = NULL;
+ guint8 ok = 0;
+ gchar extra_text[ 32 ];
+ *extra_text = '\0';
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( cri_item, "Missing " );
+ expert_add_info_format( pinfo, cri_item, KIP_ERROR, "Expected: min 2 bytes" );
+ //ok = 0;
+ }
+ else
+ {
+ proto_tree* cri_tree = proto_item_add_subtree( cri_item, ett_cri );
+ proto_item* length_item = knxip_tree_add_length( cri_tree, tvb, offset, struct_len );
+ proto_item* type_item = NULL;
+ guint8 length_ok = 1;
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+ //ok = 0;
+ length_ok = 0;
+ }
+
+ if( struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, cri_item, KIP_ERROR, "Missing 1 byte Connection Type" );
+ //ok = 0;
+ }
+ else
+ {
+ conn_type = tvb_get_guint8( tvb, offset + 1 );
+ type_item = proto_tree_add_item( cri_tree, hf_knxip_connection_type, tvb, offset + 1, 1, ENC_BIG_ENDIAN );
+ conn_type_name = try_val_to_str( conn_type, connection_type_vals );
+ if( !conn_type_name )
+ {
+ proto_item_prepend_text( type_item, "? " );
+ expert_add_info_format( pinfo, type_item, KIP_ERROR, "Unknown" );
+ //ok = 0;
+
+ if( struct_len > 2 )
+ {
+ knxip_tree_add_unknown_data( cri_tree, tvb, offset + 2, struct_len - 2 );
+ }
+ }
+ else
+ {
+ proto_item_append_text( cri_item, " %s", conn_type_name );
+ ok = 1;
+
+ switch( conn_type )
+ {
+ case KIP_DEVICE_MGMT_CONNECTION:
+ case KIP_REMLOG_CONNECTION:
+ case KIP_REMCONF_CONNECTION:
+ case KIP_OBJSVR_CONNECTION:
+ if( struct_len > 2 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 2 bytes" );
+ length_ok = 0;
+
+ knxip_tree_add_unknown_data( cri_tree, tvb, offset + 2, struct_len - 2 );
+ ok = 0;
+ }
+ break;
+
+ case KIP_TUNNEL_CONNECTION:
+ if( (struct_len != 4) && (struct_len != 6) )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 4 or 6 bytes" );
+ length_ok = 0;
+ ok = 0;
+ }
+ if( struct_len >= 3 )
+ {
+ guint8 knx_layer = tvb_get_guint8( tvb, offset + 2 );
+ const gchar* knx_layer_name = try_val_to_str( knx_layer, knx_layer_vals );
+ proto_item* layer_item = proto_tree_add_item( cri_tree, hf_knxip_knx_layer, tvb, offset + 2, 1, ENC_BIG_ENDIAN );
+ proto_item_append_text( cri_item, ", Layer: %s", knx_layer_name ? knx_layer_name : "Unknown" );
+ if( !knx_layer_name )
+ {
+ proto_item_prepend_text( layer_item, "? " );
+ expert_add_info_format( pinfo, layer_item, KIP_ERROR, "Expected: 0x02" );
+ ok = 0;
+ }
+
+ if( struct_len < 4 )
+ {
+ expert_add_info_format( pinfo, cri_item, KIP_ERROR, "Missing Reserved byte" );
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_reserved( cri_tree, tvb, offset + 3, pinfo, &ok );
+ }
+ if( struct_len >= 6 )
+ {
+ knxip_tree_add_knx_address( cri_tree, hf_knxip_knx_address, tvb, offset + 4, extra_text, sizeof extra_text );
+ }
+ if( struct_len > 6 )
+ {
+ knxip_tree_add_unknown_data( cri_tree, tvb, offset + 6, struct_len - 6 );
+ ok = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if( !length_ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+ }
+
+ conn_type_name = try_val_to_str( conn_type, conn_type_vals );
+ if( !conn_type_name )
+ {
+ ok = 0;
+ }
+ else
+ {
+ if( pinfo )
+ {
+ column_info* cinfo = pinfo->cinfo;
+ col_prepend_fstr( cinfo, COL_INFO, "%s ", conn_type_name );
+ if( *extra_text )
+ {
+ col_append_fstr( cinfo, COL_INFO, ", %s", extra_text );
+ }
+ }
+
+ proto_item_append_text( item, ", %s", conn_type_name );
+ if( *extra_text )
+ {
+ proto_item_append_text( item, ", %s", extra_text );
+ }
+ }
+
+ if( !ok )
+ {
+ proto_item_prepend_text( cri_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset += struct_len;
+ return struct_len;
+}
+
+/* Dissect CRD (= Connection Response Data)
+*/
+static guint8 dissect_crd( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+
+ proto_item* crd_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "CRD" );
+
+ guint8 conn_type = 0;
+ const gchar* conn_type_name = NULL;
+ guint8 ok = 0;
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( crd_item, "Missing " );
+ expert_add_info_format( pinfo, crd_item, KIP_ERROR, "Expected: min 2 bytes" );
+ //ok = 0;
+ }
+ else
+ {
+ proto_tree* crd_tree = proto_item_add_subtree( crd_item, ett_crd );
+ proto_item* length_item = knxip_tree_add_length( crd_tree, tvb, offset, struct_len );
+ proto_item* type_item = NULL;
+ guint8 length_ok = 1;
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+ //ok = 0;
+ length_ok = 0;
+ }
+
+ if( struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, crd_item, KIP_ERROR, "Missing 1 byte Connection Type" );
+ //ok = 0;
+ }
+ else
+ {
+ conn_type = tvb_get_guint8( tvb, offset + 1 );
+ type_item = proto_tree_add_item( crd_tree, hf_knxip_connection_type, tvb, offset + 1, 1, ENC_BIG_ENDIAN );
+ conn_type_name = try_val_to_str( conn_type, connection_type_vals );
+ if( !conn_type_name )
+ {
+ proto_item_prepend_text( type_item, "? " );
+ expert_add_info_format( pinfo, type_item, KIP_ERROR, "Unknown" );
+ //ok = 0;
+
+ if( struct_len > 2 )
+ {
+ knxip_tree_add_unknown_data( crd_tree, tvb, offset + 2, struct_len - 2 );
+ }
+ }
+ else
+ {
+ proto_item_append_text( crd_item, " %s", conn_type_name );
+ ok = 1;
+
+ switch( conn_type )
+ {
+ case KIP_DEVICE_MGMT_CONNECTION:
+ case KIP_REMLOG_CONNECTION:
+ case KIP_REMCONF_CONNECTION:
+ case KIP_OBJSVR_CONNECTION:
+ if( struct_len > 2 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 2 bytes" );
+ knxip_tree_add_unknown_data( crd_tree, tvb, offset + 2, struct_len - 2 );
+ ok = 0;
+ length_ok = 0;
+ }
+ break;
+
+ case KIP_TUNNEL_CONNECTION:
+ if( struct_len != 4 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 4 bytes" );
+ ok = 0;
+ length_ok = 0;
+ }
+
+ if( struct_len < 4 )
+ {
+ expert_add_info_format( pinfo, crd_item, KIP_ERROR, "Missing 2 bytes KNX Address" );
+ //ok = 0;
+ if( struct_len > 2 )
+ {
+ knxip_tree_add_unknown_data( crd_tree, tvb, offset + 2, struct_len - 2 );
+ }
+ }
+ else
+ {
+ gchar output[ 40 ];
+ knxip_tree_add_knx_address( crd_tree, hf_knxip_knx_address, tvb, offset + 2, output, sizeof output );
+ proto_item_append_text( crd_item, ", KNX Address: %s", output );
+ if( pinfo )
+ {
+ col_append_fstr( pinfo->cinfo, COL_INFO, ", %s", output );
+ }
+ if( item )
+ {
+ proto_item_append_text( item, ", %s", output );
+ }
+ if( struct_len > 4 )
+ {
+ knxip_tree_add_unknown_data( crd_tree, tvb, offset + 4, struct_len - 4 );
+ //ok = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if( !length_ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+ }
+
+ conn_type_name = try_val_to_str( conn_type, conn_type_vals );
+ if( pinfo && conn_type_name ) col_prepend_fstr( pinfo->cinfo, COL_INFO, "%s ", conn_type_name );
+ proto_item_append_text( item, ", %s", conn_type_name ? conn_type_name : "???" );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( crd_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset += struct_len;
+ return struct_len;
+}
+
+/* Dissect Connection Header
+*/
+static guint8 dissect_cnhdr( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok, guint8 response )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+
+ proto_item* cnhdr_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "Connection Header" );
+
+ guint8 ok = 0;
+ gchar info[ 100 ];
+ gint output_max = sizeof info;
+ gchar* output = info;
+
+ *output++ = '#';
+ output_max--;
+ g_snprintf( output, output_max, "???" );
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( cnhdr_item, "Missing " );
+ expert_add_info_format( pinfo, cnhdr_item, KIP_ERROR, "Expected: 4 bytes" );
+ }
+ else
+ {
+ proto_tree* cnhdr_tree = proto_item_add_subtree( cnhdr_item, ett_cnhdr );
+ proto_item* length_item = knxip_tree_add_length( cnhdr_tree, tvb, offset, struct_len );
+
+ gint end_pos = offset + eff_struct_len;
+ offset++;
+
+ if( struct_len == 4 )
+ {
+ ok = 1;
+ }
+ else
+ {
+ knxip_item_illegal_length( length_item, pinfo, "Expected: 4 bytes" );
+ }
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+
+ if( ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ ok = 0;
+ }
+ }
+
+ if( struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, cnhdr_item, KIP_ERROR, "Missing 1 byte Channel" );
+ //ok = 0;
+ }
+ else
+ {
+ g_snprintf( output, output_max, "%02X:", tvb_get_guint8( tvb, offset ) );
+ while( *output ) { ++output; --output_max; }
+ g_snprintf( output, output_max, "???" );
+
+ proto_tree_add_item( cnhdr_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ if( struct_len < 3 )
+ {
+ expert_add_info_format( pinfo, cnhdr_item, KIP_ERROR, "Missing 1 byte Sequence Counter" );
+ //ok = 0;
+ }
+ else
+ {
+ g_snprintf( output, output_max, "%u", tvb_get_guint8( tvb, offset ) );
+ while( *output ) { ++output; --output_max; }
+
+ proto_tree_add_item( cnhdr_tree, hf_knxip_seq_counter, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ if( response )
+ {
+ if( output_max > 1 )
+ {
+ *output++ = ' ';
+ output_max--;
+ g_snprintf( output, output_max, "???" );
+ }
+ }
+
+ if( struct_len < 4 )
+ {
+ expert_add_info_format( pinfo, cnhdr_item, KIP_ERROR, "Missing 1 byte %s", response ? "Status" : "Reserved" );
+ //ok = 0;
+ }
+ else
+ {
+ if( response )
+ {
+ g_snprintf( output, output_max, "%s", val_to_str( tvb_get_guint8( tvb, offset ), error_vals, "Error 0x%02x" ) );
+ knxip_tree_add_status( cnhdr_tree, tvb, offset );
+ }
+ else
+ {
+ knxip_tree_add_reserved( cnhdr_tree, tvb, offset, pinfo, &ok );
+ }
+
+ offset++;
+ }
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( cnhdr_tree, tvb, offset, end_pos - offset );
+ //ok = 0;
+ }
+
+ proto_item_append_text( cnhdr_item, ": %s", info );
+ }
+ }
+
+ if( pinfo ) col_append_fstr( pinfo->cinfo, COL_INFO, " %s", info );
+ proto_item_append_text( item, ", %s", info );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( cnhdr_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset += struct_len;
+ return struct_len;
+}
+
+/* Dissect tunneling feature frames.
+*/
+static void dissect_tunneling_feature( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok, guint16 service )
+{
+ column_info* cinfo = pinfo->cinfo;
+ gint offset = *p_offset;
+ gint remaining_len;
+ proto_item* node;
+ guint8 c;
+ const gchar* name;
+ guint8 ok = 1;
+ guint8 isResponse = (service == KIP_TUNNELING_FEATURE_RESPONSE);
+ guint8 status = 0;
+
+ /* Connection Header */
+ dissect_cnhdr( tvb, pinfo, item, tree, &offset, &ok, FALSE );
+
+ remaining_len = tvb_captured_length_remaining( tvb, offset );
+
+ /* 1 byte Feature Identifier */
+ if( remaining_len <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Feature Identifier" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ c = tvb_get_guint8( tvb, offset );
+ name = try_val_to_str( c, tunneling_feature_id_vals );
+ if( !name ) name = "Unknown";
+ node = proto_tree_add_item( tree, hf_knxip_tunnel_feature, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_item_append_text( node, " = %s", name );
+ proto_item_append_text( item, " %s", name );
+ col_append_fstr( cinfo, COL_INFO, " %s", name );
+
+ ++offset;
+ --remaining_len;
+ }
+
+ /* 1 byte Return Code / Reserved */
+ name = isResponse ? "Status" : "Reserved";
+ if( remaining_len <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? %s", name );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ status = tvb_get_guint8( tvb, offset );
+ proto_tree_add_item( tree, isResponse ? hf_knxip_status : hf_knxip_reserved, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( isResponse && (status != 0 || remaining_len == 1) )
+ {
+ proto_item_append_text( item, " E=$%02X", status );
+ col_append_fstr( cinfo, COL_INFO, " E=$%02X", status );
+ }
+
+ ++offset;
+ --remaining_len;
+ }
+
+ /* Feature Value */
+ if( remaining_len <= 0 )
+ {
+ if( service != KIP_TUNNELING_FEATURE_GET && status == 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Feature Value" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Missing" );
+ ok = 0;
+ }
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, remaining_len, cinfo, item, "Feature Value", " $", " $" );
+ if( service == KIP_TUNNELING_FEATURE_GET )
+ {
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Unexpected" );
+ ok = 0;
+ }
+ offset += remaining_len;
+ }
+
+ *p_offset = offset;
+
+ if( p_ok && !ok ) *p_ok = 0;
+}
+
+/* Dissect cEMI
+*/
+static void dissect_cemi( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, gint* p_offset )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+
+ /* Call the cEMI data dissector for the remaining bytes
+ */
+ tvb = tvb_new_subset_remaining( tvb, offset );
+
+ dissector_handle_t cemi_handle = find_dissector( "cemi" );
+ if( cemi_handle )
+ {
+ call_dissector( cemi_handle, tvb, pinfo, tree );
+ }
+
+
+ *p_offset = offset + remaining_len;
+}
+
+/* Dissect ROUTING_LOSS
+*/
+static guint8 dissect_routing_loss( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+ guint8 ok = 0;
+
+ proto_item* info_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, struct_len, "Loss Info" );
+
+ gchar info[ 16 ];
+ g_snprintf( info, sizeof info, "???" );
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( info_item, "Missing " );
+ expert_add_info_format( pinfo, info_item, KIP_ERROR, "Expected: 4 bytes" );
+ }
+ else
+ {
+ proto_tree* info_tree = proto_item_add_subtree( info_item, ett_loss );
+ proto_item* length_item = knxip_tree_add_length( info_tree, tvb, offset, struct_len );
+
+ gint end_pos = offset + eff_struct_len;
+ offset++;
+
+ if( struct_len == 4 )
+ {
+ ok = 1;
+ }
+ else
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 4 bytes" );
+ }
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+ ok = 0;
+ }
+
+ if( !ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+
+ if( struct_len >= 2 )
+ {
+ knxip_tree_add_status( info_tree, tvb, offset );
+ offset++;
+
+ /* 2 bytes Lost Messages */
+ if( struct_len >= 4 )
+ {
+ guint16 loss = tvb_get_ntohs( tvb, offset );
+ g_snprintf( info, sizeof info, "%u", loss );
+ proto_tree_add_item( info_tree, hf_knxip_routing_loss, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( info_tree, tvb, offset, end_pos - offset );
+ }
+
+ proto_item_append_text( info_item, ": %s", info );
+ }
+ }
+
+ if( pinfo ) col_append_fstr( pinfo->cinfo, COL_INFO, ": %s", info );
+ proto_item_append_text( item, ": %s", info );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( info_item, "? " );
+ }
+
+ *p_offset += struct_len;
+ return ok;
+}
+
+/* Dissect ROUTING_BUSY
+*/
+static guint8 dissect_routing_busy( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+ guint8 ok = 0;
+
+ proto_item* info_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "Busy Info" );
+
+ gchar info[ 16 ];
+ g_snprintf( info, sizeof info, "???" );
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( info_item, "Missing " );
+ expert_add_info_format( pinfo, info_item, KIP_ERROR, "Expected: 6 bytes" );
+ }
+ else
+ {
+ proto_tree* info_tree = proto_item_add_subtree( info_item, ett_loss );
+ proto_item* length_item = knxip_tree_add_length( info_tree, tvb, offset, struct_len );
+
+ gint end_pos = offset + eff_struct_len;
+ offset++;
+
+ if( struct_len == 6 )
+ {
+ ok = 1;
+ }
+ else
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 6 bytes" );
+ }
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+ ok = 0;
+ }
+
+ if( !ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+
+ if( struct_len >= 2 )
+ {
+ knxip_tree_add_status( info_tree, tvb, offset );
+ offset++;
+
+ if( struct_len >= 4 )
+ {
+ /* 2 bytes Wait Time (ms) */
+ proto_item* new_item = proto_tree_add_item( info_tree, hf_knxip_busy_time, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_item_append_text( new_item, " ms" );
+ g_snprintf( info, sizeof info, "%u ms", tvb_get_ntohs( tvb, offset ) );
+ offset += 2;
+
+ if( struct_len >= 6 )
+ {
+ /* 2 bytes Control */
+ proto_tree_add_item( info_tree, hf_knxip_busy_control, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+ }
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( info_tree, tvb, offset, end_pos - offset );
+ }
+
+ proto_item_append_text( info_item, ": %s", info );
+ }
+ }
+
+ if( pinfo ) col_append_fstr( pinfo->cinfo, COL_INFO, ": %s", info );
+ proto_item_append_text( item, ": %s", info );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( info_item, "? " );
+ }
+
+ *p_offset += struct_len;
+ return ok;
+}
+
+/* Dissect SELECTOR field
+*/
+static guint8 dissect_selector( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+ guint8 ok = 0;
+
+ proto_item* info_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "Selector" );
+
+ gchar info[ 40 ];
+ g_snprintf( info, sizeof info, "???" );
+
+ if( struct_len <= 0 )
+ {
+ proto_item_prepend_text( info_item, "Missing " );
+ expert_add_info_format( pinfo, info_item, KIP_ERROR, "Expected: min 2 bytes" );
+ //ok = 0;
+ }
+ else
+ {
+ proto_tree* info_tree = proto_item_add_subtree( info_item, ett_loss );
+ proto_item* length_item = knxip_tree_add_length( info_tree, tvb, offset, struct_len );
+ guint8 length_ok = 1;
+
+ gint end_pos = offset + eff_struct_len;
+ offset++;
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ //ok = 0;
+ length_ok = 0;
+ struct_len = (guint8) remaining_len;
+ }
+
+ if( struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: min 2 bytes" );
+ //ok = 0;
+ length_ok = 0;
+ }
+ else
+ {
+ /* 1 byte Selection Type */
+ guint8 sel = tvb_get_guint8( tvb, offset );
+ proto_item* type_item = proto_tree_add_item( info_tree, hf_knxip_selector, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_item_append_text( type_item, " = %s", (sel == SELECT_PROGMODE) ? "ProgMode" : (sel == SELECT_MACADDRESS) ? "MAC" : "Unknown" );
+ offset++;
+ ok = 1;
+
+ if( sel == SELECT_PROGMODE )
+ {
+ g_snprintf( info, sizeof info, "ProgMode" );
+
+ if( struct_len != 2 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 2 bytes" );
+ ok = 0;
+ length_ok = 0;
+ }
+ }
+ else if( sel == SELECT_MACADDRESS )
+ {
+ gchar* output = info;
+ gint output_max = sizeof info;
+ g_snprintf( output, output_max, "MAC=" );
+ while( *output ) { ++output; --output_max; }
+ g_snprintf( output, output_max, "???" );
+
+ if( struct_len != 8 )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: 8 bytes" );
+ ok = 0;
+ length_ok = 0;
+ }
+
+ if( struct_len >= 8 )
+ {
+ /* 6 bytes MAC Address */
+ guint8 mac[ 6 ];
+ tvb_memcpy( tvb, mac, offset, 6 );
+ g_snprintf( output, output_max, "%02x:%02x:%02x:%02x:%02x:%02x", mac[ 0 ], mac[ 1 ], mac[ 2 ], mac[ 3 ], mac[ 4 ], mac[ 5 ] );
+ proto_tree_add_item( info_tree, hf_knxip_mac_address, tvb, offset, 6, ENC_NA );
+ offset += 6;
+ }
+ }
+ else
+ {
+ proto_item_prepend_text( type_item, "? " );
+ expert_add_info_format( pinfo, type_item, KIP_ERROR, "Unknown" );
+ ok = 0;
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( info_tree, tvb, offset, end_pos - offset );
+ ok = 0;
+ }
+
+ proto_item_append_text( info_item, ": %s", info );
+ }
+
+ if( !length_ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+ }
+
+ if( pinfo ) col_append_fstr( pinfo->cinfo, COL_INFO, " %s", info );
+ proto_item_append_text( item, ", %s", info );
+
+ if( !ok )
+ {
+ proto_item_prepend_text( info_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset += struct_len;
+ if( p_ok && !ok ) *p_ok = 0;
+ return struct_len;
+}
+
+/* Dissect DevInfo DIB
+*/
+static guint8 dissect_dib_devinfo( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ gchar* info = output;
+ guint8 prog_mode = 0;
+ guint8 ok = 1;
+
+ if( struct_len != 54 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: 54 bytes" );
+ ok = 0;
+ }
+
+ if( struct_len >= 3 )
+ {
+ /* 1 byte KNX Medium */
+ guint8 knx_medium = tvb_get_guint8( tvb, offset );
+ proto_item* item = proto_tree_add_item( dib_tree, hf_knxip_knx_medium, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_tree* tree = proto_item_add_subtree( item, ett_medium );
+ knxip_tree_add_bit( tree, tvb, offset, 5, "IP", NULL, 0 );
+ knxip_tree_add_bit( tree, tvb, offset, 4, "RF", NULL, 0 );
+ knxip_tree_add_bit( tree, tvb, offset, 3, "PL132", NULL, 0 );
+ knxip_tree_add_bit( tree, tvb, offset, 2, "PL110", NULL, 0 );
+ knxip_tree_add_bit( tree, tvb, offset, 1, "TP1", NULL, 0 );
+ knxip_tree_add_bit( tree, tvb, offset, 0, "TP0", NULL, 0 );
+
+ /* Check for missing or multiple medium */
+ {
+ guint8 data = knx_medium;
+ guint8 media = 0;
+ while( data )
+ {
+ if( data & 1 )
+ {
+ media++;
+ }
+ data >>= 1;;
+ }
+
+ if( media != 1 )
+ {
+ expert_add_info_format( pinfo, item, KIP_WARNING, media ? "Multiple" : "Missing" );
+ }
+ }
+
+ offset++;
+
+ if( struct_len >= 4 )
+ {
+ /* 1 byte Device Status */
+ guint8 status = tvb_get_guint8( tvb, offset );
+ item = proto_tree_add_item( dib_tree, hf_knxip_device_status, tvb, offset, 1, ENC_BIG_ENDIAN );
+ tree = proto_item_add_subtree( item, ett_status );
+ proto_tree_add_item( tree, hf_knxip_program_mode, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ if( status & 0x01 )
+ {
+ proto_item_append_text( item, " (ProgMode)" );
+ prog_mode = 1;
+ }
+
+ offset++;
+
+ if( struct_len >= 6 )
+ {
+ /* 2 bytes KNX Address */
+ item = knxip_tree_add_knx_address( dib_tree, hf_knxip_knx_address, tvb, offset, output, output_max );
+
+ if( output )
+ {
+ while( *output ) { output++; output_max--; }
+ }
+
+ offset += 2;
+
+ if( struct_len >= 8 )
+ {
+ /* 2 bytes Project Installation Identifier */
+ guint16 project_id = tvb_get_ntohs( tvb, offset );
+ item = proto_tree_add_item( dib_tree, hf_knxip_project_id, tvb, offset, 2, ENC_BIG_ENDIAN );
+ tree = proto_item_add_subtree( item, ett_projectid );
+ proto_tree_add_item( tree, hf_knxip_project_number, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_tree_add_item( tree, hf_knxip_installation_number, tvb, offset, 2, ENC_BIG_ENDIAN );
+ proto_item_append_text( item, " (%u:%u)", project_id / 16, project_id % 16 );
+
+ offset += 2;
+
+ if( struct_len >= 14 )
+ {
+ /* 6 bytes KNX Serial Number */
+ proto_tree_add_item( dib_tree, hf_knxip_serial_number, tvb, offset, 6, ENC_BIG_ENDIAN );
+ offset += 6;
+ }
+
+ if( struct_len >= 18 )
+ {
+ /* 4 bytes Routing Multicast Address */
+ proto_tree_add_item( dib_tree, hf_knxip_multicast_address, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 24 )
+ {
+ /* 6 bytes MAC Address */
+ proto_tree_add_item( dib_tree, hf_knxip_mac_address, tvb, offset, 6, ENC_NA );
+ offset += 6;
+
+ if( struct_len >= 54 )
+ {
+ /* 30 bytes Friendly Name */
+ proto_tree_add_item( dib_tree, hf_knxip_friendly_name, tvb, offset, 30, ENC_ASCII | ENC_NA );
+
+ if( output )
+ {
+ if( output_max > 33 )
+ {
+ *output++ = ' ';
+ *output++ = '"';
+ output_max -= 2;
+ tvb_get_nstringz( tvb, offset, 30, (guint8 *) output );
+ output[ 30 ] = '\0';
+ while( *output ) { output++; output_max--; }
+ *output++ = '"';
+ output_max--;
+ }
+ }
+
+ offset += 30;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if( output )
+ {
+ *output = '\0';
+
+ if( output == info )
+ {
+ g_snprintf( output, output_max, "???" );
+ while( *output ) { output++; output_max--; }
+ }
+
+ if( prog_mode )
+ {
+ g_snprintf( output, output_max, " PROGMODE" );
+ }
+
+ proto_item_append_text( dib_item, ": %s", info );
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect SuppSvc DIB
+*/
+static guint8 dissect_dib_suppsvc( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len )
+{
+ gint offset = *p_offset;
+ gint end_pos = offset - 2 + struct_len;
+ guint8 ok = 1;
+ gchar separator = ':';
+ guint8 sf_count[ 8 ] = { 0 };
+
+ if( struct_len & 1 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: even number" );
+ ok = 0;
+ }
+
+ while( offset + 2 <= end_pos )
+ {
+ guint8 service_family = tvb_get_guint8( tvb, offset );
+ guint8 version = tvb_get_guint8( tvb, offset + 1 );
+ const gchar* service_family_name = try_val_to_str( service_family, knxip_service_family_vals );
+ proto_item* item = proto_tree_add_none_format( dib_tree, hf_folder, tvb, offset, 2, "KNXnet/IP %s v%u",
+ service_family_name ? service_family_name : "Unknown Service Family", version );
+ proto_tree* tree = proto_item_add_subtree( item, ett_service_family );
+
+ /* 1 byte Service Family ID */
+ proto_tree_add_item( tree, hf_knxip_service_family, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ /* 1 byte Service Family Version */
+ proto_tree_add_item( tree, hf_knxip_service_version, tvb, offset + 1, 1, ENC_BIG_ENDIAN );
+
+ if( service_family >= KIP_SERVICE_TUNNELING && service_family_name )
+ {
+ proto_item_append_text( dib_item, "%c %s", separator, service_family_name );
+ separator = ',';
+ }
+
+ if( service_family < 8 )
+ {
+ ++sf_count[ service_family ];
+ }
+
+ offset += 2;
+ }
+
+ if( !sf_count[ KIP_SERVICE_CORE ] )
+ {
+ expert_add_info_format( pinfo, dib_item, KIP_WARNING, "Missing: Core (0x02)" );
+ }
+ if( !sf_count[ KIP_SERVICE_MANAGEMENT ] )
+ {
+ expert_add_info_format( pinfo, dib_item, KIP_WARNING, "Missing: Device Management (0x03)" );
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect IpConfig DIB
+*/
+static guint8 dissect_dib_ipconfig( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ guint8 ok = 1;
+ gchar text[ 32 ];
+
+ if( struct_len != 16 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: 16 bytes" );
+ ok = 0;
+ }
+
+ if( struct_len < 6 )
+ {
+ g_snprintf( text, sizeof text, "???" );
+ }
+ else
+ {
+ /* 4 bytes IP Address */
+ knxip_tree_add_ip_address( dib_tree, tvb, offset, text, sizeof text );
+ offset += 4;
+
+ if( struct_len >= 10 )
+ {
+ /* 4 bytes Subnet Mask */
+ proto_tree_add_item( dib_tree, hf_knxip_ip_subnet, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 14 )
+ {
+ /* 4 bytes Default Gateway */
+ proto_tree_add_item( dib_tree, hf_knxip_ip_gateway, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 15 )
+ {
+ /* 1 byte IP Capabilities */
+ knxip_tree_add_ip_assignment( dib_tree, hf_knxip_ip_caps, tvb, offset, 0 );
+ offset++;
+
+ if( struct_len >= 16 )
+ {
+ /* 1 byte IP Assignment Method */
+ knxip_tree_add_ip_assignment( dib_tree, hf_knxip_ip_assign, tvb, offset, 1 );
+ offset++;
+ }
+ }
+ }
+ }
+ }
+
+ proto_item_append_text( dib_item, ": %s", text );
+ if( output ) g_snprintf( output, output_max, "%s", text );
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect CurConfig DIB
+*/
+static guint8 dissect_dib_curconfig( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ guint8 ok = 1;
+ gchar text[ 32 ];
+
+ if( struct_len != 20 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: 20 bytes" );
+ ok = 0;
+ }
+
+ if( struct_len < 6 )
+ {
+ g_snprintf( text, sizeof text, "???" );
+ }
+ else
+ {
+ /* 4 bytes IP Address */
+ knxip_tree_add_ip_address( dib_tree, tvb, offset, text, sizeof text );
+ offset += 4;
+
+ if( struct_len >= 10 )
+ {
+ /* 4 bytes Subnet Mask */
+ proto_tree_add_item( dib_tree, hf_knxip_ip_subnet, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 14 )
+ {
+ /* 4 bytes Default Gateway */
+ proto_tree_add_item( dib_tree, hf_knxip_ip_gateway, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 18 )
+ {
+ /* 4 bytes DHCP Server */
+ proto_tree_add_item( dib_tree, hf_knxip_ip_dhcp, tvb, offset, 4, ENC_BIG_ENDIAN );
+ offset += 4;
+
+ if( struct_len >= 19 )
+ {
+ /* IP Assignment Method */
+ knxip_tree_add_ip_assignment( dib_tree, hf_knxip_ip_assign, tvb, offset, 1 );
+ offset++;
+
+ if( struct_len >= 20 )
+ {
+ /* Reserved Byte */
+ knxip_tree_add_reserved( dib_tree, tvb, offset, pinfo, &ok );
+ offset++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ proto_item_append_text( dib_item, ": %s", text );
+ if( output ) g_snprintf( output, output_max, "%s", text );
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect KnxAddr DIB
+*/
+static guint8 dissect_dib_knxaddr( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ guint8 ok = 1;
+ gchar text1[ 32 ];
+ gchar text2[ 32 ];
+
+ if( struct_len < 4 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: >= 4 bytes" );
+ g_snprintf( text1, sizeof text1, "???" );
+ ok = 0;
+ }
+ else
+ {
+ gint end_pos = offset - 2 + struct_len;
+
+ if( struct_len & 1 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: even number" );
+ ok = 0;
+ }
+
+ /* 2 bytes KNX Address */
+ knxip_tree_add_knx_address( dib_tree, hf_knxip_knx_address, tvb, offset, text1, sizeof text1 );
+ proto_item_append_text( dib_item, ": %s", text1 );
+ offset += 2;
+
+ while( offset + 2 <= end_pos )
+ {
+ /* 2 bytes Additional KNX Address */
+ knxip_tree_add_knx_address( dib_tree, hf_knxip_knx_address, tvb, offset, text2, sizeof text2 );
+ proto_item_append_text( dib_item, ", %s", text2 );
+ offset += 2;
+ }
+ }
+
+ if( output ) g_snprintf( output, output_max, "%s", text1 );
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect SecuredServices DIB
+*/
+static guint8 dissect_dib_secured_service_families( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len )
+{
+ gint offset = *p_offset;
+ gint end_pos = offset - 2 + struct_len;
+ guint8 ok = 1;
+ gchar separator = ':';
+
+ if( struct_len & 1 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: even number" );
+ ok = 0;
+ }
+
+ while( offset + 2 <= end_pos )
+ {
+ guint8 service_family = tvb_get_guint8( tvb, offset );
+ guint8 version = tvb_get_guint8( tvb, offset + 1 );
+ const gchar* service_family_name = try_val_to_str( service_family, knxip_service_family_vals );
+ proto_item* item = proto_tree_add_none_format( dib_tree, hf_folder, tvb, offset, 2, "KNXnet/IP %s v%u",
+ service_family_name ? service_family_name : "Unknown Service Family", version );
+ proto_tree* tree = proto_item_add_subtree( item, ett_service_family );
+
+ /* 1 byte Service Family ID */
+ proto_tree_add_item( tree, hf_knxip_service_family, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ /* 1 byte Security Version */
+ proto_tree_add_item( tree, hf_knxip_security_version, tvb, offset + 1, 1, ENC_BIG_ENDIAN );
+
+ if( service_family_name )
+ {
+ proto_item_append_text( dib_item, "%c %s", separator, service_family_name );
+ separator = ',';
+ }
+
+ offset += 2;
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect TunnelingInfo DIB
+*/
+static guint8 dissect_dib_tunneling_info( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len )
+{
+ gint offset = *p_offset;
+ guint8 ok = 1;
+
+ if( struct_len < 4 )
+ {
+ if( length_ok )
+ {
+ knxip_item_illegal_length( length_item, pinfo, "Expected: >= 4 bytes" );
+ ok = 0;
+ }
+ }
+ else
+ {
+ gint end_pos = offset - 2 + struct_len;
+ gchar separator = ':';
+
+ /* 2 bytes Max APDU Length */
+ proto_tree_add_item( dib_tree, hf_knxip_max_apdu_length, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+
+ if( struct_len & 3 )
+ {
+ if( length_ok )
+ {
+ knxip_item_illegal_length( length_item, pinfo, "Expected: 4 + n * 4 bytes" );
+ ok = 0;
+ }
+ }
+
+ while( offset + 4 <= end_pos )
+ {
+ guint8 flags = tvb_get_guint8( tvb, offset + 3 );
+ guint8 is_free = flags & 1;
+ gchar text[ 32 ];
+ proto_item* node;
+ proto_tree* list;
+
+ node = proto_tree_add_none_format( dib_tree, hf_folder, tvb, offset, 4, "Tunneling Slot" );
+ list = proto_item_add_subtree( node, ett_tunnel );
+
+ /* 2 bytes KNX Address, 1 byte reserved */
+ knxip_tree_add_knx_address( list, hf_knxip_knx_address, tvb, offset, text, sizeof text );
+ proto_item_append_text( node, ": %s Free=%u", text, is_free );
+ offset += 3;
+
+ /* 1 byte flags */
+ knxip_tree_add_bit( list, tvb, offset, 2, "Usable", NULL, 0 );
+ knxip_tree_add_bit( list, tvb, offset, 1, "Authorized", NULL, 0 );
+ knxip_tree_add_bit( list, tvb, offset, 0, "Free", NULL, 0 );
+ offset++;
+
+ if( !is_free )
+ {
+ proto_item_append_text( dib_item, "%c %s", separator, text );
+ separator = ',';
+ }
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect ExtDevInfo DIB
+*/
+static guint8 dissect_dib_extdevinfo( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ guint8 status = 0;
+ guint8 ok = 1;
+
+ if( struct_len != 8 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: 8 bytes" );
+ ok = 0;
+ }
+
+ if( struct_len >= 3 )
+ {
+ /* 1 byte Medium Status */
+ status = tvb_get_guint8( tvb, offset );
+ proto_tree_add_item( dib_tree, hf_knxip_medium_status, tvb, offset, 1, ENC_BIG_ENDIAN );
+ if( status )
+ {
+ proto_item_append_text( dib_item, ": MediumStatus=$%02X", status );
+ if( output ) g_snprintf( output, output_max, "MediumStatus=$%02X", status );
+ }
+
+ offset++;
+
+ if( struct_len >= 4 )
+ {
+ /* 1 byte reserved */
+ knxip_tree_add_reserved( dib_tree, tvb, offset, pinfo, &ok );
+ offset++;
+
+ if( struct_len >= 6 )
+ {
+ /* 2 bytes Max APDU Length */
+ proto_tree_add_item( dib_tree, hf_knxip_max_apdu_length, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+
+ if( struct_len >= 8 )
+ {
+ /* 2 bytes Mask Version */
+ proto_tree_add_item( dib_tree, hf_knxip_mask_version, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+ }
+ }
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect MfrData DIB
+*/
+static guint8 dissect_dib_mfrdata( tvbuff_t* tvb, packet_info* pinfo,
+ proto_item* dib_item, proto_tree* dib_tree, proto_item* length_item, guint8 length_ok,
+ gint* p_offset, guint8 struct_len, gchar* output, gint output_max )
+{
+ gint offset = *p_offset;
+ guint8 ok = 1;
+ gchar text[ 32 ];
+
+ if( struct_len < 4 )
+ {
+ if( length_ok ) knxip_item_illegal_length( length_item, pinfo, "Expected: >= 4 bytes" );
+ g_snprintf( text, sizeof text, "???" );
+ ok = 0;
+ }
+ else
+ {
+ proto_tree_add_item( dib_tree, hf_knxip_manufacturer_code, tvb, offset, 2, ENC_BIG_ENDIAN );
+ g_snprintf( text, sizeof text, "0x%04x", tvb_get_ntohs( tvb, offset ) );
+ offset += 2;
+ }
+
+ proto_item_append_text( dib_item, ": %s", text );
+ if( output ) g_snprintf( output, output_max, "%s", text );
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect DIB
+*/
+static guint8 dissect_dib( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree,
+ gint* p_offset, gchar** p_output, gint* p_output_max, gchar separator, guint8* p_count, guint8* p_ok )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ if( struct_len > 0 )
+ {
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+ gint end_pos = offset + eff_struct_len;
+ const gchar* dib_name = NULL;
+ guint8 dib_type = 0;
+ guint8 ok = 1;
+ guint8 length_ok = 1;
+
+ proto_item* dib_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "DIB" );
+ proto_tree* dib_tree = proto_item_add_subtree( dib_item, ett_dib );
+ proto_item* length_item = knxip_tree_add_length( dib_tree, tvb, offset, struct_len );
+
+ offset++;
+
+ if( struct_len > remaining_len )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ struct_len = (guint8) remaining_len;
+ ok = 0;
+ length_ok = 0;
+ }
+
+ if( eff_struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, dib_item, KIP_ERROR, "Missing 1 byte Description Type" );
+ ok = 0;
+ }
+ else
+ {
+ proto_item* type_item = proto_tree_add_item( dib_tree, hf_knxip_description_type, tvb, offset, 1, ENC_BIG_ENDIAN );
+ gchar info[ 80 ];
+
+ dib_type = tvb_get_guint8( tvb, offset );
+ dib_name = try_val_to_str( dib_type, descr_type_vals );
+ offset++;
+
+ if( !dib_name )
+ {
+ proto_item_append_text( dib_item, " ???" );
+ proto_item_append_text( type_item, " (Unknown)" );
+ }
+ else
+ {
+ proto_item_append_text( dib_item, " %s", dib_name );
+ }
+
+ if( p_count )
+ {
+ ++p_count[ dib_type ];
+ }
+
+ switch( dib_type )
+ {
+ case KIP_DIB_DEVICE_INFO:
+ ok &= dissect_dib_devinfo( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ if( p_output )
+ {
+ gchar* output = *p_output;
+ int output_max = *p_output_max;
+ g_snprintf( output, output_max, "%s", info );
+ while( *output ) { ++output; --output_max; }
+ *p_output = output;
+ *p_output_max = output_max;
+ }
+ break;
+
+ case KIP_DIB_SUPP_SVC_FAMILIES:
+ ok &= dissect_dib_suppsvc( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len );
+ break;
+
+ case KIP_DIB_IP_CONFIG:
+ ok &= dissect_dib_ipconfig( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ break;
+
+ case KIP_DIB_CUR_CONFIG:
+ ok &= dissect_dib_curconfig( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ break;
+
+ case KIP_DIB_KNX_ADDRESSES:
+ ok &= dissect_dib_knxaddr( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ break;
+
+ case KIP_DIB_SECURED_SERVICE_FAMILIES:
+ ok &= dissect_dib_secured_service_families( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len );
+ break;
+
+ case KIP_DIB_TUNNELING_INFO:
+ ok &= dissect_dib_tunneling_info( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len );
+ break;
+
+ case KIP_DIB_EXTENDED_DEVICE_INFO:
+ ok &= dissect_dib_extdevinfo( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ break;
+
+ case KIP_DIB_MFR_DATA:
+ ok &= dissect_dib_mfrdata( tvb, pinfo, dib_item, dib_tree, length_item, length_ok, &offset, struct_len, info, sizeof info );
+ break;
+
+ default:
+ expert_add_info_format( pinfo, type_item, KIP_WARNING, "Unknown DIB Type" );
+ break;
+ }
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_unknown_data( dib_tree, tvb, offset, end_pos - offset );
+ offset = end_pos;
+ }
+ }
+
+ if( !p_output )
+ {
+ if( pinfo )
+ {
+ column_info* cinfo = pinfo->cinfo;
+ col_append_fstr( cinfo, COL_INFO, "%c ", separator );
+
+ if( !dib_name )
+ {
+ col_append_str( cinfo, COL_INFO, "???" );
+ }
+ else
+ {
+ if( !ok ) col_append_str( cinfo, COL_INFO, "? " );
+ col_append_str( cinfo, COL_INFO, dib_name );
+ }
+ }
+
+ if( item )
+ {
+ proto_item_append_text( item, "%c ", separator );
+
+ if( !dib_name )
+ {
+ proto_item_append_text( item, "???" );
+ }
+ else
+ {
+ if( !ok ) proto_item_append_text( item, "? " );
+ proto_item_append_text( item, "%s", dib_name );
+ }
+ }
+ }
+
+ if( !ok )
+ {
+ proto_item_prepend_text( dib_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ *p_offset = offset;
+ }
+
+ return struct_len;
+}
+
+/* Dissect sequence of DIBs
+*/
+static gchar dissect_dibs( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, gchar** p_output, gint* p_output_max, gchar separator, guint8* p_count, guint8* p_ok )
+{
+ while( dissect_dib( tvb, pinfo, item, tree, p_offset, p_output, p_output_max, separator, p_count, p_ok ) )
+ {
+ separator = ',';
+ }
+
+ return separator;
+}
+
+/* Dissect SRP
+*/
+static guint8 dissect_srp( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset, guint8* p_ok )
+{
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = (remaining_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ if( struct_len > 0 )
+ {
+ gint eff_struct_len = (struct_len <= remaining_len) ? struct_len : remaining_len;
+ gint end_pos = offset + eff_struct_len;
+ column_info* cinfo = pinfo ? pinfo->cinfo : NULL;
+ proto_item* srp_item = proto_tree_add_none_format( tree, hf_folder, tvb, offset, eff_struct_len, "SRP" );
+ proto_tree* srp_tree = proto_item_add_subtree( srp_item, ett_dib );
+ proto_item* length_item = knxip_tree_add_length( srp_tree, tvb, offset, struct_len );
+ guint8 ok = 1;
+ guint8 length_ok = 1;
+
+ offset++;
+
+ if( struct_len > remaining_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ ok = 0;
+ length_ok = 0;
+ }
+
+ if( eff_struct_len < 2 )
+ {
+ expert_add_info_format( pinfo, srp_item, KIP_ERROR, "Missing 1 byte SRP Type" );
+ ok = 0;
+ }
+ else
+ {
+ /* 1 bit Mandatory */
+ proto_tree_add_item( srp_tree, hf_knxip_srp_mandatory, tvb, offset, 1, ENC_BIG_ENDIAN );
+
+ /* 7 bits SRP Type */
+ guint8 srp_type = tvb_get_guint8( tvb, offset ) & 0x7F;
+ const gchar* srp_name = try_val_to_str( srp_type, srp_type_vals );
+ proto_item* type_item = proto_tree_add_item( srp_tree, hf_knxip_srp_type, tvb, offset, 1, ENC_BIG_ENDIAN );
+ guint8 expected_len = 0;
+ guint8 unknown = !srp_name;
+ if( unknown )
+ {
+ expert_add_info_format( pinfo, type_item, KIP_WARNING, "Unknown SRP Type" );
+ srp_name = "???";
+ }
+
+ proto_item_append_text( srp_item, " %s", srp_name ? srp_name : "???" );
+ proto_item_append_text( type_item, " = %s", srp_name ? srp_name : "???" );
+
+ if( !unknown )
+ {
+ col_append_fstr( cinfo, COL_INFO, " %s", srp_name );
+ proto_item_append_text( item, ", %s", srp_name );
+ }
+
+ switch( srp_type )
+ {
+ case 1:
+ expected_len = 2;
+ break;
+ case 2:
+ expected_len = 8;
+ break;
+ case 3:
+ expected_len = 4;
+ break;
+ }
+
+ if( expected_len )
+ {
+ if( struct_len != expected_len )
+ {
+ expert_add_info_format( pinfo, length_item, KIP_ERROR, "Expected: %u bytes", expected_len );
+ ok = 0;
+ length_ok = 0;
+ }
+ }
+ offset++;
+
+ if( offset < end_pos )
+ {
+ knxip_tree_add_data( srp_tree, tvb, offset, end_pos - offset, srp_name ? cinfo : NULL, item, "Data", "=$", " = $" );
+
+ proto_item_append_text( srp_item, ": $" );
+ while( offset < end_pos )
+ {
+ proto_item_append_text( srp_item, " %02X", tvb_get_guint8( tvb, offset ) );
+ ++offset;
+ }
+
+ //offset = end_pos;
+ }
+ }
+
+ if( !ok )
+ {
+ proto_item_prepend_text( srp_item, "? " );
+ if( p_ok ) *p_ok = 0;
+ }
+
+ if( !length_ok )
+ {
+ proto_item_prepend_text( length_item, "? " );
+ }
+
+ *p_offset += struct_len;
+ }
+
+ return struct_len;
+}
+
+/* Dissect sequence of SRPs
+*/
+static void dissect_srps( tvbuff_t *tvb, packet_info *pinfo, proto_item *item, proto_tree *tree, gint *p_offset, guint8* p_ok )
+{
+ while( dissect_srp( tvb, pinfo, item, tree, p_offset, p_ok ) );
+}
+
+/* Dissect RESET command
+*/
+static guint8 dissect_resetter( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 0;
+ gint offset = *p_offset;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ guint8 struct_len = ((guint) remaining_len < 2) ? (guint8) remaining_len : 2;
+ guint8 mode = (struct_len <= 0) ? 0 : tvb_get_guint8( tvb, offset );
+ const gchar* mode_name = (mode == 0x01) ? "Restart" : (mode == 0x02) ? "Master Reset" : NULL;
+ const gchar* mode_info = mode_name ? mode_name : "???";
+ proto_item* node;
+
+ if( struct_len <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Command, Reserved" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ }
+ else
+ {
+ /* 1 byte Reset Command */
+ node = proto_tree_add_item( tree, hf_knxip_reset_command, tvb, offset, 1, ENC_BIG_ENDIAN );
+ proto_item_append_text( node, " = %s", mode_info );
+
+ if( !mode_name )
+ {
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 0x01 or 0x02" );
+ }
+ else
+ {
+ ok = 1;
+ }
+
+ if( struct_len != 2 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Reserved" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ /* 1 byte Reserved */
+ knxip_tree_add_reserved( tree, tvb, offset + 1, pinfo, &ok );
+ }
+ }
+
+ if( pinfo ) col_append_fstr( pinfo->cinfo, COL_INFO, ", %s", mode_info );
+ proto_item_append_text( item, ", %s", mode_info );
+
+ *p_offset += struct_len;
+ return ok;
+}
+
+/* Decrypt SECURE_WRAPPER. Returns decrypted part if MAC matches
+*/
+static guint8* decrypt_secure_wrapper( const guint8* key, const guint8* data, gint h_length, gint p_length )
+{
+ guint8 header_length = *data;
+ gint a_length = header_length + 2;
+ if( a_length > h_length )
+ {
+ a_length = h_length;
+ }
+
+ if( h_length >= header_length + 16 && p_length >= 16 )
+ {
+ const guint8* nonce = data + a_length;
+ guint8* decrypted = knxip_ccm_decrypt( NULL, key, data + h_length, p_length, nonce, 14 );
+
+ if( decrypted )
+ {
+ /* Calculate MAC */
+ guint8 mac[ 16 ];
+ p_length -= 16;
+
+ knxip_ccm_calc_cbc_mac( mac, key, data, a_length, decrypted, p_length, nonce, 14 );
+
+ /* Check MAC */
+ if( memcmp( decrypted + p_length, mac, 16 ) != 0 )
+ {
+ wmem_free( wmem_packet_scope(), decrypted );
+ decrypted = NULL;
+ }
+ }
+
+ return decrypted;
+ }
+
+ return NULL;
+}
+
+static void make_key_info( gchar* text, gint text_max, const guint8* key, const gchar* context )
+{
+ guint8 count;
+
+ if( !key )
+ {
+ g_snprintf( text, text_max, "without key" );
+ }
+ else
+ {
+ if( context )
+ {
+ g_snprintf( text, text_max, "with %s key", context );
+ }
+ else
+ {
+ g_snprintf( text, text_max, "with key" );
+ }
+
+ for( count = 16; count; --count )
+ {
+ while( *text ) { ++text; --text_max; }
+ g_snprintf( text, text_max, " %02X", *key++ );
+ }
+ }
+}
+
+/* Dissect SECURE_WRAPPER
+*/
+static guint8 dissect_secure_wrapper( guint8 header_length, tvbuff_t* tvb, packet_info* pinfo, proto_tree* root, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ column_info* cinfo = pinfo->cinfo;
+ const guint8* dest_addr = (pinfo->dst.type == AT_IPv4) ? (const guint8*) pinfo->dst.data : NULL;
+ proto_item* node;
+
+ /* 2 bytes Session ID */
+ if( size < 2 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Session" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ guint16 session = tvb_get_ntohs( tvb, offset );
+ node = proto_tree_add_item( tree, hf_knxip_session, tvb, offset, 2, ENC_BIG_ENDIAN );
+
+ if( session )
+ {
+ col_append_fstr( cinfo, COL_INFO, " #%04X", session );
+ proto_item_append_text( item, ", Session: $%04X", session );
+ }
+
+ offset += 2;
+ size -= 2;
+
+ /* 6 bytes Sequence Nr */
+ if( size < 6 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Sequence Number" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, 6, cinfo, item, "Sequence Number", " $", ", Seq Nr: $" );
+ offset += 6;
+ size -= 6;
+
+ /* 6 bytes Serial Nr */
+ if( size < 6 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Serial Number" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, 6, cinfo, item, "Serial Number", ".", ", Ser Nr: $" );
+ offset += 6;
+ size -= 6;
+
+ /* 2 bytes Tag */
+ if( size < 2 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Tag" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ guint16 tag = tvb_get_ntohs( tvb, offset );
+ node = proto_tree_add_item( tree, hf_knxip_tag, tvb, offset, 2, ENC_BIG_ENDIAN );
+ col_append_fstr( cinfo, COL_INFO, ".%04X", tag );
+ proto_item_append_text( item, ", Tag: $%04X", tag );
+ offset += 2;
+ size -= 2;
+
+ /* Encrypted part */
+ if( size < 16 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Encrypted" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: min 16 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ const guint8* encrypted = tvb_get_ptr( tvb, offset, size - offset );
+ const gint a_length = header_length + 16; // length of leading non-encrypted data
+ const guint8* a_data = encrypted - a_length; // ptr to KIP header
+ guint8* decrypted = NULL;
+ const guint8* key = NULL;
+ gchar decrypt_info[ 128 ];
+ struct knx_keyring_mca_keys* mca_key;
+ guint8 key_index;
+
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, encrypted, "Encrypted (%d bytes)", size );
+
+ *decrypt_info = '\0';
+
+ if( dest_addr )
+ {
+ // Try keys associated with IP MCA in keyring.XML
+ for( mca_key = knx_keyring_mca_keys; mca_key; mca_key = mca_key->next )
+ {
+ if( memcmp( mca_key->mca, dest_addr, 4 ) == 0 )
+ {
+ key = mca_key->key;
+ decrypted = decrypt_secure_wrapper( key, a_data, a_length, size );
+ if( decrypted )
+ {
+ make_key_info( decrypt_info, sizeof decrypt_info, key, "MCA" );
+ break;
+ }
+ }
+ }
+ }
+
+ if( !decrypted )
+ {
+ // Try explicitly specified keys
+ for( key_index = 0; key_index < knx_decryption_key_count; ++key_index )
+ {
+ key = knx_decryption_keys[ key_index ];
+ decrypted = decrypt_secure_wrapper( key, a_data, a_length, size );
+ if( decrypted )
+ {
+ make_key_info( decrypt_info, sizeof decrypt_info, key, NULL );
+ break;
+ }
+ }
+ }
+
+ if( !decrypted )
+ {
+ const gchar* text = knx_decryption_key_count ? " (decryption failed)" : knx_keyring_mca_keys ? " (no key found)" : " (no key available)";
+ proto_item_append_text( node, "%s", text );
+ }
+ else
+ {
+ tvbuff_t* tvb2 = tvb_new_child_real_data( tvb, decrypted, size, size );
+ gint size2 = size - 16;
+ proto_item_append_text( item, ", MAC OK" );
+ //tvb_set_free_cb( tvb2, wmem_free );
+ add_new_data_source( pinfo, tvb2, "Decrypted" );
+
+ item = proto_tree_add_none_format( root, hf_folder, tvb2, 0, size, "Decrypted" );
+ tree = proto_item_add_subtree( item, ett_decrypted );
+
+ if( *decrypt_info )
+ {
+ proto_item_append_text( item, " (%s)", decrypt_info );
+ }
+
+ /* Embedded KIP packet */
+ node = knxip_tree_add_data( tree, tvb2, 0, size2, NULL, NULL, "Embedded KNXnet/IP packet", NULL, NULL );
+
+ /* MAC */
+ node = knxip_tree_add_data( tree, tvb2, size2, 16, NULL, NULL, "Message Authentication Code", NULL, NULL );
+
+ /* Dissect embedded KIP packet */
+ {
+ tvbuff_t* tvb3 = tvb_new_subset_length( tvb2, 0, size2 );
+ dissect_knxip( 1, tvb3, pinfo, root );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *p_offset = offset + size;
+ return ok;
+}
+
+/* Check encrypted MAC in TIMER_NOTIFY
+*/
+static guint8 check_timer_sync_mac( const guint8* key, const guint8* data, gint header_length )
+{
+ // Calculate and encrypt MAC
+ const guint8* nonce = data + header_length;
+ guint8 mac[ 16 ];
+ knxip_ccm_calc_cbc_mac( mac, key, data, header_length, NULL, 0, nonce, 14 );
+ knxip_ccm_encrypt( mac, key, NULL, 0, mac, nonce, 14 );
+
+ // Check MAC
+ return (memcmp( nonce + 14, mac, 16 ) == 0);
+}
+
+/* Dissect TIMER_NOTIFY
+*/
+static guint8 dissect_timer_notify( guint8 header_length, tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ column_info* cinfo = pinfo->cinfo;
+ const guint8* dest_addr = (pinfo->dst.type == AT_IPv4) ? (const guint8*) pinfo->dst.data : NULL;
+ proto_item* node;
+
+ /* 6 bytes Timestamp */
+ if( size < 6 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Timestamp" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, 6, cinfo, item, "Timestamp", " $", ", Timestamp: $" );
+ offset += 6;
+ size -= 6;
+
+ /* 6 bytes Serial Nr */
+ if( size < 6 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Serial Number" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, 6, cinfo, item, "Serial Number", ".", ", Ser Nr: $" );
+ offset += 6;
+ size -= 6;
+
+ /* 2 bytes Tag */
+ if( size < 2 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Tag" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ guint16 tag = tvb_get_ntohs( tvb, offset );
+ node = proto_tree_add_item( tree, hf_knxip_tag, tvb, offset, 2, ENC_BIG_ENDIAN );
+ col_append_fstr( cinfo, COL_INFO, ".%04X", tag );
+ proto_item_append_text( item, ", Tag: $%04X", tag );
+ offset += 2;
+ size -= 2;
+
+ /* 16 bytes MAC */
+ if( size < 16 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Message Authentication Code" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 16 bytes" );
+ ok = 0;
+ }
+ else
+ {
+ const gint a_length = header_length + 14; // length of leading non-encrypted data
+ const guint8* a_data = tvb_get_ptr( tvb, offset - a_length, a_length + 16 );
+ const guint8* key = NULL;
+ guint8 mac_ok = 0;
+ guint8 mac_error = 0;
+ gchar mac_info[ 128 ];
+ struct knx_keyring_mca_keys* mca_key;
+ guint8 key_index;
+
+ node = knxip_tree_add_data( tree, tvb, offset, 16, NULL, NULL, "Message Authentication Code", NULL, NULL );
+
+ *mac_info = '\0';
+
+ if( dest_addr )
+ {
+ // Try keys associated with IP MCA in keyring.XML
+ for( mca_key = knx_keyring_mca_keys; mca_key; mca_key = mca_key->next )
+ {
+ if( memcmp( mca_key->mca, dest_addr, 4 ) == 0 )
+ {
+ key = mca_key->key;
+ if( check_timer_sync_mac( key, a_data, header_length ) )
+ {
+ mac_ok = 1;
+ make_key_info( mac_info, sizeof mac_info, key, "MCA" );
+ break;
+ }
+ }
+ }
+ }
+
+ if( !mac_ok )
+ {
+ // Try explicitly specified keys
+ for( key_index = 0; key_index < knx_decryption_key_count; ++key_index )
+ {
+ key = knx_decryption_keys[ key_index ];
+ if( check_timer_sync_mac( key, a_data, header_length ) )
+ {
+ mac_ok = 1;
+ make_key_info( mac_info, sizeof mac_info, key, NULL );
+ break;
+ }
+ }
+ }
+
+ if( mac_ok )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "MAC OK" );
+ col_append_str( cinfo, COL_INFO, " OK" );
+ proto_item_append_text( item, ", MAC OK" );
+
+ if( *mac_info )
+ {
+ proto_item_append_text( node, " (%s)", mac_info );
+ }
+
+ if( mac_error )
+ {
+ expert_add_info_format( pinfo, node, KIP_WARNING, "OK with wrong key" );
+ col_append_str( cinfo, COL_INFO, " (!)" );
+ proto_item_append_text( item, " (!)" );
+ }
+ }
+
+ offset += 16;
+ size = 0;
+ }
+ }
+ }
+ }
+
+ *p_offset = offset + size;
+ return ok;
+}
+
+/* Dissect SESSION_REQUEST
+*/
+static guint8 dissect_session_request( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+
+ /* Control Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, item, tree, &offset, &ok, "Control", 1 ) )
+ {
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ proto_item* node;
+
+ /* DH Client Public Value */
+ if( size <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? DH Client Public Value" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Missing" );
+ ok = 0;
+ }
+ else
+ {
+ node = knxip_tree_add_data( tree, tvb, offset, size, NULL, NULL, "DH Client Public Value", NULL, NULL );
+
+#if ECDH_PUBLIC_VALUE_SIZE > 0
+ if( size != ECDH_PUBLIC_VALUE_SIZE )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: %u bytes", ECDH_PUBLIC_VALUE_SIZE );
+ ok = 0;
+ }
+#endif
+
+ offset += size;
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect SESSION_RESPONSE
+*/
+static guint8 dissect_session_response( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ column_info* cinfo = pinfo->cinfo;
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ proto_item *node;
+
+ /* 2 bytes Session ID */
+ if( size < 2 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Session" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
+ offset += size;
+ ok = 0;
+ }
+ else
+ {
+ guint16 session = tvb_get_ntohs( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, " #%04X", session );
+ proto_item_append_text( item, " #%04X", session );
+ proto_tree_add_item( tree, hf_knxip_session, tvb, offset, 2, ENC_BIG_ENDIAN );
+ offset += 2;
+ size -= 2;
+
+ /* DH Server Public Value */
+ {
+ gint size2 = size - 16;
+ if( size2 < 0 )
+ {
+ size2 = 0;
+ }
+
+ node = knxip_tree_add_data( tree, tvb, offset, size2, NULL, NULL, "DH Server Public Value", NULL, NULL );
+
+ if( size2 != ECDH_PUBLIC_VALUE_SIZE )
+ {
+ proto_item_prepend_text( node, "? " );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: %u bytes", ECDH_PUBLIC_VALUE_SIZE );
+ ok = 0;
+ }
+
+ offset += size2;
+ size -= size2;
+ }
+
+ /* 16 bytes MAC */
+ if( size < 16 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Message Authentication Code" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 16 bytes" );
+ offset += size;
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_data( tree, tvb, offset, 16, NULL, NULL, "Message Authentication Code", NULL, NULL );
+ offset += 16;
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect SESSION_AUTHENTICATE
+*/
+static guint8 dissect_session_auth( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ column_info* cinfo = pinfo->cinfo;
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ proto_item* node;
+
+ /* 1 byte Reserved */
+ if( size <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Reserved" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_reserved( tree, tvb, offset, pinfo, &ok );
+ ++offset;
+ --size;
+
+ /* 1 byte User ID */
+ if( size <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? User" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 user_id = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, " User=%u", user_id );
+ proto_item_append_text( item, ", User = %u", user_id );
+ proto_tree_add_item( tree, hf_knxip_user, tvb, offset, 1, ENC_BIG_ENDIAN );
+ ++offset;
+ --size;
+
+ /* 16 bytes MAC */
+ if( size < 16 )
+ {
+ node = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, size, NULL, "? Message Authentication Code" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 16 bytes" );
+ offset += size;
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_data( tree, tvb, offset, 16, NULL, NULL, "Message Authentication Code", NULL, NULL );
+ offset += 16;
+ }
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect SESSION_STATUS
+*/
+static guint8 dissect_session_status( tvbuff_t* tvb, packet_info* pinfo, proto_item* item, proto_tree* tree, gint* p_offset )
+{
+ guint8 ok = 1;
+ gint offset = *p_offset;
+ column_info* cinfo = pinfo->cinfo;
+ gint size = tvb_captured_length_remaining( tvb, offset );
+ proto_item* node;
+
+ /* 1 byte Status */
+ if( size <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Status" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 status = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, " %u", status );
+ proto_item_append_text( item, ": %u", status );
+ proto_tree_add_item( tree, hf_knxip_session_status, tvb, offset, 1, ENC_BIG_ENDIAN );
+ ++offset;
+ --size;
+
+ /* 1 byte Reserved */
+ if( size <= 0 )
+ {
+ node = proto_tree_add_debug_text( tree, "? Reserved" );
+ expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 1 byte" );
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_reserved( tree, tvb, offset, pinfo, &ok );
+ ++offset;
+ --size;
+ }
+ }
+
+ *p_offset = offset;
+ return ok;
+}
+
+/* Dissect KNX-IP data after KNX-IP header
+*/
+static void dissect_knxip_data( guint8 header_length, guint8 protocol_version _U_, guint16 service, tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* kip_item, proto_tree* kip_tree )
+{
+ guint8 ok = 1;
+ guint8 service_family = (service >> 8);
+ const gchar* service_family_name = try_val_to_str( service_family, knxip_service_family_vals );
+ const gchar* service_name = try_val_to_str( service, knxip_service_type_vals );
+ const gchar* svc_name = try_val_to_str( service, svc_vals );
+ gint offset = header_length;
+ gint remaining_len = tvb_captured_length_remaining( tvb, offset );
+ column_info* cinfo = pinfo->cinfo;
+ gchar info[ 80 ];
+
+ /* Make sure that we cope with a well known service family
+ */
+ if( service_family_name == NULL )
+ {
+ col_add_str( cinfo, COL_INFO, "Unknown Service Family" );
+ proto_item_append_text( kip_item, " Unknown Service Family" );
+ ok = 0;
+ }
+ else
+ {
+ /* Make sure that we cope with a well known service type
+ */
+ if( service_name == NULL )
+ {
+ col_append_fstr( cinfo, COL_INFO, "%s: ? Unknown Service Type", service_family_name );
+ proto_item_append_text( kip_item, " Unknown Service Type" );
+ ok = 0;
+ }
+ else
+ {
+ col_append_str( cinfo, COL_INFO, svc_name ? svc_name : service_name );
+ proto_item_append_text( kip_item, " %s", service_name );
+
+ /* Dissect according to Service Type
+ */
+ switch( service )
+ {
+
+ /* CORE */
+
+ case KIP_SEARCH_REQUEST:
+ {
+ /* Discovery Endpoint HPAI */
+ dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Discovery", 1 );
+ }
+ break;
+
+ case KIP_SEARCH_REQUEST_EXT:
+ {
+ /* Discovery Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Discovery", 0 ) )
+ {
+ /* Search Request Parameters */
+ dissect_srps( tvb, pinfo, kip_item, kip_tree, &offset, &ok );
+ }
+ }
+ break;
+
+ case KIP_SEARCH_RESPONSE:
+ case KIP_SEARCH_RESPONSE_EXT:
+ {
+ /* Control Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Control", 0 ) )
+ {
+ /* DIBs */
+ guint8 dib_count[ 256 ] = { 0 };
+ gchar* output = info;
+ gint output_max = sizeof info;
+
+ *info = '\0';
+ dissect_dibs( tvb, pinfo, kip_item, kip_tree, &offset, &output, &output_max, '\0', dib_count, &ok );
+ if( *info )
+ {
+ col_append_fstr( cinfo, COL_INFO, ", %s", info );
+ proto_item_append_text( kip_item, ", %s", info );
+ }
+
+ if( service == KIP_SEARCH_RESPONSE )
+ {
+ if( !dib_count[ KIP_DIB_DEVICE_INFO ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB DevInfo" );
+ ok = 0;
+ }
+ if( !dib_count[ KIP_DIB_SUPP_SVC_FAMILIES ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB SuppSvc" );
+ ok = 0;
+ }
+ }
+ }
+ }
+ break;
+
+ case KIP_DESCRIPTION_REQUEST:
+ {
+ /* Control Endpoint HPAI */
+ dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Control", 1 );
+ }
+ break;
+
+ case KIP_DESCRIPTION_RESPONSE:
+ {
+ /* DIBs */
+ guint8 dib_count[ 256 ] = { 0 };
+ dissect_dibs( tvb, pinfo, kip_item, kip_tree, &offset, NULL, 0, ':', dib_count, &ok );
+ if( !dib_count[ KIP_DIB_DEVICE_INFO ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB DevInfo" );
+ ok = 0;
+ }
+ if( !dib_count[ KIP_DIB_SUPP_SVC_FAMILIES ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB SuppSvc" );
+ ok = 0;
+ }
+ }
+ break;
+
+ case KIP_CONNECT_REQUEST:
+ {
+ /* Control Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Control", 1 ) )
+ {
+ /* Data Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Data", 1 ) )
+ {
+ /* CRI */
+ dissect_cri( tvb, pinfo, kip_item, kip_tree, &offset, &ok );
+ }
+ }
+ }
+ break;
+
+ case KIP_CONNECT_RESPONSE:
+ {
+ /* 1 byte Channel ID */
+ if( remaining_len < 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " ???" );
+ proto_item_append_text( kip_item, ", ???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Channel" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 channel = tvb_get_guint8( tvb, offset );
+ proto_tree_add_item( kip_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ /* 1 byte Status */
+ if( remaining_len < 2 )
+ {
+ col_append_fstr( cinfo, COL_INFO, " ???" );
+ proto_item_append_text( kip_item, ", ???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Status" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 status = tvb_get_guint8( tvb, offset );
+ knxip_tree_add_status( kip_tree, tvb, offset );
+ offset++;
+
+ if( status == KIP_E_NO_ERROR )
+ {
+ col_append_fstr( cinfo, COL_INFO, " #%02X", channel );
+ proto_item_append_text( kip_item, ", Conn #%02X", channel );
+
+ /* Data Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Data", 1 ) )
+ {
+ /* CRD */
+ dissect_crd( tvb, pinfo, kip_item, kip_tree, &offset, &ok );
+ }
+ }
+ else
+ {
+ const gchar* status_info = val_to_str( status, error_vals, "Error 0x%02x" );
+ col_append_fstr( cinfo, COL_INFO, " %s", status_info );
+ proto_item_append_text( kip_item, ": %s", status_info );
+ }
+ }
+ }
+ }
+ break;
+
+ case KIP_CONNECTIONSTATE_REQUEST:
+ {
+ /* 1 byte Channel ID */
+ col_append_fstr( cinfo, COL_INFO, " #" );
+ proto_item_append_text( kip_item, ", Conn #" );
+
+ if( remaining_len < 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Channel" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 channel = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, "%02X", channel );
+ proto_item_append_text( kip_item, "%02X", channel );
+ proto_tree_add_item( kip_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ /* Reserved Byte */
+ if( remaining_len < 2 )
+ {
+ knxip_tree_add_missing_reserved( kip_tree, pinfo );
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_reserved( kip_tree, tvb, offset, pinfo, &ok );
+ offset++;
+
+ /* Control Endpoint HPAI */
+ dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Control", 1 );
+ }
+ }
+ }
+ break;
+
+ case KIP_CONNECTIONSTATE_RESPONSE:
+ {
+ /* 1 byte Channel ID */
+ col_append_fstr( cinfo, COL_INFO, " #" );
+ proto_item_append_text( kip_item, ", Conn #" );
+
+ if( remaining_len < 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Channel" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 channel = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, "%02X ", channel );
+ proto_item_append_text( kip_item, "%02X: ", channel );
+ proto_tree_add_item( kip_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ /* 1 byte Status */
+ if( remaining_len < 2 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Status" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 status = tvb_get_guint8( tvb, offset );
+ const gchar* status_info = val_to_str( status, error_vals, "Error 0x%02x" );
+ col_append_fstr( cinfo, COL_INFO, "%s", status_info );
+ proto_item_append_text( kip_item, "%s", status_info );
+ knxip_tree_add_status( kip_tree, tvb, offset );
+ offset++;
+ }
+ }
+ }
+ break;
+
+ case KIP_DISCONNECT_REQUEST:
+ {
+ /* 1 byte Channel ID */
+ col_append_fstr( cinfo, COL_INFO, " #" );
+ proto_item_append_text( kip_item, ", Conn #" );
+
+ if( remaining_len < 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Channel" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 channel = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, "%02X", channel );
+ proto_item_append_text( kip_item, "%02X", channel );
+ proto_tree_add_item( kip_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ /* Reserved Byte */
+ if( remaining_len < 2 )
+ {
+ knxip_tree_add_missing_reserved( kip_tree, pinfo );
+ ok = 0;
+ }
+ else
+ {
+ knxip_tree_add_reserved( kip_tree, tvb, offset, pinfo, &ok );
+ offset++;
+
+ /* Control Endpoint HPAI */
+ dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Control", 1 );
+ }
+ }
+ }
+ break;
+
+ case KIP_DISCONNECT_RESPONSE:
+ {
+ /* 1 byte Channel ID */
+ col_append_fstr( cinfo, COL_INFO, " #" );
+ proto_item_append_text( kip_item, ", Conn #" );
+
+ if( remaining_len < 1 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Channel" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 channel = tvb_get_guint8( tvb, offset );
+ col_append_fstr( cinfo, COL_INFO, "%02X ", channel );
+ proto_item_append_text( kip_item, "%02X: ", channel );
+ proto_tree_add_item( kip_tree, hf_knxip_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
+ offset++;
+
+ /* 1 byte Status */
+ if( remaining_len < 2 )
+ {
+ col_append_fstr( cinfo, COL_INFO, "???" );
+ proto_item_append_text( kip_item, "???" );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing 1 byte Status" );
+ ok = 0;
+ }
+ else
+ {
+ guint8 status = tvb_get_guint8( tvb, offset );
+ const gchar* status_info = val_to_str( status, error_vals, "Error 0x%02x" );
+ col_append_fstr( cinfo, COL_INFO, "%s", status_info );
+ proto_item_append_text( kip_item, "%s", status_info );
+ knxip_tree_add_status( kip_tree, tvb, offset );
+ offset++;
+ }
+ }
+ }
+ break;
+
+ /* MANAGEMENT */
+
+ case KIP_CONFIGURATION_REQUEST:
+ {
+ /* Connection Header */
+ if( dissect_cnhdr( tvb, pinfo, kip_item, kip_tree, &offset, &ok, FALSE ) )
+ {
+ /* cEMI */
+ dissect_cemi( tvb, pinfo, tree, &offset );
+ }
+ }
+ break;
+
+ case KIP_CONFIGURATION_ACK:
+ {
+ /* Connection Header */
+ dissect_cnhdr( tvb, pinfo, kip_item, kip_tree, &offset, &ok, TRUE );
+ }
+ break;
+
+ /* TUNNELING */
+
+ case KIP_TUNNELING_REQUEST:
+ {
+ /* Connection Header */
+ if( dissect_cnhdr( tvb, pinfo, kip_item, kip_tree, &offset, &ok, FALSE ) )
+ {
+ /* cEMI */
+ dissect_cemi( tvb, pinfo, tree, &offset );
+ }
+ }
+ break;
+
+ case KIP_TUNNELING_ACK:
+ {
+ /* Connection Header */
+ dissect_cnhdr( tvb, pinfo, kip_item, kip_tree, &offset, &ok, TRUE );
+ }
+ break;
+
+ case KIP_TUNNELING_FEATURE_GET:
+ case KIP_TUNNELING_FEATURE_RESPONSE:
+ case KIP_TUNNELING_FEATURE_SET:
+ case KIP_TUNNELING_FEATURE_INFO:
+ {
+ /* Connection Header, 1 byte Feature ID, 1 byte Return Code, Feature Value */
+ dissect_tunneling_feature( tvb, pinfo, kip_item, kip_tree, &offset, &ok, service );
+ }
+ break;
+
+ /* ROUTING */
+
+ case KIP_ROUTING_INDICATION:
+ {
+ /* cEMI */
+ dissect_cemi( tvb, pinfo, tree, &offset );
+ }
+ break;
+
+ case KIP_ROUTING_LOST_MESSAGE:
+ {
+ /* Routing Loss */
+ ok &= dissect_routing_loss( tvb, pinfo, kip_item, kip_tree, &offset );
+ }
+ break;
+
+ case KIP_ROUTING_BUSY:
+ {
+ /* Routing Busy */
+ ok &= dissect_routing_busy( tvb, pinfo, kip_item, kip_tree, &offset );
+ }
+ break;
+
+ case KIP_ROUTING_SYSTEM_BROADCAST:
+ {
+ /* cEMI */
+ dissect_cemi( tvb, pinfo, tree, &offset );
+ }
+ break;
+
+ /* REMOTE_DIAG_AND_CONFIG */
+
+ case KIP_REMOTE_DIAG_REQUEST:
+ {
+ /* Discovery Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Discovery", 0 ) )
+ {
+ /* Selector */
+ dissect_selector( tvb, pinfo, kip_item, kip_tree, &offset, &ok );
+ }
+ }
+ break;
+
+ case KIP_REMOTE_DIAG_RESPONSE:
+ {
+ /* Selector */
+ if( dissect_selector( tvb, pinfo, kip_item, kip_tree, &offset, &ok ) )
+ {
+ /* DIBs */
+ guint8 dib_count[ 256 ] = { 0 };
+ dissect_dibs( tvb, pinfo, kip_item, kip_tree, &offset, NULL, 0, ',', dib_count, &ok );
+ if( !dib_count[ KIP_DIB_IP_CONFIG ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB IpConfig" );
+ ok = 0;
+ }
+ if( !dib_count[ KIP_DIB_CUR_CONFIG ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB CurConfig" );
+ ok = 0;
+ }
+ if( !dib_count[ KIP_DIB_KNX_ADDRESSES ] )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Missing DIB KnxAddr" );
+ ok = 0;
+ }
+ }
+ }
+ break;
+
+ case KIP_REMOTE_CONFIG_REQUEST:
+ {
+ /* Discovery Endpoint HPAI */
+ if( dissect_hpai( tvb, pinfo, kip_item, kip_tree, &offset, &ok, "Discovery", 0 ) )
+ {
+ /* Selector */
+ if( dissect_selector( tvb, pinfo, kip_item, kip_tree, &offset, &ok ) )
+ {
+ /* DIBs */
+ gint old_offset = offset;
+ dissect_dibs( tvb, pinfo, kip_item, kip_tree, &offset, NULL, 0, ',', NULL, &ok );
+ if( offset <= old_offset )
+ {
+ expert_add_info_format( pinfo, kip_item, KIP_WARNING, "Missing DIB" );
+ }
+ }
+ }
+ }
+ break;
+
+ case KIP_REMOTE_RESET_REQUEST:
+ {
+ /* Selector */
+ if( dissect_selector( tvb, pinfo, kip_item, kip_tree, &offset, &ok ) )
+ {
+ /* Reset Mode */
+ ok &= dissect_resetter( tvb, pinfo, kip_item, kip_tree, &offset );
+ }
+ }
+ break;
+
+ case KIP_SECURE_WRAPPER:
+ ok &= dissect_secure_wrapper( header_length, tvb, pinfo, tree, kip_item, kip_tree, &offset );
+ break;
+
+ case KIP_TIMER_NOTIFY:
+ ok &= dissect_timer_notify( header_length, tvb, pinfo, kip_item, kip_tree, &offset );
+ break;
+
+ case KIP_SESSION_REQUEST:
+ ok &= dissect_session_request( tvb, pinfo, kip_item, kip_tree, &offset );
+ break;
+
+ case KIP_SESSION_RESPONSE:
+ ok &= dissect_session_response( tvb, pinfo, kip_item, kip_tree, &offset );
+ break;
+
+ case KIP_SESSION_AUTHENTICATE:
+ ok &= dissect_session_auth( tvb, pinfo, kip_item, kip_tree, &offset );
+ break;
+
+ case KIP_SESSION_STATUS:
+ ok &= dissect_session_status( tvb, pinfo, kip_item, kip_tree, &offset );
+ break;
+ }
+ }
+ }
+
+ if( offset >= 0 )
+ {
+ remaining_len = tvb_captured_length_remaining( tvb, offset );
+ if( remaining_len > 0 )
+ {
+ if( tree )
+ {
+ proto_item* unknown_item = knxip_tree_add_unknown_data( kip_tree, tvb, offset, remaining_len );
+ expert_add_info_format( pinfo, unknown_item, KIP_ERROR, "Unexpexted trailing data" );
+ }
+
+ ok = 0;
+ }
+ }
+
+ if( !ok )
+ {
+ /* If not already done */
+ if( !knxip_error )
+ {
+ knxip_error = 1;
+ col_prepend_fstr( cinfo, COL_INFO, "? " );
+ }
+
+ proto_item_prepend_text( kip_item, "? " );
+ }
+}
+
+static void dissect_knxip( guint8 level, tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree )
+{
+ gint offset = 0;
+ guint remaining_len = tvb_captured_length( tvb );
+ guint8 header_len = 0;
+ guint8 protocol_version = 0;
+ guint16 service_id = 0;
+ guint16 total_len = 0;
+ guint8 error = 0;
+
+ column_info* cinfo = pinfo->cinfo;
+
+ proto_item* kip_item = NULL;
+ proto_tree* kip_tree = NULL;
+ proto_item* header_item = NULL;
+ proto_tree* header_tree = NULL;
+ proto_item* header_len_item = NULL;
+ proto_item* version_item = NULL;
+ proto_item* service_item = NULL;
+ proto_tree* service_tree = NULL;
+ proto_item* total_length_item = NULL;
+
+ gchar version_info[ 16 ];
+
+ if( level == 0 )
+ {
+ knxip_error = 0;
+ col_set_str( cinfo, COL_PROTOCOL, "KNXnet/IP" );
+ col_clear( cinfo, COL_INFO );
+ }
+ else
+ {
+ col_append_str( cinfo, COL_INFO, " " );
+ }
+
+ kip_item = proto_tree_add_item( tree, proto_knxip, tvb, offset, (remaining_len <= 0) ? 0 : -1, ENC_BIG_ENDIAN );
+ kip_tree = proto_item_add_subtree( kip_item, ett_kip );
+
+ if( remaining_len <= 0 )
+ {
+ /* This may happen if we are embedded in another KNXnet/IP frame (level != 0)
+ */
+ proto_item_prepend_text( kip_item, "? " );
+ expert_add_info_format( pinfo, kip_item, KIP_ERROR, "Expected: min 6 bytes" );
+ col_append_str( cinfo, COL_INFO, "? empty" );
+
+ /* If not already done */
+ if( !knxip_error )
+ {
+ knxip_error = 1;
+ col_prepend_fstr( cinfo, COL_INFO, "? " );
+ }
+ }
+ else
+ {
+ /* 1 byte Header Length */
+ header_len = tvb_get_guint8( tvb, 0 );
+
+ if( tree )
+ {
+ header_item = proto_tree_add_none_format( kip_tree, hf_folder, tvb, 0,
+ (header_len <= remaining_len) ? header_len : remaining_len, "KNX/IP Header" );
+ header_tree = proto_item_add_subtree( header_item, ett_efcp );
+ header_len_item = proto_tree_add_uint_format( header_tree, hf_knxip_header_length,
+ tvb, 0, 1, header_len, "Header Length: %u bytes", header_len );
+ }
+
+ if( header_len > remaining_len )
+ {
+ proto_item_prepend_text( header_len_item, "? " );
+ expert_add_info_format( pinfo, header_len_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ error = 1;
+ header_len = (guint8) remaining_len;
+ }
+ else if( header_len != 0x06 )
+ {
+ proto_item_prepend_text( header_len_item, "? " );
+ expert_add_info_format( pinfo, header_len_item, KIP_ERROR, "Expected: 6 bytes" );
+ error = 1;
+ }
+
+ offset++;
+
+ if( header_len >= 2 )
+ {
+ /* 1 byte Protocol Version */
+ protocol_version = tvb_get_guint8( tvb, 1 );
+ g_snprintf( version_info, sizeof version_info, "%u.%u", hi_nibble( protocol_version ), lo_nibble( protocol_version ) );
+
+ if( tree )
+ {
+ version_item = proto_tree_add_uint_format( header_tree, hf_knxip_protocol_version,
+ tvb, 1, 1, protocol_version, "Protocol Version: %s", version_info );
+ }
+
+ if( protocol_version != 0x10 )
+ {
+ proto_item_prepend_text( version_item, "? " );
+ expert_add_info_format( pinfo, version_item, KIP_ERROR, "Expected: Protocol Version 1.0" );
+ error = 1;
+ }
+
+ offset++;
+
+ if( header_len >= 4 )
+ {
+ /* 2 bytes Service ID */
+ service_id = tvb_get_ntohs( tvb, 2 );
+
+ if( tree )
+ {
+ const gchar* name = try_val_to_str( service_id, knxip_service_type_vals );
+ proto_item_append_text( header_item, ": " );
+ if( name )
+ proto_item_append_text( header_item, "%s", name );
+ else
+ proto_item_append_text( header_item, "Service = 0x%04x", service_id );
+ service_item = proto_tree_add_item( header_tree, hf_knxip_service_id, tvb, 2, 2, ENC_BIG_ENDIAN );
+ service_tree = proto_item_add_subtree( service_item, ett_service );
+ proto_tree_add_item( service_tree, hf_knxip_service_family, tvb, 2, 1, ENC_BIG_ENDIAN );
+ proto_tree_add_item( service_tree, hf_knxip_service_type, tvb, 2, 2, ENC_BIG_ENDIAN );
+ }
+
+ offset += 2;
+
+ if( header_len >= 6 )
+ {
+ /* 2 bytes Total Length */
+ total_len = tvb_get_ntohs( tvb, 4 );
+
+ if( tree )
+ {
+ total_length_item = proto_tree_add_uint_format( header_tree, hf_knxip_total_length,
+ tvb, 4, 2, total_len, "Total Length: %u bytes", total_len );
+ }
+
+ if( total_len < header_len )
+ {
+ proto_item_prepend_text( total_length_item, "? " );
+ expert_add_info_format( pinfo, total_length_item, KIP_ERROR, "Expected: >= Header Length" );
+ error = 1;
+ }
+ else if( total_len > remaining_len )
+ {
+ proto_item_prepend_text( total_length_item, "? " );
+ expert_add_info_format( pinfo, total_length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ error = 1;
+ }
+ else if( total_len < remaining_len )
+ {
+ proto_item_prepend_text( total_length_item, "? " );
+ expert_add_info_format( pinfo, total_length_item, KIP_ERROR, "Available: %u bytes", remaining_len );
+ error = 1;
+ }
+
+ offset += 2;
+ }
+ }
+ }
+
+ if( offset < header_len )
+ {
+ knxip_tree_add_unknown_data( header_tree, tvb, offset, header_len - offset );
+ }
+
+ if( error )
+ {
+ proto_item_prepend_text( header_item, "? " );
+
+ if( level == 0 )
+ {
+ col_prepend_fstr( cinfo, COL_PROTOCOL, "? " );
+ }
+ else
+ {
+ /* If not already done */
+ if( !knxip_error )
+ {
+ knxip_error = 1;
+ col_prepend_fstr( cinfo, COL_INFO, "? " );
+ }
+ }
+ }
+
+ dissect_knxip_data( header_len, protocol_version, service_id, tvb, pinfo, tree, kip_item, kip_tree );
+ }
+}
+
+static gint dissect_tcp_knxip( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* udata _U_ )
+{
+ knxip_host_protocol = IP_PROTO_TCP;
+ dissect_knxip( 0, tvb, pinfo, tree );
+ return tvb_captured_length( tvb );
+}
+
+static gint dissect_udp_knxip( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* udata _U_ )
+{
+ knxip_host_protocol = IP_PROTO_UDP;
+ dissect_knxip( 0, tvb, pinfo, tree );
+ return tvb_captured_length( tvb );
+}
+
+void proto_register_knxip( void )
+{
+ /* Header fields */
+ static hf_register_info hf[] =
+ {
+ { &hf_bytes, { "Data", "knxip.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_folder, { "Folder", "knxip.folder", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_header_length, { "Header Length", "knxip.headerlength", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_protocol_version, { "Protocol Version", "knxip.version", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_service_id, { "Service Identifier", "knxip.service", FT_UINT16, BASE_HEX, VALS( knxip_service_type_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_service_family, { "Service Family", "knxip.service.family", FT_UINT8, BASE_HEX, VALS( knxip_service_family_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_service_type, { "Service Type", "knxip.service.type", FT_UINT16, BASE_HEX, VALS( knxip_service_type_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_total_length, { "Total Length", "knxip.totallength", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_structure_length, { "Structure Length", "knxip.struct.length", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_host_protocol, { "Host Protocol", "knxip.hostprotocol", FT_UINT8, BASE_HEX, VALS( host_protocol_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_ip_address, { "IP Address", "knxip.ipaddr", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_port, { "Port Number", "knxip.port", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_description_type, { "Description Type", "knxip.dibtype", FT_UINT8, BASE_HEX, VALS( description_type_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_knx_medium, { "KNX Medium", "knxip.medium", FT_UINT8, BASE_HEX, VALS( medium_type_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_device_status, { "Device Status", "knxip.device.status", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_program_mode, { "Programming Mode", "knxip.progmode", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_knx_address, { "KNX Individual Address", "knxip.knxaddr", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_project_id, { "Project Installation Identifier", "knxip.project", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_project_number, { "Project Number", "knxip.project.nr", FT_UINT16, BASE_DEC, NULL, 0xFFF0, NULL, HFILL } },
+ { &hf_knxip_installation_number, { "Installation Number", "knxip.project.installation", FT_UINT16, BASE_DEC, NULL, 0x000F, NULL, HFILL } },
+ { &hf_knxip_serial_number, { "KNX Serial Number", "knxip.sernr", FT_UINT48, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_multicast_address, { "Multicast Address", "knxip.mcaddr", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_mac_address, { "MAC Address", "knxip.macaddr", FT_ETHER, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_friendly_name, { "Friendly Name", "knxip.device.name", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_service_version, { "Service Version", "knxip.service.version", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_security_version, { "Security Version", "knxip.security.version", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_manufacturer_code, { "KNX Manufacturer Code", "knxip.manufacturer", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_connection_type, { "Connection Type", "knxip.conn.type", FT_UINT8, BASE_HEX, VALS( connection_type_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_knx_layer, { "KNX Layer", "knxip.tunnel.layer", FT_UINT8, BASE_HEX, VALS( knx_layer_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_channel, { "Channel", "knxip.channel", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_status, { "Status", "knxip.status", FT_UINT8, BASE_HEX, VALS( error_vals ), 0, NULL, HFILL } },
+ { &hf_knxip_reserved, { "Reserved", "knxip.reserved", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_seq_counter, { "Sequence Counter", "knxip.seqctr", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_ip_subnet, { "Subnet Mask", "knxip.subnet", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_ip_gateway, { "Default Gateway", "knxip.gateway", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_ip_assign, { "IP Assignment", "knxip.ipassign", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_ip_caps, { "IP Capabilities", "knxip.ipcaps", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_ip_dhcp, { "DHCP Server", "knxip.dhcp", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_tunnel_feature, { "Tunneling Feature Identifier", "knxip.tunnel.feature", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_routing_loss, { "Lost Messages", "knxip.loss", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_busy_time, { "Wait Time", "knxip.busy.time", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_busy_control, { "Control", "knxip.busy.control", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_selector, { "Selector", "knxip.selector", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_max_apdu_length, { "Max APDU Length", "knxip.maxapdulength", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_medium_status, { "Medium Status", "knxip.medium.status", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_mask_version, { "Mask Version", "knxip.mask.version", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_srp_mandatory, { "Mandatory", "knxip.srp.mandatory", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
+ { &hf_knxip_srp_type, { "SRP Type", "knxip.srp.type", FT_UINT8, BASE_HEX, NULL, 0x7F, NULL, HFILL } },
+ { &hf_knxip_reset_command, { "Command", "knxip.reset.command", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_session, { "Session", "knxip.session", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_tag, { "Tag", "knxip.tag", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_user, { "User", "knxip.user", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
+ { &hf_knxip_session_status, { "Status", "knxip.session.status", FT_UINT8, BASE_HEX, VALS( session_status_vals ), 0, NULL, HFILL } },
+ };
+
+ /* Subtrees */
+ static gint *ett[] =
+ {
+ &ett_kip,
+ &ett_efcp,
+ &ett_service,
+ &ett_hpai,
+ &ett_dib,
+ &ett_medium,
+ &ett_status,
+ &ett_projectid,
+ &ett_service_family,
+ &ett_ip_assignment,
+ &ett_cri,
+ &ett_crd,
+ &ett_cnhdr,
+ &ett_loss,
+ &ett_busy,
+ &ett_selector,
+ &ett_decrypted,
+ &ett_tunnel,
+ };
+
+ static ei_register_info ei[] =
+ {
+ { &ei_knxip_error, { "knxip.error", PI_MALFORMED, PI_ERROR, "KNX/IP error", EXPFILL }},
+ { &ei_knxip_warning, { "knxip.warning", PI_PROTOCOL, PI_WARN, "KNX/IP warning", EXPFILL }},
+ };
+
+ expert_module_t* expert_knxip;
+ module_t* knxip_module;
+ guint8 x;
+
+ proto_knxip = proto_register_protocol( "KNX/IP", "KNX/IP", "kip" );
+
+ proto_register_field_array( proto_knxip, hf, array_length( hf ) );
+ proto_register_subtree_array( ett, array_length( ett ) );
+
+ register_dissector( "udp.knxip", dissect_udp_knxip, proto_knxip );
+ register_dissector( "tcp.knxip", dissect_tcp_knxip, proto_knxip );
+
+ //register_dissector_table( "knxip.version", "KNXnet/IP Protocol Version", proto_knxip, FT_UINT8, BASE_HEX );
+
+ expert_knxip = expert_register_protocol( proto_knxip );
+ expert_register_field_array( expert_knxip, ei, array_length( ei ) );
+
+ knxip_module = prefs_register_protocol( proto_knxip, proto_reg_handoff_knxip );
+
+ prefs_register_filename_preference( knxip_module, "key_file", "Key file", "Keyring.XML file (exported from ETS)",
+ &pref_key_file_name, FALSE );
+ prefs_register_string_preference( knxip_module, "key_file_pwd", "Key file password", "Keyring password",
+ &pref_key_file_pwd );
+ prefs_register_filename_preference( knxip_module, "key_info_file", "Key info output file", "Output file (- for stdout) for keys extracted from key file",
+ &pref_key_info_file_name, FALSE );
+
+ prefs_register_static_text_preference( knxip_module, "", "", NULL );
+
+ prefs_register_static_text_preference( knxip_module, "keys_0",
+ "KNX decryption keys",
+ NULL );
+ prefs_register_static_text_preference( knxip_module, "keys_1",
+ "(KNX/IP multicast/group keys, KNX/IP unicast session keys, KNX data-security tool keys and link-table keys)",
+ NULL );
+ prefs_register_static_text_preference( knxip_module, "keys_2",
+ "(format: 16 bytes as hex; example: A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF)",
+ NULL );
+
+ for( x = 1; x <= MAX_KNX_DECRYPTION_KEYS; ++x )
+ {
+ gchar* name = wmem_strdup_printf( wmem_epan_scope(), "key_%u", x );
+ gchar* title = wmem_strdup_printf( wmem_epan_scope(), "%u. key", x );
+ prefs_register_string_preference( knxip_module, name, title,
+ "KNX decryption key (format: 16 bytes as hex; example: A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF)",
+ &pref_key_texts[ x - 1 ] );
+ }
+}
+
+void proto_reg_handoff_knxip( void )
+{
+ dissector_handle_t knxip_handle;
+ guint8 x;
+ const gchar* text;
+
+ knxip_handle = find_dissector( "udp.knxip" );
+ dissector_add_uint( "udp.port", 3671, knxip_handle );
+ dissector_add_uint( "udp.port", 3672, knxip_handle );
+ dissector_add_uint( "udp.port", 40000, knxip_handle );
+
+ knxip_handle = find_dissector( "tcp.knxip" );
+ dissector_add_uint( "tcp.port", 3671, knxip_handle );
+ dissector_add_uint( "tcp.port", 3672, knxip_handle );
+ dissector_add_uint( "tcp.port", 40000, knxip_handle );
+
+ /* Evaluate preferences
+ */
+ if( pref_key_file_name )
+ {
+ /* Read Keyring.XML file (containing decryption keys, exported from ETS) */
+ read_knx_keyring_xml_file( pref_key_file_name, pref_key_file_pwd, pref_key_info_file_name );
+ }
+
+ knx_decryption_key_count = 0;
+ for( x = 0; x < MAX_KNX_DECRYPTION_KEYS && knx_decryption_key_count < MAX_KNX_DECRYPTION_KEYS; ++x )
+ {
+ text = pref_key_texts[ x ];
+ if( text )
+ {
+ if( hex_to_knx_key( text, knx_decryption_keys[ knx_decryption_key_count ] ) )
+ {
+ ++knx_decryption_key_count;
+ }
+ }
+ }
+}
+
+/*
+ * 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:
+ */
diff --git a/epan/dissectors/packet-knxip.h b/epan/dissectors/packet-knxip.h
new file mode 100644
index 0000000000..b5a04bdaa4
--- /dev/null
+++ b/epan/dissectors/packet-knxip.h
@@ -0,0 +1,56 @@
+/* packet-knxip.h
+ * Routines for KNXnet/IP dissection
+ * Copyright 2004, Jan Kessler <kessler@ise.de>
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef PACKET_KNXIP_H
+#define PACKET_KNXIP_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <epan/expert.h>
+#include <epan/packet.h>
+#include <epan/proto.h>
+#include <epan/ipproto.h>
+#include <epan/prefs.h>
+#include <epan/tvbuff.h>
+
+#include "packet-knxip_decrypt.h"
+
+#define KIP_ERROR &ei_knxip_error
+#define KIP_WARNING &ei_knxip_warning
+
+extern expert_field ei_knxip_error;
+extern expert_field ei_knxip_warning;
+
+extern guint8 knxip_host_protocol;
+extern guint8 knxip_error;
+
+#define MAX_KNX_DECRYPTION_KEYS 10
+
+extern guint8 knx_decryption_keys[ MAX_KNX_DECRYPTION_KEYS ][ KNX_KEY_LENGTH ];
+extern guint8 knx_decryption_key_count;
+
+#endif
+
+/*
+ * 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:
+ */
diff --git a/epan/dissectors/packet-knxip_decrypt.c b/epan/dissectors/packet-knxip_decrypt.c
new file mode 100644
index 0000000000..d9648257c4
--- /dev/null
+++ b/epan/dissectors/packet-knxip_decrypt.c
@@ -0,0 +1,811 @@
+/* packet-knxip_decrypt.c
+ * Decryption keys and decryption functions for KNX/IP Dissector
+ * Copyright 2018, ise GmbH <Ralf.Nasilowski@ise.de>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+// Activate g_debug output with environment variable: G_MESSAGES_DEBUG=packet-knxip
+#define G_LOG_DOMAIN "packet-knxip"
+
+#include <wsutil/file_util.h>
+#include "proto.h"
+#include "packet-knxip_decrypt.h"
+#include <epan/wmem/wmem.h>
+#include <wsutil/wsgcrypt.h>
+#include <wsutil/strtoi.h>
+
+#define TEXT_BUFFER_SIZE 128
+
+#define IPA_SIZE 4 // = size of IPv4 address
+
+#define BASE64_KNX_KEY_LENGTH 24 // = length of base64 encoded KNX key
+
+struct knx_keyring_mca_keys* knx_keyring_mca_keys;
+struct knx_keyring_ga_keys* knx_keyring_ga_keys;
+struct knx_keyring_ga_senders* knx_keyring_ga_senders;
+struct knx_keyring_ia_keys* knx_keyring_ia_keys;
+struct knx_keyring_ia_seqs* knx_keyring_ia_seqs;
+
+// Encrypt 16-byte block via AES
+static void encrypt_block( const guint8 key[ KNX_KEY_LENGTH ], const guint8 plain[ KNX_KEY_LENGTH ], guint8 p_crypt[ KNX_KEY_LENGTH ] )
+{
+ gcry_cipher_hd_t cryptor = NULL;
+ gcry_cipher_open( &cryptor, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0 );
+ gcry_cipher_setkey( cryptor, key, KNX_KEY_LENGTH );
+ gcry_cipher_encrypt( cryptor, p_crypt, KNX_KEY_LENGTH, plain, KNX_KEY_LENGTH );
+ gcry_cipher_close( cryptor );
+}
+
+// Create B_0 for CBC-MAC
+static void build_b0( guint8 p_result[ KNX_KEY_LENGTH ], const guint8* nonce, guint8 nonce_length )
+{
+ DISSECTOR_ASSERT( nonce_length <= KNX_KEY_LENGTH );
+ if( nonce_length ) memcpy( p_result, nonce, nonce_length );
+ memset( p_result + nonce_length, 0, KNX_KEY_LENGTH - nonce_length );
+}
+
+// Create Ctr_0 for CCM encryption/decryption
+static void build_ctr0( guint8 p_result[ KNX_KEY_LENGTH ], const guint8* nonce, guint8 nonce_length )
+{
+ build_b0( p_result, nonce, nonce_length );
+ p_result[ KNX_KEY_LENGTH - 2 ] = 0xFF;
+}
+
+// Calculate MAC for KNX IP Security or KNX Data Security
+void knx_ccm_calc_cbc_mac( guint8* p_mac, const guint8 key[ KNX_KEY_LENGTH ],
+ const guint8* a_bytes, gint a_length, const guint8* p_bytes, gint p_length,
+ const guint8 b_0[ KNX_KEY_LENGTH ] )
+{
+ guint8 plain[ KNX_KEY_LENGTH ];
+ guint8 b_pos;
+
+ // Add B_0
+ memcpy( plain, b_0, KNX_KEY_LENGTH );
+ encrypt_block( key, plain, p_mac );
+
+ // Add a_length
+ plain[ 0 ] = (guint8) ((a_length >> 8) ^ p_mac[ 0 ]);
+ plain[ 1 ] = (guint8) ((a_length & 0xFF) ^ p_mac[ 1 ]);
+ b_pos = 2;
+
+ // Add a_bytes directly followed by p_bytes
+ while( a_length || p_length )
+ {
+ while( a_length && b_pos < KNX_KEY_LENGTH )
+ {
+ plain[ b_pos ] = *a_bytes++ ^ p_mac[ b_pos ];
+ --a_length;
+ ++b_pos;
+ }
+
+ while( p_length && b_pos < KNX_KEY_LENGTH )
+ {
+ plain[ b_pos ] = *p_bytes++ ^ p_mac[ b_pos ];
+ --p_length;
+ ++b_pos;
+ }
+
+ while( b_pos < KNX_KEY_LENGTH )
+ {
+ plain[ b_pos ] = p_mac[ b_pos ];
+ ++b_pos;
+ }
+
+ encrypt_block( key, plain, p_mac );
+
+ b_pos = 0;
+ }
+}
+
+// Calculate MAC for KNX IP Security, using 6-byte Sequence ID
+void knxip_ccm_calc_cbc_mac( guint8* p_mac, const guint8 key[ KNX_KEY_LENGTH ],
+ const guint8* a_bytes, gint a_length, const guint8* p_bytes, gint p_length,
+ const guint8* nonce, guint8 nonce_length )
+{
+ guint8 b_0[ KNX_KEY_LENGTH ];
+ build_b0( b_0, nonce, nonce_length );
+ b_0[ KNX_KEY_LENGTH - 2 ] = (guint8) (p_length >> 8);
+ b_0[ KNX_KEY_LENGTH - 1 ] = (guint8) (p_length & 0xFF);
+ knx_ccm_calc_cbc_mac( p_mac, key, a_bytes, a_length, p_bytes, p_length, b_0 );
+}
+
+// Encrypt for KNX IP Security or KNX Data Security
+guint8* knx_ccm_encrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* p_bytes, gint p_length,
+ const guint8* mac, guint8 mac_length, const guint8 ctr_0[ KNX_KEY_LENGTH ], guint8 s0_bytes_used_for_mac )
+{
+ if( p_length >= 0 && !(p_length && !p_bytes) )
+ {
+ // NB: mac_length = 16 (for IP Security), or 4 (for Data Security)
+
+ guint8* result = p_result ? p_result : (guint8*) wmem_alloc( wmem_packet_scope(), p_length + mac_length );
+
+ guint8* dest = result;
+
+ guint8 ctr[ KNX_KEY_LENGTH ];
+ guint8 mask[ KNX_KEY_LENGTH ];
+ guint8 mask_0[ KNX_KEY_LENGTH ];
+ guint8 b_pos;
+
+ // Encrypt ctr_0 for mac
+ memcpy( ctr, ctr_0, KNX_KEY_LENGTH );
+ encrypt_block( key, ctr, mask_0 );
+
+ // Encrypt p_bytes with rest of S_0, only if mac_length < 16.
+ b_pos = s0_bytes_used_for_mac;
+ while (p_length && b_pos < KNX_KEY_LENGTH )
+ {
+ *dest++ = mask_0[b_pos++] ^ *p_bytes++;
+ --p_length;
+ }
+
+ // Encrypt p_bytes
+ while( p_length )
+ {
+ // Increment and encrypt ctr
+ ++ctr[ KNX_KEY_LENGTH - 1 ];
+ encrypt_block( key, ctr, mask );
+
+ // Encrypt input block via encrypted ctr
+ b_pos = 0;
+ while( p_length && b_pos < KNX_KEY_LENGTH )
+ {
+ *dest++ = mask[ b_pos++] ^ *p_bytes++;
+ --p_length;
+ }
+ }
+
+ if( mac )
+ {
+ if( mac_length > KNX_KEY_LENGTH )
+ {
+ mac_length = KNX_KEY_LENGTH;
+ }
+
+ // Encrypt and append mac
+ b_pos = 0;
+ while( mac_length )
+ {
+ *dest++ = mask_0[ b_pos++] ^ *mac++;
+ --mac_length;
+ }
+ }
+
+ return result;
+ }
+
+ return NULL;
+}
+
+// Encrypt for KNX IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
+guint8* knxip_ccm_encrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* p_bytes, gint p_length,
+ const guint8* mac, const guint8* nonce, guint8 nonce_length )
+{
+ guint8 ctr_0[ KNX_KEY_LENGTH ];
+ build_ctr0( ctr_0, nonce, nonce_length );
+ return knx_ccm_encrypt( p_result, key, p_bytes, p_length, mac, KNX_KEY_LENGTH, ctr_0, KNX_KEY_LENGTH );
+}
+
+// Decrypt for KNX-IP Security (with 16-byte MAC and Nonce based on 6-byte Sequence ID)
+guint8* knxip_ccm_decrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* crypt, gint crypt_length,
+ const guint8* nonce, guint8 nonce_length )
+{
+ gint p_length = crypt_length - KNX_KEY_LENGTH;
+ guint8 ctr_0[ KNX_KEY_LENGTH ];
+ build_ctr0( ctr_0, nonce, nonce_length );
+ return knx_ccm_encrypt( p_result, key, crypt, p_length, crypt + p_length, KNX_KEY_LENGTH, ctr_0, KNX_KEY_LENGTH );
+}
+
+static void fprintf_hex( FILE* f, const guint8* data, guint8 length )
+{
+ for( ; length; --length ) fprintf( f, " %02X", *data++ );
+ fputc( '\n', f );
+}
+
+static void clear_keyring_data( void )
+{
+ while( knx_keyring_mca_keys )
+ {
+ struct knx_keyring_mca_keys* mca_key = knx_keyring_mca_keys;
+ knx_keyring_mca_keys = mca_key->next;
+ wmem_free( wmem_epan_scope(), mca_key );
+ }
+
+ while( knx_keyring_ga_keys )
+ {
+ struct knx_keyring_ga_keys* ga_key = knx_keyring_ga_keys;
+ knx_keyring_ga_keys = ga_key->next;
+ wmem_free( wmem_epan_scope(), ga_key );
+ }
+
+ while( knx_keyring_ga_senders )
+ {
+ struct knx_keyring_ga_senders* ga_sender = knx_keyring_ga_senders;
+ knx_keyring_ga_senders = ga_sender->next;
+ wmem_free( wmem_epan_scope(), ga_sender );
+ }
+
+ while( knx_keyring_ia_keys )
+ {
+ struct knx_keyring_ia_keys* ia_key = knx_keyring_ia_keys;
+ knx_keyring_ia_keys = ia_key->next;
+ wmem_free( wmem_epan_scope(), ia_key );
+ }
+
+ while( knx_keyring_ia_seqs )
+ {
+ struct knx_keyring_ia_seqs* ia_seq = knx_keyring_ia_seqs;
+ knx_keyring_ia_seqs = ia_seq->next;
+ wmem_free( wmem_epan_scope(), ia_seq );
+ }
+}
+
+// Read IP address
+static void read_ip_addr( guint8 result[ 4 ], const gchar* text )
+{
+ ws_in4_addr value = 0;
+ if( ws_inet_pton4( text, &value ) )
+ memcpy( result, &value, 4 );
+ else
+ memset( result, 0, 4 );
+}
+
+// Read KNX group address
+static guint16 read_ga( const gchar* text )
+{
+ guint a[ 3 ];
+ gint n = sscanf( text, "%u/%u/%u", a, a + 1, a + 2 );
+ return
+ (n == 1) ? (guint16) a[ 0 ] :
+ (n == 2) ? (guint16) ((a[ 0 ] << 11) | a[ 1 ]) :
+ (n == 3) ? (guint16) ((a[ 0 ] << 11) | (a[ 1 ] << 8) | a[ 2 ]) :
+ 0;
+}
+
+// Read KNX individual address
+static guint16 read_ia( const gchar* text )
+{
+ guint a[ 3 ];
+ gint n = sscanf( text, "%u.%u.%u", a, a + 1, a + 2 );
+ return
+ (n == 1) ? (guint16) a[ 0 ] :
+ (n == 2) ? (guint16) ((a[ 0 ] << 8) | a[ 1 ]) :
+ (n == 3) ? (guint16) ((a[ 0 ] << 12) | (a[ 1 ] << 8) | a[ 2 ]) :
+ 0;
+}
+
+// Read 6-byte sequence number from decimal representation
+static guint64 read_seq( const gchar* text )
+{
+ guint64 result;
+ return ws_strtou64( text, NULL, &result ) ? result : 0;
+}
+
+// Decrypt key
+static void decrypt_key( guint8 key[] _U_, guint8 password_hash[] _U_, guint8 created_hash[] _U_ )
+{
+ // TODO: decrypt as AES128-CBC(key, password_hash, created_hash)
+}
+
+// Decode and decrypt key
+static void decode_and_decrypt_key( guint8 key[ BASE64_KNX_KEY_LENGTH + 1 ], const gchar* text, guint8 password_hash[], guint8 created_hash[] )
+{
+ gsize out_len;
+ g_snprintf( (gchar*) key, BASE64_KNX_KEY_LENGTH + 1, "%s", text );
+ g_base64_decode_inplace( (gchar*) key, &out_len );
+ decrypt_key( key, password_hash, created_hash );
+}
+
+// Add MCA <-> key association
+static void add_mca_key( const guint8 mca[ IPA_SIZE ], const gchar* text, guint8 password_hash[], guint8 created_hash[], FILE* f2 )
+{
+ gint text_length = (gint) strlen( text );
+
+ if( text_length == BASE64_KNX_KEY_LENGTH )
+ {
+ guint8 key[ BASE64_KNX_KEY_LENGTH + 1 ];
+ struct knx_keyring_mca_keys** mca_keys_next;
+ struct knx_keyring_mca_keys* mca_key;
+
+ decode_and_decrypt_key( key, text, password_hash, created_hash );
+
+ mca_keys_next = &knx_keyring_mca_keys;
+
+ while( (mca_key = *mca_keys_next) != NULL )
+ {
+ if( memcmp( mca_key->mca, mca, IPA_SIZE ) == 0 )
+ {
+ if( memcmp( mca_key->key, key, KNX_KEY_LENGTH ) == 0 )
+ {
+ return;
+ }
+ }
+
+ mca_keys_next = &mca_key->next;
+ }
+
+ if( f2 )
+ {
+ fprintf( f2, "MCA %u.%u.%u.%u key", mca[ 0 ], mca[ 1 ], mca[ 2 ], mca[ 3 ] );
+ fprintf_hex( f2, key, KNX_KEY_LENGTH );
+ }
+
+ mca_key = (struct knx_keyring_mca_keys*) wmem_alloc( wmem_epan_scope(), sizeof( struct knx_keyring_mca_keys ) );
+
+ if( mca_key )
+ {
+ mca_key->next = NULL;
+ memcpy( mca_key->mca, mca, IPA_SIZE );
+ memcpy( mca_key->key, key, KNX_KEY_LENGTH );
+
+ *mca_keys_next = mca_key;
+ }
+ }
+}
+
+// Add GA <-> key association
+static void add_ga_key( guint16 ga, const gchar* text, guint8 password_hash[], guint8 created_hash[], FILE* f2 )
+{
+ gint text_length = (gint) strlen( text );
+
+ if( text_length == BASE64_KNX_KEY_LENGTH )
+ {
+ guint8 key[ BASE64_KNX_KEY_LENGTH + 1 ];
+ struct knx_keyring_ga_keys** ga_keys_next;
+ struct knx_keyring_ga_keys* ga_key;
+
+ decode_and_decrypt_key( key, text, password_hash, created_hash );
+
+ ga_keys_next = &knx_keyring_ga_keys;
+
+ while( (ga_key = *ga_keys_next) != NULL )
+ {
+ if( ga_key->ga == ga )
+ {
+ if( memcmp( ga_key->key, key, KNX_KEY_LENGTH ) == 0 )
+ {
+ return;
+ }
+ }
+
+ ga_keys_next = &ga_key->next;
+ }
+
+ if( f2 )
+ {
+ fprintf( f2, "GA %u/%u/%u key", (ga >> 11) & 0x1F, (ga >> 8) & 0x7, ga & 0xFF );
+ fprintf_hex( f2, key, KNX_KEY_LENGTH );
+ }
+
+ ga_key = (struct knx_keyring_ga_keys*) wmem_alloc( wmem_epan_scope(), sizeof( struct knx_keyring_ga_keys ) );
+
+ if( ga_key )
+ {
+ ga_key->next = NULL;
+ ga_key->ga = ga;
+ memcpy( ga_key->key, key, KNX_KEY_LENGTH );
+
+ *ga_keys_next = ga_key;
+ }
+ }
+}
+
+// Add GA <-> sender association
+static void add_ga_sender( guint16 ga, const gchar* text, FILE* f2 )
+{
+ guint16 ia = read_ia( text );
+ struct knx_keyring_ga_senders** ga_senders_next = &knx_keyring_ga_senders;
+ struct knx_keyring_ga_senders* ga_sender;
+
+ while( (ga_sender = *ga_senders_next) != NULL )
+ {
+ if( ga_sender->ga == ga )
+ {
+ if( ga_sender->ia == ia )
+ {
+ return;
+ }
+ }
+
+ ga_senders_next = &ga_sender->next;
+ }
+
+ if( f2 )
+ {
+ fprintf( f2, "GA %u/%u/%u sender %u.%u.%u\n", (ga >> 11) & 0x1F, (ga >> 8) & 0x7, ga & 0xFF, (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF );
+ }
+
+ ga_sender = (struct knx_keyring_ga_senders*) wmem_alloc( wmem_epan_scope(), sizeof( struct knx_keyring_ga_senders ) );
+
+ if( ga_sender )
+ {
+ ga_sender->next = NULL;
+ ga_sender->ga = ga;
+ ga_sender->ia = ia;
+
+ *ga_senders_next = ga_sender;
+ }
+}
+
+// Add IA <-> key association
+static void add_ia_key( guint16 ia, const gchar* text, guint8 password_hash[], guint8 created_hash[], FILE* f2 )
+{
+ gint text_length = (gint) strlen( text );
+
+ if( text_length == BASE64_KNX_KEY_LENGTH )
+ {
+ guint8 key[ BASE64_KNX_KEY_LENGTH + 1 ];
+ struct knx_keyring_ia_keys** ia_keys_next;
+ struct knx_keyring_ia_keys* ia_key;
+
+ decode_and_decrypt_key( key, text, password_hash, created_hash );
+
+ ia_keys_next = &knx_keyring_ia_keys;
+
+ while( (ia_key = *ia_keys_next) != NULL )
+ {
+ if( ia_key->ia == ia )
+ {
+ if( memcmp( ia_key->key, key, KNX_KEY_LENGTH ) == 0 )
+ {
+ return;
+ }
+ }
+
+ ia_keys_next = &ia_key->next;
+ }
+
+ if( f2 )
+ {
+ fprintf( f2, "IA %u.%u.%u key", (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF );
+ fprintf_hex( f2, key, KNX_KEY_LENGTH );
+ }
+
+ ia_key = (struct knx_keyring_ia_keys*) wmem_alloc( wmem_epan_scope(), sizeof( struct knx_keyring_ia_keys ) );
+
+ if( ia_key )
+ {
+ ia_key->next = NULL;
+ ia_key->ia = ia;
+ memcpy( ia_key->key, key, KNX_KEY_LENGTH );
+
+ *ia_keys_next = ia_key;
+ }
+ }
+}
+
+// Add IA <-> sequence number association
+static void add_ia_seq( guint16 ia, const gchar* text, FILE* f2 )
+{
+ guint64 seq = read_seq( text );
+
+ struct knx_keyring_ia_seqs** ia_seqs_next = &knx_keyring_ia_seqs;
+ struct knx_keyring_ia_seqs* ia_seq;
+
+ while( (ia_seq = *ia_seqs_next) != NULL )
+ {
+ if( ia_seq->ia == ia )
+ {
+ if( ia_seq->seq == seq )
+ {
+ return;
+ }
+ }
+
+ ia_seqs_next = &ia_seq->next;
+ }
+
+ if( f2 )
+ {
+ fprintf( f2, "IA %u.%u.%u SeqNr %" G_GINT64_MODIFIER "u\n", (ia >> 12) & 0xF, (ia >> 8) & 0xF, ia & 0xFF, seq );
+ }
+
+ ia_seq = (struct knx_keyring_ia_seqs*) wmem_alloc( wmem_epan_scope(), sizeof( struct knx_keyring_ia_seqs ) );
+
+ if( ia_seq )
+ {
+ ia_seq->next = NULL;
+ ia_seq->ia = ia;
+ ia_seq->seq = seq;
+
+ *ia_seqs_next = ia_seq;
+ }
+}
+
+// Calculate PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
+static void make_password_hash( guint8 password_hash[] _U_, const gchar* password _U_ )
+{
+ // TODO: password_hash = PBKDF2(HMAC-SHA256, password, "1.keyring.ets.knx.org", 65536, 128)
+}
+
+// Calculate MSB128(SHA256(created))
+static void make_created_hash( guint8 created_hash[] _U_, const gchar* created _U_ )
+{
+ // TODO: created_hash = MSB128(SHA256(created))
+}
+
+// Read KNX security key info from keyring XML file.
+//
+// An example keyring XML file is
+// "test/keys/knx_keyring.xml".
+//
+// Corresponding test is
+// suite_decryption.case_decrypt_knxip.test_knxip_keyring_xml_import
+//
+// We do not use LibXml2 here, because
+// (1) we want to be platform independent,
+// (2) we just want to extract some data from the keyring XML file,
+// (3) we want to avoid the complicated recursive DOM processing implied by LibXml2.
+//
+// Resulting decoded and decrypted 16-byte keys with context info are optionally written to a "key info" text file.
+// This may be useful, as these keys are not directly available from the keyring XML file .
+void read_knx_keyring_xml_file( const gchar* key_file, const gchar* password, const gchar* key_info_file )
+{
+ // Clear old keyring data
+ clear_keyring_data();
+
+ // Read new data from keyring XML file
+ FILE* f = ws_fopen( key_file, "r" );
+
+ // Optionally write extracted data to key info file
+ FILE* f2 = (!key_info_file || !*key_info_file) ? NULL :
+ (strcmp( key_info_file, "-" ) == 0) ? stdout :
+ ws_fopen( key_info_file, "w" );
+
+ if( f )
+ {
+ guint8 backbone_mca[ IPA_SIZE ];
+ guint8 backbone_mca_valid = 0;
+ guint16 group_ga = 0;
+ guint8 group_ga_valid = 0;
+ guint16 device_ia = 0;
+ guint8 device_ia_valid = 0;
+ gchar name[ TEXT_BUFFER_SIZE ];
+ gchar value[ TEXT_BUFFER_SIZE ];
+ guint8 password_hash[ KNX_KEY_LENGTH ];
+ guint8 created_hash[ KNX_KEY_LENGTH ];
+ gchar tag_name[ TEXT_BUFFER_SIZE ];
+ guint8 tag_name_done = 0;
+ guint8 tag_end = 0;
+ guint8 in_tag = 0;
+
+ memset( backbone_mca, 0, IPA_SIZE );
+ *name = '\0';
+ *value = '\0';
+ memset( password_hash, 0, KNX_KEY_LENGTH );
+ memset( created_hash, 0, KNX_KEY_LENGTH );
+ *tag_name = '\0';
+
+ make_password_hash( password_hash, password );
+
+ g_debug( "%s:", key_file );
+
+ gint c = fgetc( f );
+
+ while( c >= 0 )
+ {
+ if( c == '<' ) // tag start
+ {
+ in_tag = 1;
+ tag_end = 0;
+ *tag_name = 0;
+ tag_name_done = 0;
+ *name = '\0';
+ *value = '\0';
+ }
+ else if( c == '>' ) // tag end
+ {
+ in_tag = 0;
+ }
+ else if( c == '/' )
+ {
+ if( in_tag ) // "</" or "/>"
+ {
+ tag_end = 1;
+ *tag_name = 0;
+ tag_name_done = 0;
+ *name = '\0';
+ *value = '\0';
+ }
+ }
+ else if( g_ascii_isalpha( c ) || c == '_' ) // possibly tag name, or attribute name
+ {
+ size_t length = 0;
+ name[ length++ ] = (gchar) c;
+ while( (c = fgetc( f )) >= 0 )
+ {
+ if( g_ascii_isalnum( c ) || c == '_' )
+ {
+ if( length < sizeof name + 1 )
+ {
+ name[ length++ ] = (gchar) c;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ name[ length ] = '\0';
+ *value = '\0';
+
+ if( !tag_name_done ) // tag name
+ {
+ g_snprintf( tag_name, sizeof tag_name, "%s", name );
+ *name = '\0';
+ tag_name_done = 1;
+ }
+ else // Check for name="value" construct
+ {
+ while( c >= 0 && g_ascii_isspace( c ) ) c = fgetc( f );
+
+ if( c == '=' )
+ {
+ while( (c = fgetc( f )) >= 0 && g_ascii_isspace( c ) );
+
+ if( c == '"' )
+ {
+ length = 0;
+
+ while( (c = fgetc( f )) >= 0 )
+ {
+ if( c == '"' )
+ {
+ c = fgetc( f );
+ if( c != '"' )
+ {
+ break;
+ }
+ }
+ if( length < sizeof value - 1 )
+ {
+ value[ length++ ] = (gchar) c;
+ }
+ }
+
+ value[ length ] = 0;
+
+ if( !tag_end )
+ {
+ // Found name="value" construct between < and >
+ g_debug( "%s %s=%s", tag_name, name, value );
+
+ // Process name/value pair
+ if( strcmp( tag_name, "Keyring" ) == 0 )
+ {
+ if( strcmp( name, "Created" ) == 0 )
+ {
+ make_created_hash( created_hash, value );
+ }
+ }
+ else if( strcmp( tag_name, "Backbone" ) == 0 )
+ {
+ group_ga_valid = 0;
+ device_ia_valid = 0;
+
+ if( strcmp( name, "MulticastAddress" ) == 0 )
+ {
+ read_ip_addr( backbone_mca, value );
+ backbone_mca_valid = 1;
+ }
+ else if( strcmp( name, "Key" ) == 0 )
+ {
+ if( backbone_mca_valid )
+ {
+ add_mca_key( backbone_mca, value, password_hash, created_hash, f2 );
+ }
+ }
+ }
+ else if( strcmp( tag_name, "Group" ) == 0 )
+ {
+ backbone_mca_valid = 0;
+ device_ia_valid = 0;
+
+ if( strcmp( name, "Address" ) == 0 )
+ {
+ group_ga = read_ga( value );
+ group_ga_valid = 1;
+ }
+ else if( strcmp( name, "Key" ) == 0 )
+ {
+ if( group_ga_valid )
+ {
+ add_ga_key( group_ga, value, password_hash, created_hash, f2 );
+ }
+ }
+ else if( strcmp( name, "Senders" ) == 0 )
+ {
+ if( group_ga_valid )
+ {
+ // Add senders given by space separated list of KNX IAs
+ static const gchar delim[] = " ,";
+ const gchar* token = strtok( value, delim );
+ while( token )
+ {
+ add_ga_sender( group_ga, token, f2 );
+ token = strtok( NULL, delim );
+ }
+ }
+ }
+ }
+ else if( strcmp( tag_name, "Device" ) == 0 )
+ {
+ backbone_mca_valid = 0;
+ group_ga_valid = 0;
+
+ if( strcmp( name, "IndividualAddress" ) == 0 )
+ {
+ device_ia = read_ia( value );
+ device_ia_valid = 1;
+ }
+ else if( strcmp( name, "ToolKey" ) == 0 )
+ {
+ if( device_ia_valid )
+ {
+ add_ia_key( device_ia, value, password_hash, created_hash, f2 );
+ }
+ }
+ else if( strcmp( name, "SequenceNumber" ) == 0 )
+ {
+ if( device_ia_valid )
+ {
+ add_ia_seq( device_ia, value, f2 );
+ }
+ }
+ }
+ else
+ {
+ backbone_mca_valid = 0;
+ group_ga_valid = 0;
+ device_ia_valid = 0;
+ }
+ }
+ }
+ }
+ }
+
+ if( c < 0 ) // EOF
+ {
+ break;
+ }
+
+ continue;
+ }
+ else
+ {
+ if( !g_ascii_isspace( c ) )
+ {
+ tag_name_done = 1;
+ *name = '\0';
+ *value = '\0';
+ }
+ }
+
+ c = fgetc( f );
+ }
+
+ fclose( f );
+ }
+
+ if( f2 && f2 != stdout )
+ {
+ fclose( f2 );
+ }
+}
+
+/*
+ * 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:
+ */
diff --git a/epan/dissectors/packet-knxip_decrypt.h b/epan/dissectors/packet-knxip_decrypt.h
new file mode 100644
index 0000000000..af25b2a7c2
--- /dev/null
+++ b/epan/dissectors/packet-knxip_decrypt.h
@@ -0,0 +1,97 @@
+/* packet-knxip_decrypt.h
+ * Decryption keys and decryption functions for KNX/IP Dissector
+ * Copyright 2018, ise GmbH <Ralf.Nasilowski@ise.de>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef KNXIP_CRYPT_H
+#define KNXIP_CRYPT_H
+
+#define KNX_KEY_LENGTH 16
+
+// Calculate MAC for KNX IP Security or KNX Data Security
+void knx_ccm_calc_cbc_mac( guint8 p_mac[ KNX_KEY_LENGTH ], const guint8 key[ KNX_KEY_LENGTH ],
+ const guint8* a_bytes, gint a_length, const guint8* p_bytes, gint p_length,
+ const guint8 b_0[ KNX_KEY_LENGTH ] );
+
+// Calculate MAC for KNX IP Security
+void knxip_ccm_calc_cbc_mac( guint8 p_mac[ KNX_KEY_LENGTH ], const guint8 key[ KNX_KEY_LENGTH ],
+ const guint8* a_bytes, gint a_length, const guint8* p_bytes, gint p_length,
+ const guint8* nonce, guint8 nonce_length );
+
+// Encrypt for KNX IP Security or KNX Data Security
+guint8* knx_ccm_encrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* p_bytes, gint p_length,
+ const guint8* mac, guint8 mac_length, const guint8 ctr_0[ KNX_KEY_LENGTH ], guint8 s0_bytes_used_for_mac);
+
+// Encrypt for KNX IP Security
+guint8* knxip_ccm_encrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* p_bytes, gint p_length,
+ const guint8 mac[ KNX_KEY_LENGTH ], const guint8* nonce, guint8 nonce_length );
+
+// Decrypt for KNX IP Security
+guint8* knxip_ccm_decrypt( guint8* p_result, const guint8 key[ KNX_KEY_LENGTH ], const guint8* crypt, gint crypt_length,
+ const guint8* nonce, guint8 nonce_length );
+
+// For importing keyring.XML file exported from ETS:
+
+struct knx_keyring_mca_keys
+{
+ struct knx_keyring_mca_keys* next;
+ guint8 mca[ 4 ]; // IP multicast address
+ guint8 key[ KNX_KEY_LENGTH ]; // encryption key
+};
+
+struct knx_keyring_ga_keys
+{
+ struct knx_keyring_ga_keys* next;
+ guint16 ga; // KNX GA
+ guint8 key[ KNX_KEY_LENGTH ]; // encryption key
+};
+
+struct knx_keyring_ga_senders
+{
+ struct knx_keyring_ga_senders* next;
+ guint16 ga; // KNX GA
+ guint16 ia; // sending KNX IA
+};
+
+struct knx_keyring_ia_keys
+{
+ struct knx_keyring_ia_keys* next;
+ guint16 ia; // KNX IA
+ guint8 key[ KNX_KEY_LENGTH ]; // encryption key
+};
+
+struct knx_keyring_ia_seqs
+{
+ struct knx_keyring_ia_seqs* next;
+ guint16 ia; // KNX IA
+ guint64 seq; // 6-byte sequence number
+};
+
+extern struct knx_keyring_mca_keys* knx_keyring_mca_keys;
+extern struct knx_keyring_ga_keys* knx_keyring_ga_keys;
+extern struct knx_keyring_ga_senders* knx_keyring_ga_senders;
+extern struct knx_keyring_ia_keys* knx_keyring_ia_keys;
+extern struct knx_keyring_ia_seqs* knx_keyring_ia_seqs;
+
+// Read KNX security keys from keyring XML file (exported from ETS)
+void read_knx_keyring_xml_file( const gchar* key_file, const gchar* password, const gchar* key_info_file );
+
+#endif // KNXIP_CRYPT_H
+
+/*
+ * 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:
+ */
diff --git a/epan/dissectors/packet-knxnetip.c b/epan/dissectors/packet-knxnetip.c
deleted file mode 100644
index cb682dc85f..0000000000
--- a/epan/dissectors/packet-knxnetip.c
+++ /dev/null
@@ -1,1842 +0,0 @@
-/* packet-knxnetip.c
- * Routines for KNXnet/IP dissection
- * Copyright 2014, Alexander Gaertner <gaertner.alex@gmx.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 <epan/expert.h>
-
-#define KNXNETIP_PROTOCOL_VERSION 0x10
-#define KNXNETIP_HEADER_LENGTH 0x06
-
-#define SEARCH_REQ 0x0201
-#define SEARCH_RES 0x0202
-#define DESCRIPTION_REQ 0x0203
-#define DESCRIPTION_RES 0x0204
-#define CONNECT_REQ 0x0205
-#define CONNECT_RES 0x0206
-#define CONNECTIONSTATE_REQ 0x0207
-#define CONNECTIONSTATE_RES 0x0208
-#define DISCONNECT_REQ 0x0209
-#define DISCONNECT_RES 0x020A
-#define DEVICE_CONFIGURATION_REQ 0x0310
-#define DEVICE_CONFIGURATION_ACK 0x0311
-#define TUNNELLING_REQ 0x0420
-#define TUNNELLING_ACK 0x0421
-#define ROUTING_INDICATION 0x0530
-#define ROUTING_LOST 0x0531
-#define ROUTING_BUSY 0x0532
-#define REMOTE_DIAG_REQ 0x0740
-#define REMOTE_DIAG_RES 0x0741
-#define REMOTE_BASIC_CONF_REQ 0x0742
-#define REMOTE_RESET_REQ 0x0743
-#define DIB_DEVICE_INFO 0x01
-#define DIB_SUPP_SVC 0x02
-#define DIB_IP_CONF 0x03
-#define DIB_IP_CURRENT 0x04
-#define DIB_KNX_ADDRESS 0x05
-#define DIB_MFR_DATA 0xFE
-#define KNX_TUNNEL_CONNECTION 0x04
-#define FLAGS_DEVICESTATUS_RESERVED 0xFE
-#define FLAGS_DEVICESTATUS_PROGRAM 0x01
-#define FLAGS_IPCAPABILITES_RESERVED 0xF8
-#define FLAGS_IPCAPABILITES_BOOTIP 0x01
-#define FLAGS_IPCAPABILITES_DHCP 0x02
-#define FLAGS_IPCAPABILITES_AUTOIP 0x04
-#define FLAGS_DEVICESTATE_RESERVED 0xFC
-#define FLAGS_DEVICESTATE_KNX 0x01
-#define FLAGS_DEVICESTATE_IP 0x02
-/*for CEMI*/
-#define RAW_REQ 0x10
-#define DATA_REQ 0x11
-#define POLL_DATA_REQ 0x13
-#define POLL_DATA_CON 0x25
-#define DATA_IND 0x29
-#define BUSMON_IND 0x2B
-#define RAW_IND 0x2D
-#define DATA_CON 0x2E
-#define RAW_CON 0x2F
-#define DATA_CONNEC_REQ 0x41
-#define DATA_INDV_REQ 0x4A
-#define DATA_CONNEC_IND 0x89
-#define DATA_INDV_IND 0x94
-#define RESET_IND 0xF0
-#define RESET_REQ 0xF1
-#define PROPWRITE_CON 0xF5
-#define PROPWRITE_REQ 0xF6
-#define PROPINFO_IND 0xF7
-#define FUNCPROPCOM_REQ 0xF8
-#define FUNCPROPSTATREAD_REQ 0xF9
-#define FUNCPROPCOM_CON 0xFA
-#define PROPREAD_CON 0xFB
-#define PROPREAD_REQ 0xFC
-#define PL_INFO 0x1
-#define RF_INFO 0x2
-#define BUSMON_INFO 0x3
-#define TIME_REL 0x4
-#define TIME_DELAY 0x5
-#define EXEND_TIME 0x6
-#define BIBAT_INFO 0x7
-#define RF_MULTI 0x8
-#define PREAMBEL 0x9
-#define RF_FAST_ACK 0xA
-#define MANU_DATA 0xFE
-#define RESER 0xFF
-#define A_GROUPVALUE_RES 0x040
-#define A_GROUPVALUE_WRT 0x080
-#define A_ADC_RED 0x180
-#define A_ADC_RES 0x1C0
-#define A_MEM_RED 0x200
-#define A_MEM_RES 0x240
-#define A_MEM_WRT 0x280
-#define A_SYS_RED 0x1C8
-#define A_SYS_RES 0x1C9
-#define A_SYS_WRT 0x1CA
-#define A_SYS_BROAD 0x1CB
-#define GROUPADD 0x80
-#define COUPLER_SPECIFIC_SERVICE 0x3C0
-#define A_AUTHORIZE_REQ 0x3D1
-#define A_AUTHORIZE_RES 0x3D2
-#define A_KEY_WRT 0x3D3
-#define A_KEY_RES 0x3D4
-#define A_PROPVALUE_RED 0x3D5
-#define A_PROPVALUE_RES 0x3D6
-
-#define FLAGS_CEMI_CONTROL1_FT 0x80
-#define FLAGS_CEMI_CONTROL1_R 0x20
-#define FLAGS_CEMI_CONTROL1_SB 0x10
-#define FLAGS_CEMI_CONTROL1_P 0x0C
-#define FLAGS_CEMI_CONTROL1_A 0x02
-#define FLAGS_CEMI_CONTROL1_C 0x01
-#define FLAGS_CEMI_CONTROL2_AT 0x80
-#define FLAGS_CEMI_CONTROL2_HC 0x70
-#define FLAGS_CEMI_CONTROL2_EFF 0x0F
-#define FLAGS_CEMI_RF_RESERVED 0xC0
-#define FLAGS_CEMI_RF_MESURE 0x30
-#define FLAGS_CEMI_RF_MESURE_RE 0x0C
-#define FLAGS_CEMI_RF_BATTERY 0x02
-#define FLAGS_CEMI_RF_BIDIRETIONAL 0x01
-#define FLAGS_CEMI_BUS_F 0x80
-#define FLAGS_CEMI_BUS_B 0x40
-#define FLAGS_CEMI_BUS_P 0x20
-#define FLAGS_CEMI_BUS_D 0x10
-#define FLAGS_CEMI_BUS_L 0x08
-#define FLAGS_CEMI_BUS_SSS 0x07
-#define FLAGS_CEMI_FASTACK_CRC 0x400
-#define FLAGS_CEMI_FASTACK_ERROR 0x200
-#define FLAGS_CEMI_FASTACK_RES 0x100
-#define FLAGS_CEMI_FASTACK_INFO 0xFF
-
-
-void proto_register_knxnetip(void);
-void proto_reg_handoff_knxnetip(void);
-
-static int proto_knxnetip = -1;
-static int hf_knxnetip_headerlength = -1;
-static int hf_knxnetip_version = -1;
-static int hf_knxnetip_servicetype = -1;
-static int hf_knxnetip_totallength = -1;
-static int hf_knxnetip_hpai = -1;
-static int hf_knxnetip_hpai_structure_length = -1;
-static int hf_knxnetip_hpai_host_protocol = -1;
-static int hf_knxnetip_hpai_ip_address = -1;
-static int hf_knxnetip_hpai_port = -1;
-static int hf_knxnetip_dib = -1;
-static int hf_knxnetip_structure_length = -1;
-static int hf_knxnetip_dib_type = -1;
-static int hf_knxnetip_dib_medium = -1;
-static int hf_knxnetip_knxaddress = -1;
-static int hf_knxnetip_dib_projectid = -1;
-static int hf_knxnetip_dib_serialnumber = -1;
-static int hf_knxnetip_dib_multicast_address = -1;
-static int hf_knxnetip_mac_address = -1;
-static int hf_knxnetip_dib_friendly = -1;
-static int hf_knxnetip_dib_service = -1;
-static int hf_knxnetip_dib_ipaddress = -1;
-static int hf_knxnetip_dib_subnet = -1;
-static int hf_knxnetip_dib_gateway = -1;
-static int hf_knxnetip_dib_ipassign = -1;
-static int hf_knxnetip_dib_dhcp = -1;
-static int hf_knxnetip_dib_manuid = -1;
-static int hf_knxnetip_dib_manudata = -1;
-static int hf_knxnetip_cri = -1;
-static int hf_knxnetip_connection_type = -1;
-static int hf_knxnetip_cri_protocol_data = -1;
-static int hf_knxnetip_communication_channel_id = -1;
-static int hf_knxnetip_crd_protocol_data = -1;
-static int hf_knxnetip_crd = -1;
-static int hf_knxnetip_connect_status = -1;
-static int hf_knxnetip_connectionstate_status = -1;
-static int hf_knxnetip_counter = -1;
-static int hf_knxnetip_confack_status = -1;
-static int hf_knxnetip_tunnelack_status = -1;
-static int hf_knxnetip_numberoflost = -1;
-static int hf_knxnetip_busywaittime = -1;
-static int hf_knxnetip_busycontrol = -1;
-static int hf_knxnetip_knxlayer = -1;
-static int hf_knxnetip_selector_type = -1;
-static int hf_knxnetip_reset = -1;
-static int hf_knxnetip_projectnumber = -1;
-static int hf_knxnetip_installnumber = -1;
-static int hf_knxnetip_dib_svc_version = -1;
-static int hf_knxnetip_reserved = -1;
-static int hf_knxnetip_raw = -1;
-static int hf_knxnetip_data = -1;
-static int hf_knxnetip_additional = -1;
-static int hf_knxnetip_unknown = -1;
-static int hf_knxnetip_polldata = -1;
-
-
-static int hf_knxnetip_cemi = -1;
-static int hf_knxnetip_cemi_mc = -1;
-static int hf_knxnetip_cemi_addlength = -1;
-static int hf_knxnetip_cemi_additemlength = -1;
-static int hf_knxnetip_cemi_typid = -1;
-static int hf_knxnetip_cemi_type_pl = -1;
-static int hf_knxnetip_cemi_type_relt = -1;
-static int hf_knxnetip_cemi_type_delay = -1;
-static int hf_knxnetip_cemi_type_exttime = -1;
-static int hf_knxnetip_cemi_type_bibat = -1;
-static int hf_knxnetip_cemi_sourceaddress = -1;
-static int hf_knxnetip_cemi_destaddress = -1;
-static int hf_knxnetip_cemi_tpci = -1;
-static int hf_knxnetip_cemi_counter = -1;
-static int hf_knxnetip_cemi_npdu_length = -1;
-static int hf_knxnetip_cemi_tpdu_length = -1;
-static int hf_knxnetip_cemi_apci = -1;
-static int hf_knxnetip_cemi_data = -1;
-static int hf_knxnetip_cemi_numberofslots = -1;
-static int hf_knxnetip_cemi_iot = -1;
-static int hf_knxnetip_cemi_oi = -1;
-static int hf_knxnetip_cemi_six = -1;
-static int hf_knxnetip_cemi_pid = -1;
-static int hf_knxnetip_cemi_reserved = -1;
-static int hf_knxnetip_cemi_noe = -1;
-static int hf_knxnetip_cemi_error = -1;
-static int hf_knxnetip_cemi_return = -1;
-static int hf_knxnetip_cemi_numberofelements = -1;
-static int hf_knxnetip_cemi_apci_memory_number = -1;
-static int hf_knxnetip_cemi_rf_lfn = -1;
-static int hf_knxnetip_cemi_type_bibat_block = -1;
-static int hf_knxnetip_cemi_type_rf_multi_fastack = -1;
-static int hf_knxnetip_cemi_type_rf_multi_freq = -1;
-static int hf_knxnetip_cemi_type_rf_multi_channel = -1;
-static int hf_knxnetip_cemi_type_rf_multi_recep_freq = -1;
-static int hf_knxnetip_cemi_rf_sn = -1;
-static int hf_knxnetip_cemi_type_preamble_length = -1;
-static int hf_knxnetip_cemi_type_postamble_length = -1;
-static int hf_knxnetip_cemi_subfunction = -1;
-static int hf_knxnetip_cemi_manuspecificdata = -1;
-static int hf_knxnetip_cemi_apci_mem_address = -1;
-static int hf_knxnetip_cemi_channel = -1;
-static int hf_knxnetip_cemi_apci_key = -1;
-static int hf_knxnetip_cemi_apci_level = -1;
-static int hf_knxnetip_cemi_apci_object = -1;
-static int hf_knxnetip_cemi_apci_propid = -1;
-
-
-/*FLAGS
-DIB Device Status Flags*/
-static int hf_knxnetip_dib_status = -1;
-static int hf_knxnetip_dib_status_flag_reserved = -1;
-static int hf_knxnetip_dib_status_flag_program = -1;
-static const int *dib_device_status_flags[] = {
- &hf_knxnetip_dib_status_flag_reserved,
- &hf_knxnetip_dib_status_flag_program,
- NULL
-};
-/*DIB IP Capabilities Flags*/
-static int hf_knxnetip_dib_ipcapa = -1;
-static int hf_knxnetip_dib_ipcapa_flag_bootip = -1;
-static int hf_knxnetip_dib_ipcapa_flag_dhcp = -1;
-static int hf_knxnetip_dib_ipcapa_flag_autoip = -1;
-static int hf_knxnetip_dib_ipcapa_flag_reserved = -1;
-static const int *dib_ipcapabilities_flags[] = {
- &hf_knxnetip_dib_ipcapa_flag_bootip,
- &hf_knxnetip_dib_ipcapa_flag_dhcp,
- &hf_knxnetip_dib_ipcapa_flag_autoip,
- &hf_knxnetip_dib_ipcapa_flag_reserved,
- NULL
-};
-/*Device State*/
-static int hf_knxnetip_devicestate = -1;
-static int hf_knxnetip_devicestate_reserved = -1;
-static int hf_knxnetip_devicestate_knx = -1;
-static int hf_knxnetip_devicestate_ip = -1;
-static const int *devicestate_flags[] = {
- &hf_knxnetip_devicestate_knx,
- &hf_knxnetip_devicestate_ip,
- &hf_knxnetip_devicestate_reserved,
- NULL
-};
-/*cEMI FLAGS
-controlfield 1*/
-static int hf_knxnetip_cemi_controlfield1 = -1;
-static int hf_knxnetip_cemi_flag_frametype = -1;
-static int hf_knxnetip_cemi_flag_repeat = -1;
-static int hf_knxnetip_cemi_flag_sb = -1;
-static int hf_knxnetip_cemi_flag_priority = -1;
-static int hf_knxnetip_cemi_flag_ack = -1;
-static int hf_knxnetip_cemi_flag_confirm = -1;
-static const int *cemi_control1_flags[] = {
- &hf_knxnetip_cemi_flag_frametype,
- &hf_knxnetip_cemi_flag_repeat,
- &hf_knxnetip_cemi_flag_sb,
- &hf_knxnetip_cemi_flag_priority,
- &hf_knxnetip_cemi_flag_ack,
- &hf_knxnetip_cemi_flag_confirm,
- NULL
-};
-/*controlfield 2*/
-static int hf_knxnetip_cemi_controlfield2 = -1;
-static int hf_knxnetip_flag_destaddress = -1;
-static int hf_knxnetip_flag_hop = -1;
-static int hf_knxnetip_flag_eff = -1;
-static const int *cemi_control2_flags[] = {
- &hf_knxnetip_flag_destaddress,
- &hf_knxnetip_flag_hop,
- &hf_knxnetip_flag_eff,
- NULL
-};
-
-static int hf_knxnetip_cemi_type_rf_info = -1;
-static int hf_knxnetip_cemi_type_rf_reserved = -1;
-static int hf_knxnetip_cemi_type_rf_mesure = -1;
-static int hf_knxnetip_cemi_type_rf_mesure_re = -1;
-static int hf_knxnetip_cemi_type_rf_battery = -1;
-static int hf_knxnetip_cemi_type_rf_bidirekt = -1;
-static const int *cemi_rf_info[] = {
- &hf_knxnetip_cemi_type_rf_reserved,
- &hf_knxnetip_cemi_type_rf_mesure,
- &hf_knxnetip_cemi_type_rf_mesure_re,
- &hf_knxnetip_cemi_type_rf_battery,
- &hf_knxnetip_cemi_type_rf_bidirekt,
- NULL
-};
-
-static int hf_knxnetip_cemi_type_bus = -1;
-static int hf_knxnetip_cemi_type_bus_flag_f = -1;
-static int hf_knxnetip_cemi_type_bus_flag_b = -1;
-static int hf_knxnetip_cemi_type_bus_flag_p = -1;
-static int hf_knxnetip_cemi_type_bus_flag_d = -1;
-static int hf_knxnetip_cemi_type_bus_flag_l = -1;
-static int hf_knxnetip_cemi_type_bus_flag_sss = -1;
-static const int *cemi_bus_flags[] = {
- &hf_knxnetip_cemi_type_bus_flag_f,
- &hf_knxnetip_cemi_type_bus_flag_b,
- &hf_knxnetip_cemi_type_bus_flag_p,
- &hf_knxnetip_cemi_type_bus_flag_d,
- &hf_knxnetip_cemi_type_bus_flag_l,
- &hf_knxnetip_cemi_type_bus_flag_sss,
- NULL
-};
-
-static int hf_knxnetip_cemi_type_fastack = -1;
-static int hf_knxnetip_cemi_type_fastack_crc = -1;
-static int hf_knxnetip_cemi_type_fastack_error = -1;
-static int hf_knxnetip_cemi_type_fastack_received = -1;
-static int hf_knxnetip_cemi_type_fastack_info = -1;
-static const int *cemi_fastack_flags[] = {
- &hf_knxnetip_cemi_type_fastack_crc,
- &hf_knxnetip_cemi_type_fastack_error,
- &hf_knxnetip_cemi_type_fastack_received,
- &hf_knxnetip_cemi_type_fastack_info,
- NULL
-};
-
-
-static const value_string knxnetip_service_identifier[] = {
- { SEARCH_REQ, "SEARCH_REQUEST" },
- { SEARCH_RES, "SEARCH_RESPONSE" },
- { DESCRIPTION_REQ, "DESCRIPTION_REQUEST" },
- { DESCRIPTION_RES, "DESCRIPTION_RESPONSE" },
- { CONNECT_REQ, "CONNECT_REQUEST" },
- { CONNECT_RES, "CONNECT_RESPONSE" },
- { CONNECTIONSTATE_REQ, "CONNECTIONSTATE_REQUEST" },
- { CONNECTIONSTATE_RES, "CONNECTIONSTATE_RESPONSE" },
- { DISCONNECT_REQ, "DISCONNECT_REQUEST" },
- { DISCONNECT_RES, "DISCONNECT_RESPONSE" },
- { DEVICE_CONFIGURATION_REQ, "DEVICE_CONFIGURATION_REQUEST" },
- { DEVICE_CONFIGURATION_ACK, "DEVICE_CONFIGURATION_ACK" },
- { TUNNELLING_REQ, "TUNNELLING_REQUEST" },
- { TUNNELLING_ACK, "TUNNELING_ACK" },
- { ROUTING_INDICATION, "ROUTING_INDICATION" },
- { ROUTING_LOST, "ROUTING_LOST_MESSAGE" },
- { ROUTING_BUSY, "ROUTING_BUSY" },
- { REMOTE_DIAG_REQ, "REMOTE_DIAGNOSTIC_REQUEST" },
- { REMOTE_DIAG_RES, "REMOTE_DIAGNOSTIC_RESPONSE" },
- { REMOTE_BASIC_CONF_REQ, "REMOTE_BASIC_CONFIGURATION_REQUEST" },
- { REMOTE_RESET_REQ, "REMOTE_RESET_REQUEST" },
- { 0, NULL }
-};
-
-
-static const value_string knxnetip_service_types[] = {
- { 0x02, "KNXnet/IP Core" },
- { 0x03, "KNXnet/IP Device Management" },
- { 0x04, "KNXnet/IP Tunneling" },
- { 0x05, "KNXnet/IP Routing" },
- { 0x06, "KNXnet/IP Remote Logging" },
- { 0x07, "KNXnet/IP Remote Configuration and Diagnosis" },
- { 0x08, "KNXnet/IP Object Server" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_connection_types[] = {
- { 0x03, "DEVICE_MGMT_CONNECTION" },
- { 0x04, "TUNNEL_CONNECTION" },
- { 0x06, "REMLOG_CONNECTION" },
- { 0x07, "REMCONF_CONNECTION" },
- { 0x08, "OBJSVR_CONNECTION" },
- { 0, NULL }
-};
-
-
-static const value_string knxnetip_connect_response_status_codes[] = {
- { 0x00, "E_NO_ERROR - The connection was established successfully" },
- { 0x22, "E_CONNECTION_TYPE - The KNXnet/IP server device does not support the requested connection type" },
- { 0x23, "E_CONNECTION_OPTION - The KNXnet/IP server device does not support one or more requested connection options" },
- { 0x24, "E_NO_MORE_CONNECTIONS - The KNXnet/IP server device could not accept the new data connection (busy)" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_connectionstate_response_status_codes[] = {
- { 0x00, "E_NO_ERROR - The connection state is normal" },
- { 0x21, "E_CONNECTION_ID - The KNXnet/IP server device could not find an active data connection with the specified ID" },
- { 0x26, "E_DATA_CONNECTION - The KNXnet/IP server device detected an error concerning the data connection with the specified ID" },
- { 0x27, "E_KNX_CONNECTION - The KNXnet/IP server device detected an error concerning the EIB bus / KNX subsystem connection with the specified ID" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_tunneling_error_codes[] = {
- { 0x00, "E_NO_ERROR - The message was received successfully" },
- { 0x29, "E_TUNNELLING_LAYER - The KNXnet/IP server device does not support the requested tunnelling layer" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_device_configuration_ack_status_codes[] = {
- { 0x00, "E_NO_ERROR - The message was received successfully" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_dib_description_type_codes[] = {
- { DIB_DEVICE_INFO, "DEVICE_INFO" },
- { DIB_SUPP_SVC, "SUPP_SVC_FAMILIES" },
- { DIB_IP_CONF, "IP_CONFIG" },
- { DIB_IP_CURRENT, "IP_CUR_CONFIG" },
- { DIB_KNX_ADDRESS, "KNX_ADDRESSES" },
- { DIB_MFR_DATA, "MFR_DATA" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_dib_medium_codes[] = {
- { 0x01, "reserved" },
- { 0x02, "KNX TP" },
- { 0x04, "KNX PL110" },
- { 0x08, "reserved" },
- { 0x10, "KNX RF" },
- { 0x20, "KNX IP" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_host_protocol_codes[] = {
- { 0x01, "IPV4_UDP" },
- { 0x02, "IPV4_TCP" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_ip_assignment_method[] = {
- { 0x01, "manuell" },
- { 0x02, "BootP" },
- { 0x04, "DHCP" },
- { 0x08, "AutoIP" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_knxlayer_values[] = {
- { 0x02, "TUNNEL_LINKLAYER" },
- { 0x04, "TUNNEL_RAW"},
- { 0x80, "TUNNEL_BUSMONITOR"},
- { 0, NULL}
-};
-
-static const value_string knxnetip_selector_types[] = {
- { 0x01, "PrgMode Selector" },
- { 0x02, "MAC Selector" },
- { 0, NULL }
-};
-
-static const value_string knxnetip_reset_codes[] = {
- { 0x01, "Restart" },
- { 0x02, "Master Reset" },
- { 0, NULL }
-};
-
-/*for CEMI*/
-static const value_string cemi_messagecodes[] = {
- { RAW_REQ, "L_Raw.req"},
- { DATA_REQ, "L_Data.req"},
- { POLL_DATA_REQ, "L_Poll_Data.req"},
- { POLL_DATA_CON, "L_Poll_Data.con"},
- { DATA_IND, "L_Data.ind"},
- { BUSMON_IND, "L_Busmon.ind"},
- { RAW_IND, "L_Raw.ind"},
- { DATA_CON, "L_Data.con"},
- { RAW_CON, "L_Raw.con"},
- { DATA_CONNEC_REQ, "T_Data_Connected.req"},
- { DATA_INDV_REQ, "T_Data_Individual.req"},
- { DATA_CONNEC_IND, "T_Data_Connected.ind"},
- { DATA_INDV_IND, "T_Data_Individual.ind"},
- { RESET_IND, "M_Reset.ind"},
- { RESET_REQ, "M_Reset.req"},
- { PROPWRITE_CON, "M_PropWrite.con"},
- { PROPWRITE_REQ, "M_PropWrite.req"},
- { PROPINFO_IND, "M_PropInfo.ind"},
- { FUNCPROPCOM_REQ, "M_FuncPropCommand.req"},
- { FUNCPROPSTATREAD_REQ, "M_FuncPropStateRead.req"},
- { FUNCPROPCOM_CON, "M_FuncPropCommand/StateRead.con"},
- { PROPREAD_CON, "M_PropRead.con"},
- { PROPREAD_REQ, "M_PropRead.req"},
- { 0, NULL }
-};
-
-static const value_string cemi_add_type_id[] = {
- { 0x00, "reserved" },
- { PL_INFO, "PL Info"},
- { RF_INFO, "RF Info"},
- { BUSMON_INFO, "Busmonitor Info"},
- { TIME_REL, "relative timestamp"},
- { TIME_DELAY, "time delay until send"},
- { EXEND_TIME, "extended relative timestamp"},
- { BIBAT_INFO, "BiBat information"},
- { RF_MULTI, "RF Multi information"},
- { PREAMBEL, "Preamble and postamble"},
- { RF_FAST_ACK, "RF Fast Ack information"},
- { MANU_DATA, "Manufacturer specific data"},
- { RESER, "reserved"},
- { 0, NULL}
-};
-
-static const value_string cemi_tpci_vals[] = {
- { 0x0, "UDT (Unnumbered Data Packet)" },
- { 0x2, "UCD (Unnumbered)"},
- { 0x1, "NDT (Numbered Data Packet)"},
- { 0x3, "NCD (Numbered Control Data)"},
- { 0, NULL}
-};
-
-static const value_string cemi_apci_codes[] = {
- { 0x000, "A_GroupValue_Read" },
- { 0x001, "A_GroupValue_Response"},
- { 0x002, "A_GroupValue_Write"},
- { 0x0C0, "A_IndividualAddress_Write"},
- { 0x100, "A_IndividualAddress_Read"},
- { 0x140, "A_IndividualAddress_Response"},
- { 0x006, "A_ADC_Read"},
- { 0x1C0, "A_ADC_Response"},
- { 0x1C4, "A_SystemNetworkParameter_Read"},
- { 0x1C9, "A_SystemNetworkParameter_Response"},
- { 0x1CA, "A_SystemNetworkParameter_Write"},
- { 0x020, "A_Memory_Read"},
- { 0x024, "A_Memory_Response"},
- { 0x028, "A_Memory_Write"},
- { 0x2C0, "A_UserMemory_Read"},
- { 0x2C1, "A_UserMemory_Response"},
- { 0x2C2, "A_UserMemory_Write"},
- { 0x2C5, "A_UserManufacturerInfo_Read"},
- { 0x2C6, "A_UserManufacturerInfo_Response"},
- { 0x2C7, "A_FunctionPropertyCommand"},
- { 0x2C8, "A_FunctionPropertyState_Read"},
- { 0x2C9, "A_FunctionPropertyState_Response"},
- { 0x300, "A_DeviceDescriptor_Read"},
- { 0x340, "A_DeviceDescriptor_Response"},
- { 0x380, "A_Restart"},
- { 0x3D1, "A_Authorize_Request"},
- { 0x3D2, "A_Authorize_Response"},
- { 0x3D3, "A_Key_Write"},
- { 0x3D4, "A_Key_Response"},
- { 0x3D5, "A_PropertyValue_Read"},
- { 0x3D6, "A_PropertyValue_Response"},
- { 0x3D7, "A_PropertyValue_Write"},
- { 0x3D8, "A_PropertyDescription_Read"},
- { 0x3D9, "A_PropertyDescription_Response"},
- { 0x3DA, "A_NetworkParameter_Read"},
- { 0x3DB, "A_NetworkParameter_Response"},
- { 0x3DC, "A_IndividualAddressSerialNumber_Read"},
- { 0x3DD, "A_IndividualAddressSerialNumber_Response"},
- { 0x3DF, "A_IndividualAddressSerialNumber_Write"},
- { 0x3E0, "A_DomainAddress_Write"},
- { 0x3E1, "A_DomainAddress_Read"},
- { 0x3E2, "A_DomainAddress_Response"},
- { 0x3E3, "A_DomainAddressSelective_Read"},
- { 0x3E4, "A_NetworkParameter_Write"},
- { 0x3E5, "A_Link_Read"},
- { 0x3E6, "A_Link_Response"},
- { 0x3E7, "A_Link_Write"},
- { 0x3E8, "A_GroupPropValue_Read"},
- { 0x3E9, "A_GroupPropValue_Response"},
- { 0x3EA, "A_GroupPropValue_Write"},
- { 0x3EB, "A_GroupPropValue_InfoReport"},
- { 0x3EC, "A_DomainAddressSerialNumber_Read"},
- { 0x3ED, "A_DomainAddressSerialNumber_Response"},
- { 0x3EE, "A_DomainAddressSerialNumber_Write"},
- { 0x3F0, "A_FileStream_InforReport"},
- { 0, NULL}
-};
-
-static const value_string cemi_propertyid[] = {
- { 1, "PID_OBJECT_TYPE" },
- { 8, "PID_SERVICE_CONTROL" },
- { 9, "PID_FIRMWARE_REVISION" },
- { 11, "PID_SERIAL_NUMBER" },
- { 12, "PID_MANUFACTURER_ID" },
- { 14, "PID_DEVICE_CONTROL" },
- { 19, "PID_MANUFACTURE_DATA" },
- { 51, "PID_ROUTING_COUNT" },
- { 52, "PID_MAX_RETRY_COUNT " },
- { 53, "PID_ERROR_FLAGS" },
- { 54, "PID_PROGMODE" },
- { 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" },
- { 70, "PID_DOMAIN_ADDR"},
- { 71, "PID_IO_LIST"},
- { 0, NULL }
-};
-
-static const value_string cemi_error_codes[] = {
- { 0x00, "Unspecified Error"},
- { 0x01, "Out of range"},
- { 0x02, "Out of maxrange"},
- { 0x03, "Out of minrange"},
- { 0x04, "Memory Error"},
- { 0x05, "Read only"},
- { 0x06, "Illegal command"},
- { 0x07, "Void DP"},
- { 0x08, "Type conflict"},
- { 0x09, "Prop. Index range error"},
- { 0x0A, "Value temporarily not writeable"},
- { 0, NULL }
-};
-
-static const value_string cemi_bibat_ctrl[] = {
- { 0x0, "asynchr. RF frame"},
- { 0x1, "Fast_ACK"},
- { 0x4, "synchronous L_Data frames"},
- { 0x5, "Sync frame"},
- { 0x6, "Help Call"},
- { 0x7, "Help Call Response"},
- { 0, NULL }
-};
-
-static gint ett_knxnetip = -1;
-static gint ett_knxnetip_header = -1;
-static gint ett_knxnetip_body = -1;
-static gint ett_knxnetip_hpai = -1;
-static gint ett_knxnetip_dib = -1;
-static gint ett_knxnetip_dib_projectid = -1;
-static gint ett_knxnetip_dib_service = -1;
-static gint ett_knxnetip_cri = -1;
-static gint ett_knxnetip_crd = -1;
-static gint ett_knxnetip_dib_status = -1;
-static gint ett_knxnetip_dib_ipcapa = -1;
-static gint ett_knxnetip_devicestate = -1;
-static gint ett_knxnetip_cemi = -1;
-static gint ett_knxnetip_cemi_additional = -1;
-static gint ett_knxnetip_cemi_additional_item = -1;
-static gint ett_knxnetip_cemi_control1 = -1;
-static gint ett_knxnetip_cemi_control2 = -1;
-static gint ett_knxnetip_cemi_rf_info = -1;
-static gint ett_knxnetip_cemi_bus_info = -1;
-static gint ett_knxnetip_cemi_fastack = -1;
-
-static expert_field ei_knxnetip_length = EI_INIT;
-
-static void dissect_hpai(tvbuff_t *tvb, guint32 *offset, proto_tree *insert_tree, const char *append_text) {
-
- proto_item *hpai_item;
- proto_tree *hpai_tree;
-
- hpai_item = proto_tree_add_item( insert_tree, hf_knxnetip_hpai, tvb, *offset, 8, ENC_NA );
- hpai_tree = proto_item_add_subtree(hpai_item, ett_knxnetip_hpai);
- proto_item_append_text(hpai_item, "%s", append_text);
- proto_tree_add_item(hpai_tree, hf_knxnetip_hpai_structure_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(hpai_tree, hf_knxnetip_hpai_host_protocol, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(hpai_tree, hf_knxnetip_hpai_ip_address, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(hpai_tree, hf_knxnetip_hpai_port, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
-
-}
-
-static gboolean dissect_dib(tvbuff_t *tvb, guint32 *offset, proto_tree *insert_tree) {
-
- proto_item *dib_item = NULL;
- proto_item *projectid_item = NULL;
- proto_item *service_item = NULL;
-
- proto_tree *dib_tree = NULL;
- proto_tree *projectid_tree = NULL;
- proto_tree *service_tree = NULL;
-
- guint8 i;
- guint8 dib_type;
- guint8 length;
- guint16 knx_address;
- guint16 install_id;
-
- length = tvb_get_guint8(tvb, *offset);
- dib_item = proto_tree_add_item(insert_tree, hf_knxnetip_dib, tvb, *offset, length, ENC_NA);
- dib_tree = proto_item_add_subtree(dib_item, ett_knxnetip_dib);
- proto_tree_add_item(dib_tree, hf_knxnetip_structure_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
- dib_type = tvb_get_guint8(tvb, *offset);
- proto_item_append_text(dib_item, ": %s", val_to_str_const(dib_type, knxnetip_dib_description_type_codes, "Unknown Type"));
- *offset+=1;
-
- switch (dib_type){
-
- case(DIB_DEVICE_INFO):
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_medium, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_bitmask(dib_tree, tvb, *offset, hf_knxnetip_dib_status, ett_knxnetip_dib_status, dib_device_status_flags, ENC_BIG_ENDIAN);
- *offset+=1;
- knx_address = tvb_get_ntohs(tvb, *offset);
- proto_tree_add_uint_format(dib_tree, hf_knxnetip_knxaddress, tvb, *offset, 2, knx_address, "KNX Address %d.%d.%d", ((knx_address & 0xF000)>>12),((knx_address & 0x0F00)>>8),(knx_address & 0xFF));
- *offset+=2;
- projectid_item = proto_tree_add_item(dib_tree, hf_knxnetip_dib_projectid, tvb, *offset, 2, ENC_BIG_ENDIAN);
- projectid_tree = proto_item_add_subtree(projectid_item, ett_knxnetip_dib_projectid);
- install_id = tvb_get_ntohs(tvb, *offset);
- proto_tree_add_uint_format(projectid_tree, hf_knxnetip_projectnumber, tvb, *offset, 2, install_id, "Project number %d", (install_id & 0xFFF0)>>4);
- proto_tree_add_uint_format(projectid_tree, hf_knxnetip_installnumber, tvb, *offset, 2, install_id, "Installation number %d", (install_id & 0xF));
- *offset+=2;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_serialnumber, tvb, *offset, 6, ENC_NA);
- *offset+=6;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_multicast_address, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_mac_address, tvb, *offset, 6, ENC_NA);
- *offset+=6;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_friendly, tvb, *offset, 30, ENC_ASCII|ENC_NA );
- *offset+=30;
- break;
-
- case(DIB_SUPP_SVC):
- if (length > 4) {
- length-=4;
- } else {
- return TRUE;
- }
-
- for (i = 0; i <= length; i+=2) {
- service_item = proto_tree_add_item(dib_tree, hf_knxnetip_dib_service, tvb, *offset, 1, ENC_BIG_ENDIAN);
- service_tree = proto_item_add_subtree(service_item, ett_knxnetip_dib_service);
- *offset+=1;
- proto_tree_add_item(service_tree, hf_knxnetip_dib_svc_version, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- }
- break;
-
- case(DIB_IP_CONF):
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_ipaddress, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_subnet, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_gateway, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_bitmask(dib_tree, tvb, *offset, hf_knxnetip_dib_ipcapa, ett_knxnetip_dib_ipcapa, dib_ipcapabilities_flags, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_ipassign, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- break;
-
- case(DIB_IP_CURRENT):
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_ipaddress, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_subnet, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_gateway, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_dhcp, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_ipassign, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(dib_tree, hf_knxnetip_reserved, tvb, *offset, 1, ENC_NA);
- *offset+=1;
- break;
-
- case(DIB_KNX_ADDRESS):
- if (length > 4) {
- length-=4;
- } else {
- return TRUE;
- }
-
- for (i = 0; i <= length; i+=2) {
- knx_address = tvb_get_ntohs(tvb, *offset);
- proto_tree_add_uint_format(dib_tree, hf_knxnetip_knxaddress, tvb, *offset, 2, knx_address, "KNX Address %d.%d.%d", ((knx_address & 0xF000)>>12),((knx_address & 0x0F00)>>8),(knx_address & 0xFF));
- *offset+=2;
- }
- break;
-
- case(DIB_MFR_DATA):
- if (length > 4) {
- length-=4;
- } else {
- return TRUE;
- }
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_manuid, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(dib_tree, hf_knxnetip_dib_manudata, tvb, *offset, length, ENC_ASCII|ENC_NA);
- *offset+=length;
- break;
- }
-
- return FALSE;
-}
-
-static guint dissect_cri(tvbuff_t *tvb, guint32 offset, proto_tree *insert_tree) {
-
- proto_item *cri_item = NULL;
- proto_tree *cri_tree = NULL;
-
- guint8 length;
-
- length = tvb_get_guint8(tvb ,offset);
- cri_item = proto_tree_add_item(insert_tree, hf_knxnetip_cri, tvb, offset, length, ENC_NA);
- cri_tree = proto_item_add_subtree(cri_item, ett_knxnetip_cri);
-
- proto_tree_add_item(cri_tree, hf_knxnetip_structure_length, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(cri_tree, hf_knxnetip_connection_type, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- if (tvb_get_guint8(tvb,offset-1)== KNX_TUNNEL_CONNECTION ){
- proto_tree_add_item(cri_tree, hf_knxnetip_knxlayer, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(cri_tree, hf_knxnetip_reserved, tvb, offset, 1, ENC_NA);
- offset+=1;
- }
- else if (length > 2) {
- proto_tree_add_item(cri_tree, hf_knxnetip_cri_protocol_data, tvb, offset, (length-2), ENC_NA);
- offset+=(length-2);
- }
- return offset;
-}
-
-static void dissect_crd(tvbuff_t *tvb, guint32 *offset, proto_tree *insert_tree) {
-
- proto_item *crd_item = NULL;
- proto_tree *crd_tree = NULL;
-
- guint8 length;
- guint16 knx_address;
-
- length = tvb_get_guint8(tvb, *offset);
- crd_item = proto_tree_add_item(insert_tree, hf_knxnetip_crd, tvb, *offset, length, ENC_NA);
- crd_tree = proto_item_add_subtree(crd_item, ett_knxnetip_crd);
-
- proto_tree_add_item(crd_tree, hf_knxnetip_structure_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(crd_tree, hf_knxnetip_connection_type, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (tvb_get_guint8(tvb, *offset-1) == KNX_TUNNEL_CONNECTION){
- knx_address = tvb_get_ntohs(tvb, *offset);
- proto_tree_add_uint_format(crd_tree, hf_knxnetip_knxaddress, tvb, *offset, 2, knx_address, "KNX Address %d.%d.%d", ((knx_address & 0xF000)>>12),((knx_address & 0x0F00)>>8),(knx_address & 0xFF));
- *offset+=2;
- }
- else if (length > 2) {
- proto_tree_add_item(crd_tree, hf_knxnetip_crd_protocol_data, tvb, *offset, (length-2), ENC_NA);
- *offset+=(length-2);
- }
-}
-
-static guint dissect_connection_header(tvbuff_t *tvb, guint32 offset, proto_tree *insert_tree, gboolean have_status) {
-
- proto_tree_add_item(insert_tree, hf_knxnetip_structure_length, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_communication_channel_id, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_counter, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- if (have_status == FALSE){
- proto_tree_add_item(insert_tree, hf_knxnetip_reserved, tvb, offset, 1, ENC_NA);
- offset+=1;
- }
-
- return offset;
-}
-
-static guint dissect_selector(tvbuff_t *tvb, guint32 offset, proto_tree *insert_tree){
-
- proto_tree_add_item(insert_tree, hf_knxnetip_structure_length, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_selector_type, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- if (tvb_get_guint8(tvb, offset-1)==0x02){
- proto_tree_add_item(insert_tree, hf_knxnetip_mac_address, tvb, offset, 6, ENC_NA);
- offset+=6;
- }
- return offset;
-}
-
-static void dissect_apci(tvbuff_t *tvb, guint32 *offset, proto_tree *insert_tree, gboolean tpdu){
-
- guint16 type;
- guint16 sub_type;
- guint8 length;
-
- length = tvb_get_guint8(tvb, *offset-1);
- if (tpdu == TRUE){
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_reserved, tvb, *offset, 1, ENC_BIG_ENDIAN);
- }
- else {
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_tpci, tvb, *offset, 1, ENC_BIG_ENDIAN);
- type = (tvb_get_guint8(tvb, *offset)&0xC0);
- if (type == 0x40 || type == 0xC0){
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_counter, tvb, *offset, 1, ENC_BIG_ENDIAN);
- }
- }
-
- if (length != 0) {
- type = (tvb_get_ntohs(tvb, *offset) & 0x03C0);
- switch (type){
- case(A_ADC_RED):
- case(A_ADC_RES):
- type = (tvb_get_ntohs(tvb, *offset) & 0x1FF);
- if (type == A_SYS_RED || type == A_SYS_RES || type == A_SYS_WRT || type == A_SYS_BROAD){
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 10, ENC_BIG_ENDIAN);
- }
- else {
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 4, ENC_BIG_ENDIAN);
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_channel, tvb, (*offset*8)+10, 6, ENC_BIG_ENDIAN);
- }
- *offset+=2;
- break;
- case(A_GROUPVALUE_RES):
- case(A_GROUPVALUE_WRT):
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 4, ENC_BIG_ENDIAN);
- if (length == 1){
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_data, tvb, (*offset*8)+10, 6, ENC_BIG_ENDIAN);
- }
- *offset+=2;
- break;
- case(A_MEM_RED):
- case(A_MEM_RES):
- case(A_MEM_WRT):
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 6, ENC_BIG_ENDIAN);
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci_memory_number, tvb, (*offset*8)+12, 4, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_apci_mem_address, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- break;
- case(COUPLER_SPECIFIC_SERVICE):
- sub_type = (tvb_get_ntohs(tvb, *offset) & 0x3FF);
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 10, ENC_BIG_ENDIAN);
- *offset+=2;
- switch(sub_type){
- case(A_AUTHORIZE_REQ):
- case(A_KEY_WRT):
- proto_tree_add_item(insert_tree, hf_knxnetip_reserved, tvb, *offset, 1, ENC_NA);
- *offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_apci_key, tvb, *offset, 4, ENC_NA);
- *offset+=4;
- break;
- case(A_AUTHORIZE_RES):
- case(A_KEY_RES):
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_apci_level, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- break;
- case(A_PROPVALUE_RED):
- case(A_PROPVALUE_RES):
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_apci_object, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_apci_propid, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_noe, tvb, *offset, 1, ENC_BIG_ENDIAN);
- proto_tree_add_item(insert_tree, hf_knxnetip_cemi_six, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- }
- break;
- default:
- proto_tree_add_bits_item(insert_tree, hf_knxnetip_cemi_apci, tvb, (*offset*8)+6, 10, ENC_BIG_ENDIAN);
- *offset+=2;
- }
-
- if (length >= 1){
- length-=1;
- }
-
- if (length >= 1 && (tvb_reported_length_remaining(tvb, *offset) > 0)){
- proto_tree_add_item(insert_tree, hf_knxnetip_data, tvb, *offset, -1, ENC_NA);
- *offset+=length;
- }
-
- }
- else {
- *offset+=1;
- }
-
-}
-
-
-static gboolean dissect_cemi(tvbuff_t *tvb, guint32 *offset, proto_tree *insert_tree, packet_info *pinfo){
-
- proto_item *cemi_item = NULL;
- proto_item *additional_item = NULL;
- proto_item *additional_info = NULL;
-
- proto_tree *cemi_tree = NULL;
- proto_tree *additional_tree = NULL;
- proto_tree *additional_subtree = NULL;
-
- guint8 i;
- guint8 messagecode;
- guint8 length;
- guint8 type_id;
- guint8 noe;
- guint8 num_of_octets;
- guint16 knx_address;
- guint16 six;
-
- cemi_item = proto_tree_add_item(insert_tree, hf_knxnetip_cemi, tvb, *offset, -1, ENC_NA);
- cemi_tree = proto_item_add_subtree(cemi_item, ett_knxnetip_cemi);
- messagecode = tvb_get_guint8(tvb, *offset);
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_mc, tvb, *offset, 1, ENC_BIG_ENDIAN);
- col_append_fstr(pinfo->cinfo, COL_INFO, "| cEMI: %s", val_to_str(messagecode, cemi_messagecodes, "Unknown MC:0x%0x"));
- *offset+=1;
- /*check if M_ Message*/
- if ((messagecode & 0xF0) < 0xF0){
- length = tvb_get_guint8(tvb, *offset);
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_addlength, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
-
- if (length != 0){
-
- additional_info = proto_tree_add_item(cemi_tree, hf_knxnetip_additional, tvb, *offset, length, ENC_NA);
- additional_tree = proto_item_add_subtree(additional_info, ett_knxnetip_cemi_additional);
- do {
- type_id = tvb_get_guint8(tvb, *offset);
- additional_item = proto_tree_add_item(additional_tree, hf_knxnetip_cemi_typid, tvb, *offset, 1, ENC_BIG_ENDIAN);
- additional_subtree = proto_item_add_subtree(additional_item, ett_knxnetip_cemi_additional_item);
- *offset+=1;
- proto_tree_add_item(additional_item, hf_knxnetip_cemi_additemlength, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 2){
- length-=2;
- }
- else{
- return TRUE;
- }
-
- switch(type_id){
- case(PL_INFO):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_pl, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- if (length >= 2){
- length-=2;
- }
- else{
- return TRUE;
- }
- break;
- case(RF_INFO):
- proto_tree_add_bitmask(additional_subtree, tvb, *offset, hf_knxnetip_cemi_type_rf_info, ett_knxnetip_cemi_rf_info, cemi_rf_info, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_rf_sn, tvb, *offset, 6, ENC_BIG_ENDIAN);
- *offset+=6;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_rf_lfn, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 8){
- length-=8;
- }
- else{
- return TRUE;
- }
- break;
- case(BUSMON_INFO):
- proto_tree_add_bitmask(additional_subtree, tvb, *offset, hf_knxnetip_cemi_type_bus, ett_knxnetip_cemi_bus_info, cemi_bus_flags, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 1){
- length-=1;
- }
- else{
- return TRUE;
- }
- break;
- case(TIME_REL):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_relt, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- if (length >= 2){
- length-=2;
- }
- else{
- return TRUE;
- }
- break;
- case(TIME_DELAY):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_delay, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- if (length >= 4){
- length-=4;
- }
- else{
- return TRUE;
- }
- break;
- case(EXEND_TIME):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_exttime, tvb, *offset, 4, ENC_BIG_ENDIAN);
- *offset+=4;
- if (length >= 4){
- length-=4;
- }
- else{
- return TRUE;
- }
- break;
- case(BIBAT_INFO):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_bibat, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_bibat_block, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 2){
- length-=2;
- }
- else{
- return TRUE;
- }
- break;
- case(RF_MULTI):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_rf_multi_freq, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_rf_multi_channel, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_rf_multi_fastack, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_rf_multi_recep_freq, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 4){
- length-=4;
- }
- else{
- return TRUE;
- }
- break;
- case(PREAMBEL):
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_preamble_length, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_type_postamble_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (length >= 3){
- length-=3;
- }
- else{
- return TRUE;
- }
- break;
- case(RF_FAST_ACK):
- num_of_octets = tvb_get_guint8(tvb, *offset-1);
- for(i=0; i<num_of_octets; i++) {
- proto_tree_add_bitmask(additional_subtree, tvb, *offset, hf_knxnetip_cemi_type_fastack, ett_knxnetip_cemi_fastack, cemi_fastack_flags, ENC_BIG_ENDIAN);
- *offset+=2;
- if (length >= 2){
- length-=2;
- }
- else{
- return TRUE;
- }
- }
- break;
- case(MANU_DATA):
- num_of_octets = tvb_get_guint8(tvb, *offset-1);
- proto_tree_add_item(additional_subtree, hf_knxnetip_dib_manuid, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_subfunction, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(additional_subtree, hf_knxnetip_cemi_manuspecificdata, tvb, *offset, (num_of_octets-3), ENC_NA);
- *offset+=(num_of_octets-3);
- if (length >= num_of_octets){
- length-=num_of_octets;
- }
- else{
- return TRUE;
- }
- break;
- default:
- proto_tree_add_item(additional_subtree, hf_knxnetip_unknown, tvb, *offset, -1, ENC_NA);
- return *offset;
- }
- } while (length > 0);
- }
- }
- switch (messagecode){
- case(DATA_REQ):
- case(DATA_CON):
- case(DATA_IND):
- case(POLL_DATA_REQ):
- case(POLL_DATA_CON):
- proto_tree_add_bitmask(cemi_tree, tvb, *offset, hf_knxnetip_cemi_controlfield1, ett_knxnetip_cemi_control1, cemi_control1_flags, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_bitmask(cemi_tree, tvb, *offset, hf_knxnetip_cemi_controlfield2, ett_knxnetip_cemi_control2, cemi_control2_flags, ENC_BIG_ENDIAN);
- *offset+=1;
- knx_address = tvb_get_ntohs(tvb, *offset);
- proto_tree_add_uint_format(cemi_tree, hf_knxnetip_cemi_sourceaddress, tvb, *offset, 2, knx_address, "Source Address %d.%d.%d", ((knx_address & 0xF000)>>12),((knx_address & 0x0F00)>>8),(knx_address & 0xFF));
- *offset+=2;
- knx_address = tvb_get_ntohs(tvb, *offset);
- if ((tvb_get_guint8(tvb, *offset-3) & 0x80) == GROUPADD){
- proto_tree_add_uint_format(cemi_tree, hf_knxnetip_cemi_destaddress, tvb, *offset, 2, knx_address, "Destination Address %d/%d/%d or %d/%d", ((knx_address & 0x7800)>>11),((knx_address & 0x0700)>>8),(knx_address & 0xFF), ((knx_address & 0x7800)>>11),(knx_address & 0x7FF));
- }
- else {
- proto_tree_add_uint_format(cemi_tree, hf_knxnetip_cemi_destaddress, tvb, *offset, 2, knx_address, "Destination Address %d.%d.%d", ((knx_address & 0xF000)>>12),((knx_address & 0x0F00)>>8),(knx_address & 0xFF));
- }
- *offset+=2;
- if (messagecode == POLL_DATA_REQ){
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_numberofslots, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- }
- else if (messagecode == POLL_DATA_CON){
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_numberofslots, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(cemi_tree, hf_knxnetip_polldata, tvb, *offset, -1, ENC_NA);
- }
- else {
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_npdu_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- dissect_apci(tvb, offset, cemi_tree, FALSE);
- }
- break;
- case(RAW_REQ):
- case(RAW_CON):
- case(RAW_IND):
- case(BUSMON_IND):
- proto_tree_add_item(cemi_tree, hf_knxnetip_raw, tvb, *offset, -1, ENC_NA);
- break;
- case(DATA_INDV_IND):
- case(DATA_INDV_REQ):
- case(DATA_CONNEC_IND):
- case(DATA_CONNEC_REQ):
- proto_tree_add_item(cemi_tree, hf_knxnetip_reserved, tvb, *offset, 6, ENC_NA);
- *offset+=6;
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_tpdu_length, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- dissect_apci(tvb, offset, cemi_tree, TRUE);
- break;
- case(PROPREAD_REQ):
- case(PROPREAD_CON):
- case(PROPWRITE_REQ):
- case(PROPWRITE_CON):
- case(PROPINFO_IND):
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_iot, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_oi, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_pid, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- noe = ((tvb_get_guint8(tvb, *offset)& 0xF0)>>4);
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_noe, tvb, *offset, 1, ENC_BIG_ENDIAN);
- six = tvb_get_bits16(tvb, (*offset*8+4), 12, ENC_BIG_ENDIAN);
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_six, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- if (messagecode == PROPREAD_REQ || (messagecode == PROPREAD_CON && noe > 0)){
- break;
- }
- else if (noe == 0){
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_error, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- }
- else if (noe == 1 && six == 0){
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_numberofelements, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- }
- else {
- proto_tree_add_item(cemi_tree, hf_knxnetip_data, tvb, *offset, -1, ENC_NA);
- }
- break;
- case(FUNCPROPCOM_REQ):
- case(FUNCPROPSTATREAD_REQ):
- case(FUNCPROPCOM_CON):
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_iot, tvb, *offset, 2, ENC_BIG_ENDIAN);
- *offset+=2;
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_oi, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_pid, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- if (messagecode == FUNCPROPCOM_CON){
- proto_tree_add_item(cemi_tree, hf_knxnetip_cemi_return, tvb, *offset, 1, ENC_BIG_ENDIAN);
- *offset+=1;
- }
- proto_tree_add_item(cemi_tree, hf_knxnetip_data, tvb, *offset, -1, ENC_NA);
- break;
- case(RESET_REQ):
- case(RESET_IND):
- break;
- default:
- proto_tree_add_item(cemi_tree, hf_knxnetip_data, tvb, *offset, -1, ENC_NA);
- }
- return FALSE;
-}
-
-
-
-static void dissect_knxnetip (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
- proto_item *knx_item = NULL;
-
- proto_tree *knx_tree = NULL;
- proto_tree *header_tree = NULL;
- proto_tree *body_tree = NULL;
-
- guint offset = 0;
- guint16 service_type = 0;
- gboolean err = FALSE;
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "KNXnetIP");
- col_clear(pinfo->cinfo,COL_INFO);
-
- knx_item = proto_tree_add_item(tree, proto_knxnetip, tvb, 0, -1, ENC_NA);
- knx_tree = proto_item_add_subtree(knx_item, ett_knxnetip);
-
- /* HEADER*/
- header_tree = proto_tree_add_subtree(knx_tree, tvb, offset, 6, ett_knxnetip_header, NULL, "Header");
- proto_tree_add_item(header_tree, hf_knxnetip_headerlength, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(header_tree, hf_knxnetip_version, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- service_type = tvb_get_ntohs(tvb, offset);
- proto_tree_add_item(header_tree, hf_knxnetip_servicetype, tvb, offset, 2, ENC_BIG_ENDIAN);
- col_add_fstr(pinfo->cinfo, COL_INFO, "%s %d > %d", val_to_str(service_type, knxnetip_service_identifier, "Unknown Identifier:0x%02x"), pinfo->srcport, pinfo->destport);
- offset+=2;
- proto_tree_add_item(header_tree, hf_knxnetip_totallength, tvb, offset, 2, ENC_BIG_ENDIAN);
- offset+=2;
- /* BODY */
- body_tree = proto_tree_add_subtree(knx_tree, tvb, offset, -1, ett_knxnetip_body, NULL, "Body");
-
- switch(service_type) {
-
- case(SEARCH_REQ):
- dissect_hpai(tvb, &offset, body_tree, ": Discovery endpoint");
- break;
- case(SEARCH_RES):
- dissect_hpai(tvb, &offset, body_tree, ": Control endpoint");
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- break;
- case(DESCRIPTION_REQ):
- dissect_hpai(tvb, &offset, body_tree, ": Control endpoint");
- break;
- case(DESCRIPTION_RES):
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- if (tvb_reported_length_remaining(tvb, offset) != 0){
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- }
- }
- break;
- case(CONNECT_REQ):
- dissect_hpai(tvb, &offset, body_tree, ": Discovery endpoint");
- dissect_hpai(tvb, &offset, body_tree, ": Data endpoint");
- offset = dissect_cri(tvb, offset, body_tree);
- break;
- case(CONNECT_RES):
- proto_tree_add_item(body_tree, hf_knxnetip_communication_channel_id, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_connect_status, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- dissect_hpai(tvb, &offset, body_tree, ": Data endpoint");
- dissect_crd(tvb, &offset, body_tree);
- break;
- case(CONNECTIONSTATE_REQ):
- case(DISCONNECT_REQ):
- proto_tree_add_item(body_tree, hf_knxnetip_communication_channel_id, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_reserved, tvb, offset, 1, ENC_NA);
- offset+=1;
- dissect_hpai(tvb, &offset, body_tree, ": Control endpoint");
- break;
- case(DISCONNECT_RES):
- case(CONNECTIONSTATE_RES):
- proto_tree_add_item(body_tree, hf_knxnetip_communication_channel_id, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_connectionstate_status, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- break;
- case(DEVICE_CONFIGURATION_ACK):
- offset = dissect_connection_header(tvb, offset, body_tree, TRUE);
- proto_tree_add_item(body_tree, hf_knxnetip_confack_status, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- break;
- case(DEVICE_CONFIGURATION_REQ):
- case(TUNNELLING_REQ):
- offset = dissect_connection_header(tvb, offset, body_tree, FALSE);
- err = dissect_cemi (tvb, &offset, body_tree, pinfo);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- }
- break;
- case(TUNNELLING_ACK):
- offset = dissect_connection_header(tvb, offset, body_tree, TRUE);
- proto_tree_add_item(body_tree, hf_knxnetip_tunnelack_status, tvb, offset, 1, ENC_BIG_ENDIAN);
- break;
- case(ROUTING_INDICATION):
- err = dissect_cemi (tvb, &offset, body_tree, pinfo);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- }
- break;
- case(ROUTING_LOST):
- proto_tree_add_item(body_tree, hf_knxnetip_structure_length, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_bitmask(body_tree, tvb, offset, hf_knxnetip_devicestate, ett_knxnetip_devicestate, devicestate_flags, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_numberoflost, tvb, offset, 2, ENC_BIG_ENDIAN);
- offset+=2;
- break;
- case(ROUTING_BUSY):
- proto_tree_add_item(body_tree, hf_knxnetip_structure_length, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_bitmask(body_tree, tvb, offset, hf_knxnetip_devicestate, ett_knxnetip_devicestate, devicestate_flags, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_busywaittime, tvb, offset, 2, ENC_BIG_ENDIAN);
- offset+=2;
- proto_tree_add_item(body_tree, hf_knxnetip_busycontrol, tvb, offset, 2, ENC_BIG_ENDIAN);
- offset+=2;
- break;
- case(REMOTE_DIAG_REQ):
- dissect_hpai(tvb, &offset, body_tree, ": Discovery endpoint");
- offset = dissect_selector(tvb ,offset, body_tree);
- break;
- case(REMOTE_DIAG_RES):
- offset = dissect_selector(tvb ,offset, body_tree);
- do{
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- } while (tvb_reported_length_remaining(tvb,offset) > 0);
- break;
- case(REMOTE_BASIC_CONF_REQ):
- dissect_hpai(tvb, &offset, body_tree, ": Discovery endpoint");
- offset = dissect_selector(tvb ,offset, body_tree);
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- break;
- }
- if (tvb_reported_length_remaining(tvb,offset) > 0) {
- err = dissect_dib(tvb, &offset, body_tree);
- if (err == TRUE){
- proto_tree_add_expert(body_tree, pinfo, &ei_knxnetip_length, tvb, offset, -1);
- }
- }
- break;
- case(REMOTE_RESET_REQ):
- offset = dissect_selector(tvb ,offset, body_tree);
- proto_tree_add_item(body_tree, hf_knxnetip_reset, tvb, offset, 1, ENC_BIG_ENDIAN);
- offset+=1;
- proto_tree_add_item(body_tree, hf_knxnetip_reserved, tvb, offset, 1, ENC_NA);
- offset+=1;
- break;
-
- default:
- proto_tree_add_item(body_tree, hf_knxnetip_unknown, tvb, offset, -1, ENC_NA);
- }
-}
-
-static gboolean dissect_knxnetip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) {
-
- gint idx;
- idx = 0;
-
- if (tvb_captured_length(tvb) < 8){
- return (FALSE);
- }
- if ( tvb_get_guint8(tvb, 0) != KNXNETIP_HEADER_LENGTH) {
- return (FALSE);
- }
- if ( tvb_get_guint8(tvb, 1) != KNXNETIP_PROTOCOL_VERSION){
- return (FALSE);
- }
- try_val_to_str_idx((guint32)tvb_get_ntohs(tvb, 2), knxnetip_service_identifier, &idx);
- if (idx == -1){
- return (FALSE);
- }
-
- dissect_knxnetip(tvb, pinfo, tree);
- return (TRUE);
-}
-
-void proto_register_knxnetip (void) {
- expert_module_t* expert_knxnetip;
-
- static hf_register_info hf[] = {
- { &hf_knxnetip_headerlength,
- { "Header Length", "knxnetip.header_length", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_version,
- { "Protocol Version", "knxnetip.version", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_servicetype,
- { "Service Type Identifier", "knxnetip.service_type_identifier", FT_UINT16, BASE_HEX, VALS(knxnetip_service_identifier), 0x0, NULL, HFILL }},
- { &hf_knxnetip_totallength,
- { "Total Length", "knxnetip.total_length", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_hpai,
- { "HPAI", "knxnetip.hpai", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_hpai_structure_length,
- { "Structure Length", "knxnetip.hpai_structure_length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_structure_length,
- { "Structure Length", "knxnetip.struct_length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_hpai_host_protocol,
- { "Host Protocol Code", "knxnetip.hpai_host_protocol", FT_UINT8, BASE_HEX, VALS(knxnetip_host_protocol_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_hpai_ip_address,
- { "IP Address", "knxnetip.hpai_ip_address", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_hpai_port,
- { "IP Port", "knxnetip.hpai_port", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib,
- { "DIB", "knxnetip.dib", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cri,
- { "Connection Request Information", "knxnetip.cri", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_type,
- { "Description Type", "knxnetip.dib_type", FT_UINT8, BASE_HEX, VALS(knxnetip_dib_description_type_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_medium,
- { "KNX medium", "knxnetip.dib_medium", FT_UINT8, BASE_HEX, VALS(knxnetip_dib_medium_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_status,
- { "Device Status", "knxnetip.dib_status", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_projectid,
- { "Project-Installation identifier", "knxnetip.dib_projectid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_knxaddress,
- { "KNX Individual Address", "knxnetip.knxaddress", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_serialnumber,
- { "KNXnet/IP device serial number", "knxnetip.serialnumber", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_multicast_address,
- { "KNXnet/IP device multicast address", "knxnetip.multicast", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_mac_address,
- { "KNXnet/IP device MAC address", "knxnetip.macaddress", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_friendly,
- { "Device Friendly Name", "knxnetip.devicename", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_service,
- { "Service ID", "knxnetip.dib_service", FT_UINT8, BASE_HEX, VALS(knxnetip_service_types), 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_ipaddress,
- { "IP Address", "knxnetip.dib_ipaddress", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_subnet,
- { "Subnet Mask", "knxnetip.dib_subnet", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_gateway,
- { "Default Gateway", "knxnetip.dib_gateway", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_ipcapa,
- { "IP Capabilities", "knxnetip.dib_ipcapabilities", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_ipassign,
- { "IP assignment method", "knxnetip.dib_assignment", FT_UINT8, BASE_HEX, VALS(knxnetip_ip_assignment_method), 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_dhcp,
- { "DHCP Server", "knxnetip.dib_dhcp", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_manuid,
- { "Manufacturer ID", "knxnetip.manufacturer_id", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_manudata,
- { "Manufacturer specific data", "knxnetip.manufacturer_data", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_connection_type,
- { "Connection Type", "knxnetip.connection_type", FT_UINT8, BASE_HEX, VALS(knxnetip_connection_types), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cri_protocol_data,
- { "Protocol Data", "knxnetip.cri_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_communication_channel_id,
- { "Communication Channel ID", "knxnetip.communication_channel_id", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_connect_status,
- { "Status", "knxnetip.connect_status", FT_UINT8, BASE_HEX, VALS(knxnetip_connect_response_status_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_crd_protocol_data,
- { "Protocol Data", "knxnetip.crd_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_crd,
- { "Connection Response Data Block", "knxnetip.crd", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_connectionstate_status,
- { "Status", "knxnetip.connect_state_status", FT_UINT8, BASE_HEX, VALS(knxnetip_connectionstate_response_status_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_counter,
- { "Sequence Counter", "knxnetip.sequence_counter", FT_UINT8, BASE_DEC, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_confack_status,
- { "Status", "knxnetip.confirm_ack_status", FT_UINT8, BASE_HEX, VALS(knxnetip_device_configuration_ack_status_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_tunnelack_status,
- { "Status", "knxnetip.tunnel_status", FT_UINT8, BASE_HEX, VALS(knxnetip_tunneling_error_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_status_flag_reserved,
- { "reserved", "knxnetip.dib_reserved", FT_UINT8, BASE_HEX, NULL, FLAGS_DEVICESTATUS_RESERVED, NULL, HFILL }},
- { &hf_knxnetip_dib_status_flag_program,
- { "program mode", "knxnetip.dib_program_mode", FT_UINT8, BASE_DEC, NULL, FLAGS_DEVICESTATUS_PROGRAM, NULL , HFILL }},
- { &hf_knxnetip_dib_ipcapa_flag_reserved,
- { "reserved", "knxnetip.ip_capabilities_reserved", FT_UINT8, BASE_HEX, NULL, FLAGS_IPCAPABILITES_RESERVED, NULL, HFILL }},
- { &hf_knxnetip_dib_ipcapa_flag_bootip,
- { "BootIP", "knxnetip.ip_capabilities_bootip", FT_UINT8, BASE_DEC, NULL, FLAGS_IPCAPABILITES_BOOTIP, NULL, HFILL }},
- { &hf_knxnetip_dib_ipcapa_flag_dhcp,
- { "DHCP", "knxnetip.ip_capabilities_dhcp", FT_UINT8, BASE_DEC, NULL, FLAGS_IPCAPABILITES_DHCP, NULL, HFILL }},
- { &hf_knxnetip_dib_ipcapa_flag_autoip,
- { "AutoIP", "knxnetip.ip_capabilities_autoip", FT_UINT8, BASE_DEC, NULL, FLAGS_IPCAPABILITES_AUTOIP, NULL, HFILL }},
- { &hf_knxnetip_devicestate,
- { "DeviceState", "knxnetip.devicestate", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_devicestate_reserved,
- { "reserved", "knxnetip.devicestate_reserved", FT_UINT8, BASE_HEX, NULL, FLAGS_DEVICESTATE_RESERVED, NULL, HFILL }},
- { &hf_knxnetip_devicestate_knx,
- { "KNX Fault", "knxnetip.devicestate_knx", FT_UINT8, BASE_DEC, NULL, FLAGS_DEVICESTATE_KNX, "is set if KNX network cannot be accessed", HFILL }},
- { &hf_knxnetip_devicestate_ip,
- { "IP Fault", "knxnetip.devicestate_ip", FT_UINT8, BASE_DEC, NULL, FLAGS_DEVICESTATE_IP, "is set if IP network cannot be accessed", HFILL }},
- { &hf_knxnetip_numberoflost,
- { "NumberofLostMessages", "knxnetip.number_of_lost_msg", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_busywaittime,
- { "Busy Wait Time", "knxnetip.busy_time", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_milliseconds, 0x0, NULL, HFILL }},
- { &hf_knxnetip_busycontrol,
- { "Busy Control Field", "knxnetip.busy_control", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_knxlayer,
- { "KNX Layer", "knxnetip.layer", FT_UINT8, BASE_HEX, VALS(knxnetip_knxlayer_values), 0x0, NULL, HFILL }},
- { &hf_knxnetip_selector_type,
- { "Selector Type Code", "knxnetip.selector", FT_UINT8, BASE_HEX, VALS(knxnetip_selector_types), 0x0, NULL, HFILL }},
- { &hf_knxnetip_reset,
- { "Reset Command", "knxnetip.reset", FT_UINT8, BASE_HEX, VALS(knxnetip_reset_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi,
- { "cEMI", "knxnetip.cemi", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_mc,
- { "messagecode", "knxnetip.cemi_messagecode", FT_UINT8, BASE_HEX, VALS(cemi_messagecodes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_addlength,
- { "add information length", "knxnetip.additional_length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_additemlength,
- { "Length", "knxnetip.additional_item_length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_typid,
- { "Type id", "knxnetip.cemi_type_id", FT_UINT8, BASE_HEX, VALS(cemi_add_type_id), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_pl,
- { "Domain-Address", "knxnetip.cemi_type_pl", FT_UINT16, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus,
- { "Busmonitor error flags", "knxnetip.cemi_type_bus", FT_UINT8, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_relt,
- { "relative timestamp", "knxnetip.cemi_type_reltime", FT_UINT16, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_delay,
- { "delay", "knxnetip.cemi_type_delay", FT_UINT32, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_exttime,
- { "extended timestamp", "knxnetip.cemi_type_exttime", FT_UINT32, BASE_HEX, 0x0, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bibat,
- { "BiBat", "knxnetip.cemi_type_bibat", FT_UINT8, BASE_HEX, VALS(cemi_bibat_ctrl), 0xF8, NULL, HFILL }},
- { &hf_knxnetip_cemi_controlfield1,
- { "Controlfield 1", "knxnetip.controlfield_one", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_flag_frametype,
- { "Frametype", "knxnetip.controlfield_type", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL1_FT, "0: extended frame; 1: standard frame", HFILL }},
- { &hf_knxnetip_cemi_flag_repeat,
- { "Repeat", "knxnetip.controlfield_repeat", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL1_R, "0: repeat if error frame; 1: do not repeat", HFILL }},
- { &hf_knxnetip_cemi_flag_sb,
- { "System-Broadcast", "knxnetip.controlfield_broadcast", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL1_SB, "0: system-broadcast; 1: broadcast", HFILL }},
- { &hf_knxnetip_cemi_flag_priority,
- { "Priority", "knxnetip.controlfield_priority", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_CONTROL1_P, NULL, HFILL }},
- { &hf_knxnetip_cemi_flag_ack,
- { "Acknowledge-Request", "knxnetip.controlfield_ack", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL1_A, "0: no request for ack; 1: request ack", HFILL }},
- { &hf_knxnetip_cemi_flag_confirm,
- { "Confirm-Flag", "knxnetip.controlfield_confirm", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL1_C, "0: no error in frame; 1: error in frame", HFILL }},
- { &hf_knxnetip_cemi_controlfield2,
- { "Controlfield 2", "knxnetip.controlfield_two", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_flag_destaddress,
- { "Destination address type", "knxnetip.controldestaddress", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL2_AT, "0: individual; 1: group", HFILL }},
- { &hf_knxnetip_flag_hop,
- { "Hop count", "knxnetip.controlhop", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_CONTROL2_HC, NULL, HFILL }},
- { &hf_knxnetip_flag_eff,
- { "Extended Frame Format", "knxnetip.controleff", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_CONTROL2_EFF, "0000b for standard frame", HFILL }},
- { &hf_knxnetip_cemi_sourceaddress,
- { "Source Address", "knxnetip.cemisource", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_destaddress,
- { "Destination Address", "knxnetip.cemidestination", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_tpci,
- { "TPCI", "knxnetip.cemitpci", FT_UINT8, BASE_HEX, VALS(cemi_tpci_vals), 0xC0, NULL, HFILL }},
- { &hf_knxnetip_cemi_npdu_length,
- { "NPDU length", "knxnetip.npdulength", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_tpdu_length,
- { "TPDU length", "knxnetip.tpdulength", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_octet_octets, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_counter,
- { "sequence NCD/NDT", "knxnetip.npduseq", FT_UINT8, BASE_DEC, NULL, 0x3C, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci,
- { "APCI", "knxnetip.npduapci", FT_UINT16, BASE_HEX, VALS(cemi_apci_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_data,
- { "Data", "knxnetip.cemidata", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_numberofslots,
- { "number of slots", "knxnetip.ceminumberofslots", FT_UINT8, BASE_DEC, NULL, 0xF, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_memory_number,
- { "number of octets to be read/write", "knxnetip.cemidata", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_iot,
- { "Interface object type", "knxnetip.cemiiot", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_oi,
- { "Object Instance", "knxnetip.cemioi", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_pid,
- { "Property Identifier", "knxnetip.cemipid", FT_UINT8, BASE_DEC, VALS(cemi_propertyid), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_noe,
- { "Number of Elements", "knxnetip.ceminoe", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL }},
- { &hf_knxnetip_cemi_six,
- { "Startindex", "knxnetip.cemipid", FT_UINT16, BASE_DEC, NULL, 0xFFF, NULL, HFILL }},
- { &hf_knxnetip_cemi_numberofelements,
- { "Number of Elements", "knxnetip.ceminumber", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_error,
- { "Error Code", "knxnetip.cemierror", FT_UINT8, BASE_HEX, VALS(cemi_error_codes), 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_return,
- { "retrun code", "knxnetip.cemireturn", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_reserved,
- { "reserved", "knxnetip.cemireserved", FT_UINT8, BASE_HEX, NULL, 0xFC, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_info,
- { "RF-Info", "knxnetip.cemirfinfo", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_mesure,
- { "received signal strength", "knxnetip.cemirfmesure", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_RF_MESURE, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_reserved,
- { "reserved", "knxnetip.cemirfreserved", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_RF_RESERVED, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_mesure_re,
- { "retransmitter signal strrength", "knxnetip.cemirfmesurere", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_RF_MESURE_RE, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_battery,
- { "Battery state", "knxnetip.cemirfbattery", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_RF_BATTERY, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_bidirekt,
- { "is not bidirektional", "knxnetip.cemirfbattery", FT_UINT8, BASE_HEX, NULL, FLAGS_CEMI_RF_BIDIRETIONAL, NULL, HFILL }},
- { &hf_knxnetip_cemi_rf_sn,
- { "KNX Serial Number", "knxnetip.cemiknxsn", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_rf_lfn,
- { "Data Link Layer frame number", "knxnetip.cemilfn", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_f,
- { "Frame error flag", "knxnetip.cemibusferror", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_F, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_b,
- { "Bit error flag", "knxnetip.cemibusberror", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_B, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_p,
- { "Parity error flag", "knxnetip.cemibusparity", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_P, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_d,
- { "dont care", "knxnetip.cemibusdont", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_D, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_l,
- { "Lost flag", "knxnetip.cemibuslost", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_L, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bus_flag_sss,
- { "Sequence Number", "knxnetip.cemibusseq", FT_UINT8, BASE_DEC, NULL, FLAGS_CEMI_BUS_SSS, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_bibat_block,
- { "BiBat Block number", "knxnetip.cemibibbatblock", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_multi_fastack,
- { "KNX RF Multi Fast Ack", "knxnetip.cemirffastack", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_multi_freq,
- { "KNX RF Multi Transmission Frequency", "knxnetip.cemirffreq", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_multi_channel,
- { "KNX RF Multi Call Channel", "knxnetip.cemirfchannel", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_rf_multi_recep_freq,
- { "KNX RF Multi Reception Frequency", "knxnetip.cemirfrecfreq", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_preamble_length,
- { "Preamble Length", "knxnetip.cemipreamblelength", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_postamble_length,
- { "Postamble Length", "knxnetip.cemipostamblelength", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_fastack,
- { "Fast Ack information", "knxnetip.cemifastack", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_fastack_crc,
- { "Fast Ack is received with a CRC", "knxnetip.cemifastackcrc", FT_UINT16, BASE_DEC, NULL, FLAGS_CEMI_FASTACK_CRC, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_fastack_error,
- { "Fast Ack is received with a Manchester error", "knxnetip.cemifastackerror", FT_UINT16, BASE_DEC, NULL, FLAGS_CEMI_FASTACK_ERROR, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_fastack_received,
- { "Fast Ack has been received", "knxnetip.cemifastackres", FT_UINT16, BASE_DEC, NULL, FLAGS_CEMI_FASTACK_RES, NULL, HFILL }},
- { &hf_knxnetip_cemi_type_fastack_info,
- { "Fast Ack Info", "knxnetip.cemifastackinfo", FT_UINT16, BASE_HEX, NULL, FLAGS_CEMI_FASTACK_INFO, NULL, HFILL }},
- { &hf_knxnetip_cemi_subfunction,
- { "Subfunction", "knxnetip.cemisubfunction", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_manuspecificdata,
- { "Manufacturer specific data", "knxnetip.cemimanuspecificdata", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_projectnumber,
- { "Project number", "knxnetip.projectnumber", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_installnumber,
- { "Installation number", "knxnetip.installnumber", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_dib_svc_version,
- { "Version", "knxnetip.svcversion", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_reserved,
- { "reserved", "knxnetip.reserved", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_data,
- { "data", "knxnetip.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_polldata,
- { "Poll data", "knxnetip.polldata", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_raw,
- { "RAW Frame", "knxnetip.raw", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_additional,
- { "Additional information", "knxnetip.additional", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_unknown,
- { "UNKNOWN", "knxnetip.unknown", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_mem_address,
- { "Memory Address", "knxnetip.cemimemaddress", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_channel,
- { "Channel nr", "knxnetip.cemichannel", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_key,
- { "key", "knxnetip.apcikey", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_level,
- { "level", "knxnetip.apcilevel", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_object,
- { "object index", "knxnetip.apciobjidx", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
- { &hf_knxnetip_cemi_apci_propid,
- { "property id", "knxnetip.apcipropid", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}
- };
-
- static gint *ett[] = {
- &ett_knxnetip,
- &ett_knxnetip_header,
- &ett_knxnetip_body,
- &ett_knxnetip_hpai,
- &ett_knxnetip_dib,
- &ett_knxnetip_dib_projectid,
- &ett_knxnetip_dib_service,
- &ett_knxnetip_cri,
- &ett_knxnetip_crd,
- &ett_knxnetip_dib_status,
- &ett_knxnetip_dib_ipcapa,
- &ett_knxnetip_devicestate,
- &ett_knxnetip_cemi,
- &ett_knxnetip_cemi_additional,
- &ett_knxnetip_cemi_additional_item,
- &ett_knxnetip_cemi_control1,
- &ett_knxnetip_cemi_control2,
- &ett_knxnetip_cemi_rf_info,
- &ett_knxnetip_cemi_bus_info,
- &ett_knxnetip_cemi_fastack
- };
-
- static ei_register_info ei[] = {
- { &ei_knxnetip_length, { "knxnetip.invalid.length", PI_PROTOCOL, PI_ERROR, "invalid length", EXPFILL }},
- };
-
- proto_knxnetip = proto_register_protocol("KNXnet/IP", "knxnetip", "knx");
- proto_register_field_array(proto_knxnetip, hf, array_length(hf));
- proto_register_subtree_array(ett, array_length(ett));
- expert_knxnetip = expert_register_protocol(proto_knxnetip);
- expert_register_field_array(expert_knxnetip, ei, array_length(ei));
-}
-
-
-void proto_reg_handoff_knxnetip(void) {
- /* register as heuristic dissector for both TCP and UDP */
- heur_dissector_add("tcp", dissect_knxnetip_heur, "KNXnet/IP over TCP", "knxnetip_tcp", proto_knxnetip, HEURISTIC_ENABLE);
- heur_dissector_add("udp", dissect_knxnetip_heur, "KNXnet/IP over UDP", "knxnetip_udp", proto_knxnetip, HEURISTIC_ENABLE);
-}
-
-/*
- * Editor modelines - http://www.wireshark.org/tools/modelines.html
- *
- * Local variables:
- * c-basic-offset: 4
- * tab-width: 8
- * indent-tabs-mode: nil
- * End:
- *
- * vi: set shiftwidth=4 tabstop=8 expandtab:
- * :indentSize=4:tabSize=8:noTabs=true:
- */
diff --git a/test/captures/knxip_DataSec.pcap b/test/captures/knxip_DataSec.pcap
new file mode 100644
index 0000000000..7e28cd40e5
--- /dev/null
+++ b/test/captures/knxip_DataSec.pcap
Binary files differ
diff --git a/test/captures/knxip_SecureWrapper.pcap b/test/captures/knxip_SecureWrapper.pcap
new file mode 100644
index 0000000000..0be64d8c1a
--- /dev/null
+++ b/test/captures/knxip_SecureWrapper.pcap
Binary files differ
diff --git a/test/captures/knxip_TimerNotify.pcap b/test/captures/knxip_TimerNotify.pcap
new file mode 100644
index 0000000000..4e7ae3781b
--- /dev/null
+++ b/test/captures/knxip_TimerNotify.pcap
Binary files differ
diff --git a/test/keys/knx_keyring.xml b/test/keys/knx_keyring.xml
new file mode 100644
index 0000000000..c9b3cdd299
--- /dev/null
+++ b/test/keys/knx_keyring.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- All attributes of the Keyring element are mandatory. For details on how to calculate/verify the Signature attribute, see below -->
+<!-- "oKGio6SlpqeoqaqrrK2urw==" = $ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF -->
+<!-- "sLGys7S1tre4ubq7vL2+vw==" = $ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF -->
+<Keyring xmlns="http://knx.org/xml/keyring/1" Project="Family Home" Created="2015-03-04T20:55:58.0160546Z" CreatedBy="ETS 5.5 Build 456" Signature="MTIzNDU2Nzg5MDEyMzQ1Ng==">
+ <!-- A backbone element is included if the project has a secure IP backbone or the keyring contains at least one IP Routing interface -->
+ <!-- The Latency and BackboneKey attributes are only included if the project has a secure IP backbone -->
+ <!-- BackboneKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created), Project.BackboneKey as byte[])) --> (1)
+ <Backbone MulticastAddress="224.0.23.12" Latency="1000" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <!-- There might be 0..n interface elements. Possible types are "USB", "Tunneling" or "Routing". There is no support for "Eiblib/IP". -->
+ <!-- Depending on the type of interface different interface attributes are possible. -->
+ <!-- USB interfaces only appear in the keying if used for Data Security communication. They only have the mandatory attribute "IndividualAddress". -->
+ <Interface Type="USB" IndividualAddress="2.1.56">
+ <!-- Interfaces used for Data Security communication have a list of group addresses which will be communicated over this interface. -->
+ <!-- Each interface group has a mandatory list of trusted senders that are allowed to send telegrams to this group address. -->
+ <Group Address="3971" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ <Group Address="14271" Senders="1.1.1"/>
+ </Interface>
+ <!-- Tunneling interfaces might appear in the keying because either they are used for Data Security communication or access to the interface itself is protected (or both). -->
+ <!-- KNXnet/IP Tunneling interfaces have as a mandatary attribute the KNX individual address of the host device. -->
+ <!-- If the Tunneling interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- Secured Tunneling interfaces need to have the "UserID" and "Password" attributes. An "Authentication" attribute containing the interface's device authenication code is optional. -->
+ <!-- Password := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.BusAccess(IA).Password, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <Interface Type="Tunneling" Host="2.1.0" IndividualAddress="2.1.56" UserID="3" Password="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ <!-- If the Backbone (KNXnet/IP Routing) interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- If no "IndividualAddress" attribute is present, backbone interface elements shall be omitted altogether as all necessary information is part of the Backbone element already. -->
+ <Interface Type="Backbone" IndividualAddress="0.0.123">
+ <Group Address="256" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ </Interface>
+ <!-- The "GroupAddresses" collection shall only be present if the keyring contains interface groups for Data Security communication. -->
+ <!-- The "Address" and "Key" attributes are mandatory for every "GroupAddresses/Group" element. -->
+ <!-- Key := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), GroupAddress.Key as byte[])) --> (1)
+ <GroupAddresses>
+ <Group Address="256" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3971" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3972" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="14271" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ </GroupAddresses>
+ <!-- Keyings exported for the whole project (for backup or diagnostic purposes) shall contain one "Device" entry for every secure device. -->
+ <!-- For interface keyings the "Devices" list is optional and shall only be present if sequence numbers are known for devices referenced as interface group senders. -->
+ <!-- The "ToolKey" attribute shall only be present if the keying was exported for the whole project. -->
+ <!-- The "SeqNr" attribute is optional and shall only be present if a received sequence number for a given device is known. -->
+ <!-- ToolKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), Device.ToolKey as byte[])) --> (1)
+ <!-- Secured IP-enabled devices need to have the "ManagementPassword" attribute. An "Authentication" attribute containing the device's authenication code is optional. -->
+ <!-- ManagementPassword := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.ManagementPassword, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <!-- The "ManagementPassword" and "Authentication" attributes shall only be present if the keying was exported for the whole project. -->
+ <Devices>
+ <Device IndividualAddress="1.1.1" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="45678"/>
+ <Device IndividualAddress="1.1.2" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="34567"/>
+ <Device IndividualAddress="1.1.3" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="23456"/>
+ <Device IndividualAddress="1.1.4" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="12345"/>
+ <Device IndividualAddress="2.1.0" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="1234" ManagementPassword="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ </Devices>
+</Keyring> \ No newline at end of file
diff --git a/test/suite_decryption.py b/test/suite_decryption.py
index e393bf4d93..88e92438bb 100644
--- a/test/suite_decryption.py
+++ b/test/suite_decryption.py
@@ -760,3 +760,118 @@ class case_decrypt_wireguard(subprocesstest.SubprocessTestCase):
], pcap_file='wireguard-psk.pcap')
self.assertIn('2\t0', lines)
self.assertIn('4\t0', lines)
+
+class case_decrypt_knxip(subprocesstest.SubprocessTestCase):
+ # Capture files for these tests contain single telegrams.
+ # For realistic (live captured) KNX/IP telegram sequences, see:
+ # https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=14825
+
+ def test_knxip_data_security_decryption_ok(self):
+ '''KNX/IP: Data Security decryption OK'''
+ # capture_file contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_DataSec.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' DataSec '))
+ self.assertTrue(self.grepOutput(' PropExtValueWriteCon '))
+
+ def test_knxip_data_security_decryption_fails(self):
+ '''KNX/IP: Data Security decryption fails'''
+ # capture_file contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_DataSec.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' DataSec '))
+ self.assertFalse(self.grepOutput(' PropExtValueWriteCon '))
+
+ def test_knxip_secure_wrapper_decryption_ok(self):
+ '''KNX/IP: SecureWrapper decryption OK'''
+ # capture_file contains KNX/IP SecureWrapper RoutingInd telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_SecureWrapper.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' SecureWrapper '))
+ self.assertTrue(self.grepOutput(' RoutingInd '))
+
+ def test_knxip_secure_wrapper_decryption_fails(self):
+ '''KNX/IP: SecureWrapper decryption fails'''
+ # capture_file contains KNX/IP SecureWrapper RoutingInd telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_SecureWrapper.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' SecureWrapper '))
+ self.assertFalse(self.grepOutput(' RoutingInd '))
+
+ def test_knxip_timer_notify_authentication_ok(self):
+ '''KNX/IP: TimerNotify authentication OK'''
+ # capture_file contains KNX/IP TimerNotify telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_TimerNotify.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' TimerNotify '))
+ self.assertTrue(self.grepOutput(' OK$'))
+
+ def test_knxip_timer_notify_authentication_fails(self):
+ '''KNX/IP: TimerNotify authentication fails'''
+ # capture_file contains KNX/IP TimerNotify telegram
+ capture_file = os.path.join(config.capture_dir, 'knxip_TimerNotify.pcap')
+ self.runProcess((config.cmd_tshark,
+ '-r', capture_file,
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput(' TimerNotify '))
+ self.assertFalse(self.grepOutput(' OK$'))
+
+ def test_knxip_keyring_xml_import(self):
+ '''KNX/IP: keyring.xml import'''
+ # key_file "keyring.xml" contains KNX decryption keys
+ key_file = os.path.join(config.key_dir, 'knx_keyring.xml')
+ # capture_file is empty
+ capture_file = os.path.join(config.capture_dir, 'empty.pcap')
+ # Write extracted key info to stdout
+ self.runProcess((config.cmd_tshark,
+ '-o', 'kip.key_file:' + key_file,
+ '-o', 'kip.key_info_file:-',
+ '-r', capture_file,
+ ),
+ env=config.test_env)
+ self.assertTrue(self.grepOutput('^MCA 224[.]0[.]23[.]12 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]3$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 sender 1[.]1[.]2$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 6/7/191 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]1$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]3$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 sender 1[.]1[.]4$'))
+ self.assertTrue(self.grepOutput('^GA 0/1/0 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/131 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 1/7/132 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^GA 6/7/191 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]1 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]1 SeqNr 45678$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]2 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]2 SeqNr 34567$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]3 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]3 SeqNr 23456$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 SeqNr 12345$'))
+ self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
+ self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 SeqNr 1234$'))