fix(ble): Restore identity handshake detection for peripheral connections

## 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 <noreply@anthropic.com>
This commit is contained in:
torlando-tech 2025-11-04 00:58:56 -05:00
commit 88bb2fc3fa

View file

@ -738,12 +738,85 @@ class BLEInterface(Interface):
connection_type=connection_type 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): def _data_received_callback(self, address: str, data: bytes):
""" """
Driver callback: Handle received data from peer. 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) self._handle_ble_data(address, data)
def _device_disconnected_callback(self, address: str): def _device_disconnected_callback(self, address: str):