-
Notifications
You must be signed in to change notification settings - Fork 14
Description
I think there is a calculation error in the GATT server implementation that causes the ATT_FIND_INFORMATION_RESPONSE to exceed the negotiated MTU. This occurs because the code calculates the maximum number of attributes that can fit in a packet based only on the UUID size, forgetting to account for the 2-byte Attribute Handle that precedes each UUID in the response list.
This results in a protocol violation. While clients with large MTUs (like Android/iOS) may not notice (because they renegotiate MTU on connection before doing anything else), clients with the default BLE MTU of 23 bytes (like Garmin watches) will receive a packet larger than 23 bytes and immediately drop the connection.
The Bug in lib/internal/gatt.js:
node-ble-host/lib/internal/gatt.js
Line 1086 in a481cc2
| var max16 = (currentMtu - 2) / 2 >>> 0; |
In the handler for ATT_FIND_INFORMATION_REQUEST, the limits are currently calculated as:
var max16 = (currentMtu - 2) / 2 >>> 0;
var max128 = (currentMtu - 2) / 16 >>> 0;Why this is incorrect:
The ATT_FIND_INFORMATION_RESPONSE format consists of:
- 1 byte: Opcode
- 1 byte: Format
- N bytes: Information Data List (where each entry is 2 bytes Handle + UUID)
For a 16-bit UUID format, each entry is 4 bytes, not 2.
For a 128-bit UUID format, each entry is 18 bytes, not 16.
Example Failure Case:
On a connection with currentMtu = 23:
- The code calculates
max16as(23 - 2) / 2 = 10. - It attempts to pack 10 entries into the response.
- The resulting packet size is
2 + (10 * 4) = 42 bytes. - The server sends a 42-byte packet over a 23-byte link, causing a disconnect.
Suggested Fix:
Update the divisors to account for the 2-byte handle:
// Each entry is: 2 bytes (Handle) + UUID size (2 or 16)
var max16 = Math.floor((currentMtu - 2) / 4);
var max128 = Math.floor((currentMtu - 2) / 18);I am happy to create a PR with the fix.