Data Secure comissioning in ETS compatibility: extended frames, Max APDU length, and KNXnet/IP Tunnelling v2#4
Data Secure comissioning in ETS compatibility: extended frames, Max APDU length, and KNXnet/IP Tunnelling v2#4evgeny-boger wants to merge 9 commits intomainfrom
Conversation
| interface = cfg->value("interface",""); | ||
| servername = cfg->value("name", dynamic_cast<Router *>(&router)->servername); | ||
| keepalive = cfg->value("heartbeat-timeout", CONNECTION_ALIVE_TIME); | ||
| maxAPDULength = cfg->value("max-apdu-length", 249); |
There was a problem hiding this comment.
you said it defaults to 15, yet in the code it is 249. Keep 15
| switch(state) | ||
| { | ||
| case T_wait_keepalive: | ||
| TRACEPRINTF (t, 8, "State indication in wait_keepalive, going to wait"); |
There was a problem hiding this comment.
remove this traceprintf
| setstate(T_wait); | ||
| break; | ||
| case T_in_reset: | ||
| TRACEPRINTF (t, 8, "State indication in reset, staying"); |
There was a problem hiding this comment.
remove this traceprintf
| // ERRORPRINTF (t, E_ERROR | 62, "TPUART detected. Hardware ACK not supported."); | ||
| // my_addr = 0; | ||
| // } | ||
| TRACEPRINTF (t, 8, "State indication in setaddr, going to getstate"); |
There was a problem hiding this comment.
remove this traceprintf
| setstate(T_in_getstate); | ||
| break; | ||
| case T_in_getstate: | ||
| TRACEPRINTF (t, 8, "State indication in getstate, going online"); |
There was a problem hiding this comment.
remove this traceprintf
| SEARCH_REQUEST_EXTENDED = 0x020B, | ||
| SEARCH_RESPONSE_EXTENDED = 0x020C, | ||
|
|
||
| /* Tunnelling (0x0420 .. 0x042F) */ |
There was a problem hiding this comment.
add reference to the KNX standard: version, section, paragraph and so on.
| r2.installid = 0; | ||
| inet_pton(AF_INET, "224.0.23.12", &r2.multicastaddr); | ||
| strncpy((char *) r2.name, router.servername.c_str(), sizeof(r2.name) - 1); | ||
| d.version = 2; // v2 = TCP support (ISO 22510) |
There was a problem hiding this comment.
add reference to the KNX standard: version, section, paragraph and so on, instead of ISO 22510
| protected: | ||
| /** config */ | ||
| ev::tstamp keepalive; | ||
| uint16_t maxAPDULength = 249; |
There was a problem hiding this comment.
hardcoded value should be conservative, not 249
|
|
||
| if (p1.service == TUNNEL_FEATURE_GET) | ||
| { | ||
| // ISO 22510: TUNNELLING_FEATURE_GET |
There was a problem hiding this comment.
add reference to the KNX standard: version, section, paragraph and so on.
| int no; | ||
| bool nat; | ||
| uint8_t maxAPDULength; | ||
| uint16_t maxAPDULength; |
There was a problem hiding this comment.
don't need to widen the type. But do check the configuration value though, not silently corrupt data
0681d4c to
4fced1e
Compare
| EIBnet_ConnectResponse r2; | ||
| // For TCP, the "Route Back" HPAI must be used. | ||
| // See ISO 22510:2019 section 5.2.8.6.2 | ||
| // KNX Std v3.0.4, 03_08_02 Core v01.06.02, §5.2.8.6.2 |
| r2.installid = 0; | ||
| inet_pton(AF_INET, "224.0.23.12", &r2.multicastaddr); | ||
| strncpy((char *) r2.name, router.servername.c_str(), sizeof(r2.name) - 1); | ||
| d.version = 2; // v2 = TCP support, KNX Std v3.0.4, 03_08_02 Core v01.06.02 |
There was a problem hiding this comment.
find exact paragraph
| d.version = 2; // v2 = TCP support, KNX Std v3.0.4, 03_08_02 Core v01.06.02 | ||
| d.family = 2; // Core | ||
| r2.services.push_back(d); | ||
| d.family = 3; // Device Management |
There was a problem hiding this comment.
make constants for these numbers. It's table 3 in 7.5.4.3 of , KNX Std v3.0.4, 03_08_02 pdf I suppose?
|
|
||
| if (p1.service == TUNNEL_FEATURE_SET) | ||
| { | ||
| // KNX Std v3.0.4, 03_08_04 Tunnelling v01.07.01, §3.3: TUNNELLING_FEATURE_SET |
There was a problem hiding this comment.
reference a format table
|
|
||
| switch (featureID) | ||
| { | ||
| case 0x01: // SupportedEMIType — read-only |
There was a problem hiding this comment.
again, should be constants or enums
4fced1e to
e32d468
Compare
| else if (prop == PID_MAX_APDULENGTH) | ||
| { | ||
| res.resize (2); | ||
| res[1] = maxAPDULength; |
There was a problem hiding this comment.
res[1] is 8-bit, doesn't it?
|
|
||
| enum PropertyID : uint8_t | ||
| { | ||
| PID_MAX_APDULENGTH = 0x38, |
There was a problem hiding this comment.
what are other property ids?
What is the reference for this 0x38 in a standard?
| else if (prop == PID_MAX_APDULENGTH) | ||
| { | ||
| res.resize (2); | ||
| res[1] = 0xF9; |
There was a problem hiding this comment.
what is this magic number?
| TRACEPRINTF (t, 8, "DESCRIBE"); | ||
|
|
||
| Router& router = static_cast<Router &>(parent->router); | ||
| r2.KNXmedium = 2; |
There was a problem hiding this comment.
make an enum for medium types (if it's not exists yet)
a48d716 to
7d7f873
Compare
Respond to property PID_MAX_APDULENGTH (0x38) queries in KNXnet/IP tunneling config requests instead of returning count=0 (unsupported). Auto-detect max APDU length from hardware driver capabilities via Router::maxFrameLength(), which returns the minimum frame length across all bus drivers. Configurable via 'max-apdu-length' in knxd.ini (range 15..254 per 03_05_01 §4.3.7.1). Based on voxior-inc/knxd fork by Zan Pevec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add state name to timeout, retry and error messages for easier debugging. Decode TPUART state indication register bits (SC, RE, TE, PE, TW) in warning messages. Log state transitions during reset, setaddr and getstate sequences. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ISO 22510 service type enums: SEARCH_REQUEST_EXTENDED, SEARCH_RESPONSE_EXTENDED, TUNNEL_FEATURE_GET/RESPONSE/SET/INFO. Required for KNXnet/IP Secure and ETS6 TCP tunneling support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ETS6 sends DESCRIPTION_REQUEST over TCP to discover device capabilities. Respond with device info and v2 service families (Core, Device Management, Tunnelling) per ISO 22510. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass maxAPDULength from server config to TunServiceConfig so that DEVICE_CONFIGURATION_REQUEST for PID_MAX_APDULENGTH returns the configured value instead of hardcoded 249. Configurable via 'max-apdu-length' in the tcptunsrv section of knxd.ini. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7d7f873 to
cc3f64b
Compare
Extract frame-to-UART encoding from send_again() into virtual encode_frame() on TPUARTwrap. Base implementation handles standard 6-bit TPUART indexing (max 63 bytes). NCN5120wrap overrides it to insert U_L_DataOffset.req commands at 64-byte boundaries for extended frames up to 263 bytes. The virtual method approach cleanly separates chip-specific UART command encoding from the shared transmission logic in send_again(). Future TPUART-family chips (e.g. Elmos E981.03 with its U_L_LongDataContinue 3-byte encoding) can override encode_frame() without touching the shared code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete the TUNNEL_FEATURE_GET handler for all interface features defined in KNX Standard v3.0.4 §03.08.04 (Tunnelling v01.07.01), and add a TUNNEL_FEATURE_SET handler. FEATURE_GET additions (features 0x01-0x03, 0x07 were added earlier): - 0x01: Fix SupportedEMIType to return 2 bytes per spec (was 1 byte, rejected by Calimero's value length validation) - 0x03: BusConnectionStatus now reports actual router link state via Router::isIdle() instead of hardcoded "connected" - 0x04: KNXManufacturerCode (configurable via manufacturer-code) - 0x05: ActiveEMIType (cEMI) - 0x06: InterfaceIndividualAddress (per-channel, from link layer) - 0x08: InterfaceFeatureInfoEnable (per-channel state) FEATURE_SET handler: - Feature 0x08 (EnableFeatureInfoService): writable, validates value is 0x00 or 0x01, rejects others with FR_DATA_VOID - All other known features: responds FR_ACCESS_READ_ONLY with value echoed back (capped to 2 bytes) - Unknown features: responds FR_ADDRESS_VOID, no value Also adds FeatureReturnCode enum to eibnetip.h with standard cEMI error codes used by TUNNEL_FEATURE_RESPONSE. Tested with raw protocol test (13/13) and Calimero KNX library (11/11), which validates per-feature value lengths per spec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Python script that tests all TUNNEL_FEATURE_GET/SET operations against a knxd TCP tunnel server. Covers all 8 interface features, unknown feature handling, read-only rejection, writable SET with readback verification, and proper disconnect. Usage: python3 test_tunnel_features.py [host] [port] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cc3f64b to
0d6cfd0
Compare
Implement SEARCH_RESPONSE_EXTENDED (03_08_02 Core v01.06.02, §7.6.4) instead of silently ignoring SEARCH_REQUEST_EXTENDED. Returns the same device info and service family DIBs as DESCRIPTION_RESPONSE. SRP (Search Request Parameter) filtering is not implemented — all DIBs are returned unconditionally, which is a valid superset per spec. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
This PR fixes ETS compatibility issues when connecting to knxd via KNXnet/IP TCP tunnelling to talk to KNX Data Secure devices. Without these changes, ETS cannot discover the correct APDU length, fails to handle extended KNX frames, and gets no responses to mandatory v2 feature queries.
Changes by area
Max APDU length reporting and auto-detect:
PID_MAX_APDULENGTH(PID 56, 03_05_01 §4.3.7) in DEVICE_CONFIGURATION_REQUEST responsesPID_MAX_APDULENGTHin TCP tunnel config serviceRouter::maxFrameLength()(minimum across all bus drivers, minus 8 for frame overhead)max-apdu-lengthin knxd.ini;manufacturer-codefor KNX manufacturer IDKNXnet/IP TCP Tunnelling v2 (required for ETS 6.3 TCP connections):
DESCRIPTION_REQUESTon TCP connections with v2 service familiesSEARCH_REQUEST_EXTENDED(ETS falls back gracefully)TUNNEL_FEATURE_GETfor all 8 interface features (§3.6)TUNNEL_FEATURE_SETwith proper access controlBusConnectionStatusreports actual router link state viaRouter::isIdle()TPUART/NCN5120 extended frame support:
encode_frame()virtual for chip-specific frame encodingU_L_DataOffset.reqfor frames >64 bytes (critical bug fix)maxFrameLength = 263; TPUART: 63; FT12/USB: 23Enhanced TPUART logging:
Test plan
Automated:
tools/test_tunnel_features.pyTests TUNNEL_FEATURE_GET for all 8 features, FEATURE_SET (writable, read-only, unknown), and readback verification. Expected: 13/13 passed.
Manual: config validation
max-apdu-length explicit value:
00 c8(200)00 c8(200)max-apdu-length auto-detect (not configured):
max-apdu-length out of range:
max-apdu-length = 300→ clamped to 254, error logmax-apdu-length 300 exceeds 254, clampingmax-apdu-length = 5→ clamped to 15, error logmax-apdu-length 5 below minimum 15, clampingmax-apdu-length = 0→ treated as "not configured", auto-detects from hardwaremanufacturer-code:
ab cd00 00Manual: ETS 6 TCP tunnel connection
TUNNEL_FEATURE_GETrequests from ETS being answered (withtrace-mask = 0xff)Manual: TPUART/NCN5120
Manual: regression