aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-knxip.c
diff options
context:
space:
mode:
authorRalf Nasilowski <Ralf.Nasilowski@ise.de>2018-08-16 10:49:31 +0200
committerRoland Knall <rknall@gmail.com>2018-10-16 09:03:43 +0000
commit9769df50efd830254aef0310562cdf47edd4ada3 (patch)
tree4e0b386f619e5a85c44a13dac9cf00071bfece1c /epan/dissectors/packet-knxip.c
parent84fd2d79682278927ce07361d901faed35bd1202 (diff)
KNX-IP: new KNXnet/IP dissector
The new KNXnet/IP dissector replaces the old KNXnet/IP dissector. The new KNXnet/IP dissector supports the new KNX features - A_MemoryExtended services - A_PropertyExt services - KNX Data Security - KNXnet/IP Core V2 - KNXnet/IP Device Management V2 - KNXnet/IP Tunneling V2 - KNXnet/IP Routing V2 - KNXnet/IP Security Change-Id: I3d1d716ef03d16d2720e6a1fcb23c2243d1cd956 Reviewed-on: https://code.wireshark.org/review/29155 Petri-Dish: Roland Knall <rknall@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl> Reviewed-by: Roland Knall <rknall@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-knxip.c')
-rw-r--r--epan/dissectors/packet-knxip.c4136
1 files changed, 4136 insertions, 0 deletions
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:
+ */