Why does AARQ Conformance Bit 19 (General Block Transfer - GBT) mismatch cause streaming deadlocks during large buffer reads?
-
[!WARNING]
Technical Redaction & Safe Harbor Notice:
All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.We are integrating a modern DLMS Head-End System (HES) with newly deployed smart meters over IEC 62056-47 TCP Wrapper. When reading small scalar registers (e.g. Instantaneous Voltage
1.0.32.7.0.255, Class 3), communications succeed immediately withGet-Response-Normal(0xC4 0x01).However, when reading large Profile Generic buffers (OBIS
1.0.99.1.0.255, Class 7) exceeding 2048 bytes, the meter does NOT return standardGet-Response-With-Datablock(0xC4 0x02). Instead, it responds with an unexpected APDU tag0xDBwrapping a General Block Transfer (GBT) streaming control header (0xE0 0x00 ...).Our HES client parser expects normal xDLMS datablocks, fails to recognize the GBT header, and stalls until timeout.
Here is the
AARQConformance Block sent by HES:5F 1F 04 00 00 7E 1F 04 00- Conformance BitString (24 bits):
00 7E 1F
And here is the meter's
AARENegotiated Conformance:5F 1F 04 00 00 7E 5F 04 00Why is the meter forcing GBT framing when our HES client did not request it?
- Conformance BitString (24 bits):
-
Marcus, look closely at your Conformance BitString in
AARQvsAARE!Per DLMS Green Book 10th Ed Section 9.2.2 (Negotiated Conformance Block) and IEC 62056-53 Section 8.3.2:
The 24-bit Conformance Block defines which xDLMS services are supported:Bit Position Service Name Meaning Bit 0 readGET service Bit 1 writeSET service Bit 2 unconfirmed-writeUnconfirmed write Bit 19 general-block-transfer(GBT)General Block Transfer Service Bit 20 delta-c-getDelta-C get Bit 23 multiple-referencesRead/Write multiple objects In your
AARQ:
00 7E 1F=>0000 0000 0111 1110 0001 1111b.
Notice byte 3 is0x1F(0001 1111b). Bit 19 is0(GBT disabled).However, in your meter's
AARE:
00 7E 5F=>0000 0000 0111 1110 0101 1111b.
Notice byte 3 is0x5F(0101 1111b). Bit 19 is SET to 1 (general-block-transfer)!According to Green Book rules, negotiated conformance is the bitwise AND of proposed and accepted conformance ($C_{\text{neg}} = C_{\text{prop}} \land C_{\text{acc}}$).
If the meter firmware unilaterally enables Bit 19 in its response even when the client proposed Bit 19 = 0, the meter violates the negotiation contract and assumes GBT mode! -
David is spot on. Many smart meter firmware stacks (especially European IDIS / SMETS compliant builds) are configured to force GBT for any payload larger than
max-pdu-sizebecause GBT provides streaming windowing with bidirectional acknowledgment, unlike standard stop-and-wait datablocks.How GBT (General Block Transfer) Works:
Per DLMS Green Book Section 9.2.5.4:
GBT encapsulates arbitrary APDUs inside a streaming sub-header:0xE0:General-Block-TransfertagByte 1: Control byte (Bit 7 = Last Block, Bit 6 = Streaming, Bits 0-5 = Window size / Block Number)Byte 2-3: Block Number ($0 \dots 65535$)
When your HES receives
0xE0inside0xDB, it must process the GBT sub-layer rather than expecting0xC4 0x02. -
Let's compare the efficiency of GBT vs Traditional DataBlocks:
- Traditional DataBlock (
0xC4 0x02):- Strictly 1-by-1 stop-and-wait.
- For every 1024-byte block, HES must send
Get-Request-Next(0xC0 0x02). Over cellular LTE-M with 200ms latency, transferring 500 KB takes 100+ seconds!
- General Block Transfer (GBT):
- Supports Sliding Window Streaming (e.g. Window Size = 4).
- Meter streams 4 consecutive blocks before requiring a single GBT ACK from HES, cutting transfer time by 75%!
However, if the HES stack does not implement the GBT state machine, communication deadlocks immediately.
- Traditional DataBlock (
-
From the cryptographic standpoint, GBT introduces a critical security difference:
In Traditional DataBlock, each datablock is ciphered as an individual application chunk or ciphered at the outer APDU level.
In GBT, the entire multi-block stream shares a single outer ciphering context or ciphers per GBT block with rolling Invocation Counters ($IC$).If your HES does not parse GBT, it fails to extract the underlying plain
Get-Response-Normalpayload. -
So we have two actionable solutions:
Solution A: Disable GBT via Strict Conformance Negotiation
If HES does not support GBT, ensure HES proposes
proposed-conformancewith Bit 19 = 0 (0x007E1F), and if the meter'sAAREreturns Bit 19 = 1, HES should sendRLRQor enforce standard block mode.Solution B: Implement Native GBT Support in HES
Update the HES Netty pipeline to handle tag
0xE0and transmit GBT acknowledgments (0xE0 0x00 [block_num]). -
Here is the verified GBT response frame structure returned by the meter:
00 01 00 01 00 01 04 08 DB 08 53 48 41 00 00 00 00 01 30 00 00 00 15 E0 00 00 01 [1024 bytes payload] 9B E1 4C 8D 23 AA 12 90 77 1B C0 44Dissecting the GBT header:
E0: GBT Tag00: Control byte (last_block = false,window = 0)00 01: Block number1- Followed by the chunked COSEM payload.
-
And here is the HES GBT Acknowledgment frame to fetch Block #2:
00 01 00 01 00 01 00 0C DB 08 53 48 41 00 00 00 00 01 30 00 00 00 16 E0 00 00 01The meter immediately streams Block #2 upon receiving this ACK.
-
Notice how clean this is: GBT operates as a transport bridge below the COSEM APDU, allowing full backward compatibility with any COSEM object.
-
To synthesize for the community, here is the Python Gurux snippet demonstrating how to configure Conformance Bitstrings to enable or disable GBT cleanly:
from gurux_dlms.enums import Conformance def configure_conformance_bits(client, enable_gbt=False): # Base Conformance: Read, Write, UnconfirmedWrite, ParameterizedAccess client.proposed_conformance = ( Conformance.READ | Conformance.WRITE | Conformance.UN_CONFIRMED_WRITE | Conformance.SELECTIVE_ACCESS ) if enable_gbt: # Enable Bit 19 (General Block Transfer) for high-speed streaming client.proposed_conformance |= Conformance.GENERAL_BLOCK_TRANSFER print("[+] GBT (General Block Transfer) enabled in AARQ Conformance.") else: # Strict Traditional DataBlock mode client.proposed_conformance &= ~Conformance.GENERAL_BLOCK_TRANSFER print("[+] Strict Traditional DataBlock mode enforced.")Summary Checklist for Conformance Negotiation:
- Bit 19 Inspection: Check byte 3 bit 5 of the Conformance BitString in
AARQ/AARE. - GBT Capability: If HES lacks GBT parser, explicitly clear Bit 19 in
AARQ. - Firmware Conformance: Validate that meter respects bitwise AND negotiation ($C_{\text{neg}} = C_{\text{prop}} \land C_{\text{acc}}$).
- Bit 19 Inspection: Check byte 3 bit 5 of the Conformance BitString in
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login