From 622ccc7de4f77bdcbd4631264d4087b7100c5616 Mon Sep 17 00:00:00 2001 From: torlando-tech Date: Tue, 13 Jan 2026 16:45:37 -0500 Subject: [PATCH] fix(ble): update BLEPeerInterface.peer_address on MAC rotation When BLE MAC address rotation occurs (same identity, different address), the BLEPeerInterface.peer_address field was not being updated. This caused sends to fail with "Cannot send - not connected" because Python was using the stale address that no longer matched Kotlin's connectedPeers map. This fix updates peer_address in all code paths where MAC rotation can occur: - _mtu_negotiated_callback: when interface already exists for identity - _handle_identity_handshake: when interface already exists for identity - _address_changed_callback: when address migration is triggered - _spawn_peer_interface: when reusing existing interface for new address Fixes bidirectional BLE communication failure where peripheral could not send data to central after MAC rotation. Co-Authored-By: Claude Opus 4.5 --- src/ble_reticulum/BLEInterface.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/ble_reticulum/BLEInterface.py b/src/ble_reticulum/BLEInterface.py index b576ccc..c361353 100644 --- a/src/ble_reticulum/BLEInterface.py +++ b/src/ble_reticulum/BLEInterface.py @@ -1096,6 +1096,14 @@ class BLEInterface(Interface): mtu=mtu, connection_type=connection_type ) + else: + # Interface already exists - update address mapping for MAC rotation + existing_if = self.spawned_interfaces[identity_hash] + old_addr = existing_if.peer_address + if old_addr != address: + existing_if.peer_address = address + self.address_to_interface[address] = existing_if + RNS.log(f"{self} updated peer interface address for MAC rotation: {old_addr} -> {address}", RNS.LOG_DEBUG) def _handle_identity_handshake(self, address: str, data: bytes) -> bool: """ @@ -1156,6 +1164,14 @@ class BLEInterface(Interface): mtu=mtu, connection_type=connection_type ) + else: + # Interface already exists - update address mapping for MAC rotation + existing_if = self.spawned_interfaces[identity_hash] + old_addr = existing_if.peer_address + if old_addr != address: + existing_if.peer_address = address + self.address_to_interface[address] = existing_if + RNS.log(f"{self} updated peer interface address for MAC rotation: {old_addr} -> {address}", RNS.LOG_DEBUG) RNS.log(f"{self} identity handshake complete for {address}", RNS.LOG_INFO) @@ -1340,6 +1356,9 @@ class BLEInterface(Interface): if old_address in self.address_to_interface: interface = self.address_to_interface.pop(old_address) self.address_to_interface[new_address] = interface + # CRITICAL: Update the interface's peer_address so send() uses correct address + interface.peer_address = new_address + RNS.log(f"{self} updated peer interface address for MAC rotation: {old_address} -> {new_address}", RNS.LOG_DEBUG) # Migrate fragmenter/reassembler from old to new key old_frag_key = self._get_fragmenter_key(peer_identity, old_address) @@ -1798,6 +1817,11 @@ class BLEInterface(Interface): self.address_to_interface[address] = existing_if self.address_to_identity[address] = peer_identity self.identity_to_address[identity_hash] = address + # CRITICAL: Update the interface's peer_address so send() uses correct address + old_addr = existing_if.peer_address + if old_addr != address: + existing_if.peer_address = address + RNS.log(f"{self} updated peer interface address for MAC rotation: {old_addr} -> {address}", RNS.LOG_DEBUG) # Cancel any pending detach for this identity - new connection arrived if identity_hash in self._pending_detach: del self._pending_detach[identity_hash]