Skip to content

Fix bonded reconnect: connect to the visible address, not the flipped identity - #4

Open
vkolotov wants to merge 1 commit into
sgothel:masterfrom
vkolotov:bonded-reconnect-visible-address
Open

Fix bonded reconnect: connect to the visible address, not the flipped identity#4
vkolotov wants to merge 1 commit into
sgothel:masterfrom
vkolotov:bonded-reconnect-visible-address

Conversation

@vkolotov

@vkolotov vkolotov commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Problem

When a peer distributes an IRK + identity address during SMP pairing, BTDevice::updateIdentityAddress flips the tracked address to the identity (typically advertised RANDOM -> identity PUBLIC). But the controller is never given the IRK — the resolving list is not used (the acknowledged // TODO: Shall we support 'resolving list' ... in BTDevice::connectLE) — so the next le_create_conn targets the identity address the peer is not advertising under. The connection never establishes and every reconnect to a bonded LE device loops forever.

Reproducible with any peripheral that distributes an identity during bonding (e.g. an nRF52 SoftDevice peripheral, which always sets kdist_own.id).

Fix — software resolution using machinery that already exists

Direct-BT already resolves addresses in software on the discovery side: BTAdapter::findDevice matches a fresh advertisement against the visible or identity address (RPAs via matches_irk), keeps visibleAddressAndType current, and setResolvHCIConnectionAddr maps HCI events back to the identity. The only gap was the connect path.

  • BTDevice::connectLE: when the tracked identity differs from the visible (advertised) address, issue le_create_conn to the visible address and re-register setResolvHCIConnectionAddr(visible, identity) so events map back to the identity. This mirrors what the Linux kernel does when LL privacy is unavailable.
  • L2CAPClient::open: connect the ATT channel to the address the kernel tracks the ACL under (new BTDevice::getKernelConnectionAddressAndType()). The kernel rewrites its hci_conn destination to the bonded identity only where it can resolve an RPA via the peer IRK; for a static-random visible address it keeps the visible address, and an l2cap connect to the identity would miss the connection.

Validation

Tested live against an nRF52840 peripheral (static random address, distributes a public identity, both Just-Works and Passkey-Entry pairing): bonded reconnects now establish reliably after forced link drops and host restarts (previously: infinite reconnect loop). Unbonded devices are unaffected (connect_address == addressAndType keeps the existing behaviour).

Note: a peer that switches to advertising under its identity address while an older visible address is still tracked will not reconnect until re-discovery refreshes the visible address; this is documented at the connect site and does not regress current behaviour.

… identity

A peer that distributes an IRK + identity address during SMP has its tracked
address flipped to the identity (updateIdentityAddress), but the controller is
never given the IRK (no resolving-list support, BTDevice.cpp TODO), so the next
le_create_conn targets an address the peer is not advertising under and the
reconnect loops forever.

Software resolution instead of controller resolution (the same strategy the
Linux kernel uses when LL privacy is unavailable):

- connectLE(): when the tracked identity differs from the visible (advertised)
  address, issue le_create_conn to the visible address and register
  setResolvHCIConnectionAddr(visible, identity) so HCI events map back to the
  identity. BTAdapter::findDevice already resolves fresh advertisements (RPA
  via matches_irk, static random via the visible match) and keeps
  visibleAddressAndType current, so the connect target is always reachable.

- L2CAPClient::open(): connect the ATT channel to the address the kernel
  tracks the ACL under (new BTDevice::getKernelConnectionAddressAndType()):
  the kernel rewrites its hci_conn dst to the bonded identity only where it
  can resolve an RPA via the peer IRK; for a static-random visible address it
  keeps the visible address, and an l2cap connect to the identity would miss
  the connection.

Validated live (nRF52840 peripheral, static random address distributing a
public identity): Just-Works and Passkey-Entry both reconnect encrypted after
link drops (previously 21 failed-reconnect loops per window), no regression on
unbonded devices.
@vkolotov
vkolotov force-pushed the bonded-reconnect-visible-address branch from 52f7236 to a0fd62a Compare August 17, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant