From 88bb2fc3fa6c62065b3c01aea1afeb53aa6f3595 Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Tue, 4 Nov 2025 00:58:56 -0500 Subject: [PATCH] fix(ble): Restore identity handshake detection for peripheral connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem After the driver refactor (commit d1d94e5), peripheral devices were dropping the identity handshake sent by central devices. The logs showed: ``` [Warning] BLEInterface[BLE Interface] no identity for dev:B8:27:EB:A8:A7:22, cannot create fragmenter [Warning] BLEInterface[BLE Interface] no identity for peer dev:B8:27:EB:A8:A7:22, dropping data ``` Root cause: When the central sends its 16-byte identity handshake, the peripheral's `_data_received_callback` passed it to `_handle_ble_data`, which immediately dropped it (chicken-and-egg: no identity = drop data, but the dropped data IS the identity). The handshake detection logic existed in commit babb237 but was lost during the driver architecture refactor. ## Solution Added `_handle_identity_handshake()` method that: 1. Detects identity handshakes (exactly 16 bytes, no existing identity) 2. Stores the central's identity in bidirectional mappings 3. Creates fragmenter/reassembler with negotiated MTU 4. Spawns peer interface for the central 5. Returns True to prevent normal data processing Updated `_data_received_callback()` to check for handshakes before passing data to normal reassembly logic. ## Benefits - ✅ Restores bidirectional communication for peripheral connections - ✅ Peripheral can learn central's identity without scanning - ✅ Clean separation of handshake vs. data processing - ✅ Proper error handling with informative logging ## Testing Should resolve the asymmetric identity exchange seen in Pi1/Pi2 logs where central successfully connected but peripheral couldn't create fragmenter. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/RNS/Interfaces/BLEInterface.py | 75 +++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/RNS/Interfaces/BLEInterface.py b/src/RNS/Interfaces/BLEInterface.py index b073ecf..f313819 100644 --- a/src/RNS/Interfaces/BLEInterface.py +++ b/src/RNS/Interfaces/BLEInterface.py @@ -738,12 +738,85 @@ class BLEInterface(Interface): connection_type=connection_type ) + def _handle_identity_handshake(self, address: str, data: bytes) -> bool: + """ + Handle identity handshake from central device (peripheral role only). + + When a central connects to us (we're peripheral), it sends exactly 16 bytes + as the first packet - its identity hash. This allows the peripheral to learn + the central's identity without requiring discovery/scanning. + + Args: + address: MAC address of the central device + data: Received data bytes + + Returns: + True if data was handled as identity handshake, False otherwise + """ + # Check if we already have peer identity + peer_identity = self.address_to_identity.get(address) + if peer_identity: + return False # Already have identity, not a handshake + + # Identity handshake detection: exactly 16 bytes, no existing identity + if len(data) != 16: + return False # Not a handshake + + try: + # Store central's identity + central_identity = bytes(data) + identity_hash = self._compute_identity_hash(central_identity) + + self.address_to_identity[address] = central_identity + self.identity_to_address[identity_hash] = address + + RNS.log(f"{self} received identity handshake from {address}: {identity_hash}", RNS.LOG_INFO) + + # Get MTU for this connection (should be negotiated by now) + mtu = self.driver.get_peer_mtu(address) + if not mtu: + mtu = 23 # BLE 4.0 minimum MTU + + # Create fragmenter/reassembler + frag_key = self._get_fragmenter_key(central_identity, address) + + with self.frag_lock: + self.fragmenters[frag_key] = BLEFragmenter(mtu=mtu) + if frag_key not in self.reassemblers: + self.reassemblers[frag_key] = BLEReassembler() + + # Spawn peer interface if not already spawned + if identity_hash not in self.spawned_interfaces: + peer_name = f"Central-{address[-8:]}" + connection_type = "peripheral" # We're the peripheral + + self._spawn_peer_interface( + address=address, + name=peer_name, + peer_identity=central_identity, + mtu=mtu, + connection_type=connection_type + ) + + RNS.log(f"{self} identity handshake complete for {address}", RNS.LOG_INFO) + return True # Handshake processed successfully + + except Exception as e: + RNS.log(f"{self} failed to process identity handshake from {address}: {e}", RNS.LOG_ERROR) + return True # Still consumed the data, don't pass it on + def _data_received_callback(self, address: str, data: bytes): """ Driver callback: Handle received data from peer. - Passes data to reassembly and routing logic. + First checks for identity handshake (peripheral role), then passes + normal data to reassembly and routing logic. """ + # Handle identity handshake if applicable + if self._handle_identity_handshake(address, data): + return # Handshake handled, done + + # Normal data processing self._handle_ble_data(address, data) def _device_disconnected_callback(self, address: str):