fix: HW_MTU instance attribute and MTU/identity race condition

Two critical fixes ported from Android testing:

1. HW_MTU instance attribute fix:
   - Base Interface.__init__() sets self.HW_MTU = None
   - BLEInterface.HW_MTU = 500 is a CLASS attribute, not instance
   - After super().__init__(), self.HW_MTU is None (instance shadows class)
   - BLEPeerInterface copies: self.HW_MTU = parent.HW_MTU (gets None)
   - When HW_MTU is None, Transport.py truncates packet.data incorrectly
   - Result: Link establishment fails with mismatched link_id
   - Fix: Explicitly set self.HW_MTU = BLEInterface.HW_MTU after super().__init__()

2. MTU/identity race condition fix:
   - MTU negotiation can complete before identity is received
   - Previously: Warning logged, fragmenter not created
   - Now: Store pending MTU, create fragmenter when identity arrives
   - Adds pending_mtu dict to track deferred MTU values
   - _device_connected_callback checks for pending MTU after storing identity

🤖 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-25 18:18:22 -05:00
commit 2ff9ddc4f1

View file

@ -284,6 +284,26 @@ class BLEInterface(Interface):
super().__init__()
# CRITICAL: Set HW_MTU as instance attribute after super().__init__()
#
# Bug explanation:
# - Base Interface.__init__() sets self.HW_MTU = None
# - BLEInterface.HW_MTU = 500 is a CLASS attribute, not instance
# - After super().__init__(), self.HW_MTU is None (instance shadows class)
# - BLEPeerInterface copies: self.HW_MTU = parent.HW_MTU (gets None)
#
# Impact when HW_MTU is None:
# - Transport.py line ~1855 checks: if packet.receiving_interface.HW_MTU == None
# - If true, it TRUNCATES packet.data by 3 bytes (LINK_MTU_SIZE) before
# passing to Link.validate_request()
# - Link.link_id_from_lr_packet() uses len(packet.data) to compute truncation
# - Since packet.data was pre-truncated, it computes WRONG link_id
# - Link proof's destination_hash won't match pending link's link_id
# - Result: Links time out despite proof arriving correctly
#
# This bug ONLY affects BLE because other interfaces set HW_MTU in __init__
self.HW_MTU = BLEInterface.HW_MTU
# Parse configuration
c = Interface.get_config_obj(configuration)
@ -364,6 +384,7 @@ class BLEInterface(Interface):
self.fragmenters = {} # address -> BLEFragmenter (per MTU)
self.reassemblers = {} # address -> BLEReassembler
self.frag_lock = threading.Lock()
self.pending_mtu = {} # address -> mtu (for MTU/identity race condition)
# Discovery state with prioritization
@ -751,7 +772,7 @@ class BLEInterface(Interface):
role = self.driver.get_peer_role(address)
if peer_identity is not None:
# Central mode: identity provided by driver
# Identity provided by driver (central mode direct, peripheral mode via late callback)
if len(peer_identity) == 16:
identity_hash = self._compute_identity_hash(peer_identity)
@ -759,8 +780,15 @@ class BLEInterface(Interface):
self.address_to_identity[address] = peer_identity
self.identity_to_address[identity_hash] = address
RNS.log(f"{self} connected to {address} as CENTRAL, received identity: {identity_hash}", RNS.LOG_INFO)
role_str = role.upper() if role else "UNKNOWN"
RNS.log(f"{self} connected to {address} as {role_str}, received identity: {identity_hash}", RNS.LOG_INFO)
self._record_connection_success(address)
# Check for pending MTU (race condition: MTU negotiated before identity)
if address in self.pending_mtu:
pending_mtu = self.pending_mtu.pop(address)
RNS.log(f"{self} creating deferred fragmenter for {address} (MTU={pending_mtu})", RNS.LOG_DEBUG)
self._mtu_negotiated_callback(address, pending_mtu)
else:
RNS.log(f"{self} invalid identity from {address} (wrong length), disconnecting", RNS.LOG_WARNING)
self.driver.disconnect(address)
@ -819,7 +847,10 @@ class BLEInterface(Interface):
# Get peer identity
peer_identity = self.address_to_identity.get(address)
if not peer_identity:
RNS.log(f"{self} no identity for {address}, cannot create fragmenter", RNS.LOG_WARNING)
# Race condition: MTU negotiated before identity received
# Store pending MTU and create fragmenter when identity arrives
RNS.log(f"{self} no identity for {address}, storing pending MTU {mtu}", RNS.LOG_DEBUG)
self.pending_mtu[address] = mtu
return
# Create or update fragmenter
@ -980,6 +1011,10 @@ class BLEInterface(Interface):
if frag_key in self.reassemblers:
del self.reassemblers[frag_key]
# Clean up pending MTU (from MTU/identity race condition)
if address in self.pending_mtu:
del self.pending_mtu[address]
def _error_callback(self, severity: str, message: str, exc: Exception = None):
"""
Driver callback: Handle driver errors.