mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with
@ 2026-09-08 22:29 Radek Podgorny
  2026-09-08 22:29 ` [PATCH v3 1/2] Bluetooth: forget a peer's RPA once it advertises its identity address Radek Podgorny
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Radek Podgorny @ 2026-09-08 22:29 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-kernel, Luiz Augusto von Dentz, Radek Podgorny

An identity address only reaches a peer advertising an RPA if the
controller resolves it for us; where it cannot, the host has to dial the
peer's on-air address itself. v2 did that by keeping the RPA in conn->dst
when the controller will not translate, which Luiz asked for and which
holds for as long as the peer is still using that RPA.

It does not hold once a peer turns privacy off. hci_connect_le() swaps in
the cached RPA whenever the IRK has one, nothing ever clears that cache,
and a peer that stops sending RPAs stops producing the reports that would
refresh it. Before this series the conversion in __hci_conn_add() hid the
problem by turning the stale RPA back into the identity address; keeping
the RPA exposes it, and every connect attempt goes to an address the peer
has abandoned.

So patch 1 drops a cached RPA as soon as the peer is seen advertising its
identity address, and patch 2 is the v2 change on top of it. In that order
neither commit leaves the tree with the regression.

Not addressed here, by agreement: hci_conn_params_lookup() in
hci_le_create_conn_sync() is keyed by identity address and misses when
conn->dst holds an RPA, so the connection falls back to the default
intervals. That is the pre-14b06c3a88f7 behaviour and is better fixed on
top than folded in.

Measurements, hardware and limitations are unchanged from v2 and are
described in patch 2.

Changes in v3:
 - Add "forget a peer's RPA once it advertises its identity address"
   ahead of the address-selection change, after Luiz pointed out that a
   peer that disabled privacy should be dialled on its identity address
   rather than on a stale RPA.
 - Note the dependency between the two patches in patch 2's changelog.
 - Link to v2: https://lore.kernel.org/linux-bluetooth/20260908-for-upstream-le-connect-on-air-addr-v2-1-2ae9fa066ec8@podgorny.cz/

Changes in v2:
 - Move the address choice into __hci_conn_add() rather than translating
   at the create-connection command builders, as suggested by Luiz.
 - Drop the "record when an IRK's RPA was last seen" patch and the
   freshness window with it; hci_find_irk_by_rpa() already refreshes
   irk->rpa on every advertising report the host resolves.
 - Link to v1: https://lore.kernel.org/linux-bluetooth/20260908012048.3681904-2-radek@podgorny.cz/

Per Documentation/process/generated-content.rst: developed with the help
of the Claude coding assistant, as recorded in the Assisted-by trailers.
The prompts described the misdirected create-connection, the btmon
captures taken before and after, and the review feedback on v1 and v2; the
assistant was asked to locate where the dialled address is chosen, to
trace what invalidates a cached RPA, and to draft the changes and their
commit messages. The measurements, the hardware testing and the final
wording are the author's own.

Signed-off-by: Radek Podgorny <radek@podgorny.cz>
---
Radek Podgorny (2):
      Bluetooth: forget a peer's RPA once it advertises its identity address
      Bluetooth: put the peer's on-air address on air when we cannot resolve

 net/bluetooth/hci_conn.c  | 13 +++++++++++++
 net/bluetooth/hci_event.c |  9 +++++++++
 2 files changed, 22 insertions(+)
---
base-commit: 701ca71884b3d101fd25b7adbf972355056ef352
change-id: 20260908-for-upstream-le-connect-on-air-addr-a796ae625007

Best regards,
--  
Radek Podgorny <radek@podgorny.cz>


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 1/2] Bluetooth: forget a peer's RPA once it advertises its identity address
  2026-09-08 22:29 [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with Radek Podgorny
@ 2026-09-08 22:29 ` Radek Podgorny
  2026-09-08 22:29 ` [PATCH v3 2/2] Bluetooth: put the peer's on-air address on air when we cannot resolve Radek Podgorny
  2026-09-09 19:40 ` [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with patchwork-bot+bluetooth
  2 siblings, 0 replies; 4+ messages in thread
From: Radek Podgorny @ 2026-09-08 22:29 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-kernel, Luiz Augusto von Dentz, Radek Podgorny

hci_connect_le() dials the RPA cached in the peer's IRK whenever one is
set, on the assumption that a peer holding an IRK is on air with a
resolvable private address. A peer that stops using privacy breaks that
assumption: it advertises its identity address, the cached RPA keeps the
value it had before the change, and the host aims at an address the peer
has abandoned.

Nothing clears the cache. hci_find_irk_by_rpa() refreshes irk->rpa each
time an advertisement resolves, so it tracks rotation, but a peer that
stops sending RPAs stops producing the reports that would update it, and
the stale address then survives until the adapter is powered off.

On the path that creates the connection object this is currently masked:
__hci_conn_add() resolves the cached RPA back to the identity address, so
that is what goes on air. It is not masked on the reuse branch, which
copies the swapped address straight into an existing conn->dst, and the
next patch removes the conversion for the case where the controller
cannot translate an identity address, so the stale RPA would be dialled
there too.

Clear the cached RPA when the peer is seen on its identity address.
hci_find_irk_by_addr() only matches public and static random addresses,
so an unresolved RPA belonging to some other device cannot reach this
path.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Radek Podgorny <radek@podgorny.cz>
---
 net/bluetooth/hci_event.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 2f5e21ff9752..03b805207ab0 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -6311,6 +6311,15 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
 	if (irk) {
 		bdaddr = &irk->bdaddr;
 		bdaddr_type = irk->addr_type;
+	} else {
+		/* The peer is on air with its identity address, so whatever
+		 * RPA is cached for it has been abandoned. Drop it, or
+		 * hci_connect_le() would swap it back in and dial an address
+		 * the peer no longer answers.
+		 */
+		irk = hci_find_irk_by_addr(hdev, bdaddr, bdaddr_type);
+		if (irk)
+			bacpy(&irk->rpa, BDADDR_ANY);
 	}
 
 	bdaddr_type = ev_bdaddr_type(hdev, bdaddr_type, &bdaddr_resolved);

-- 
2.55.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v3 2/2] Bluetooth: put the peer's on-air address on air when we cannot resolve
  2026-09-08 22:29 [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with Radek Podgorny
  2026-09-08 22:29 ` [PATCH v3 1/2] Bluetooth: forget a peer's RPA once it advertises its identity address Radek Podgorny
@ 2026-09-08 22:29 ` Radek Podgorny
  2026-09-09 19:40 ` [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with patchwork-bot+bluetooth
  2 siblings, 0 replies; 4+ messages in thread
From: Radek Podgorny @ 2026-09-08 22:29 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz
  Cc: linux-bluetooth, linux-kernel, Luiz Augusto von Dentz, Radek Podgorny

An identity address only reaches a peer that is advertising an RPA if the
controller resolves it on our behalf. Where it cannot, the host has to put
the peer's on-air address on air itself.

hci_connect_le() still swaps the caller's identity address for the peer's
cached RPA before creating the connection, but __hci_conn_add() resolves
the RPA back to the identity address when it stores it, so the identity is
what goes out. Storing the identity is right when the controller
translates it on the way to the radio; without LL Privacy, or with this
peer absent from the resolving list, nothing does.

A peer advertising an RPA cannot answer its identity address, so the
attempt burns a full create-connection timeout. That is not merely a slow
connect: a controller without extended scanning cannot scan while it is
initiating, so every dead attempt also takes the scanner off the air for
the whole timeout.

Measured on a CYW43438, which reports neither LL Privacy nor extended
advertising (LE features 3f 00 00 08 00 00 00 00), against a peer
advertising a resolvable private address the host holds the IRK for, with
the connection requested on the peer's identity address:

  before: LE Create Connection to the identity address, public type
          1.61s -> 22.07s, then LE Create Connection Cancel
          LE Connection Complete: Unknown Connection Identifier (0x02)
  after:  LE Create Connection to the peer's RPA, random type
          LE Connection Complete: Success

Advertising reports reaching the host per second, same window, same five
unrelated devices on the adapter:

  before   1s:2   [nothing from 2s through 21s]   22s:5  23s:3
  after    0s:11 1s:5 2s:2 3s:5 4s:3 5s:4 ... 21s:2 22s:1 23s:2

One dead connect costs twenty seconds of scanning for every device on the
adapter, not just the one being dialled.

Keep the RPA in conn->dst unless the controller will translate the
identity address: address resolution enabled and the peer's identity
actually programmed into the resolving list. Testing ll_privacy_capable()
alone would not be enough: it reports the feature bit, not whether
resolution is switched on and not whether this peer is in the list.
Resolution is cleared with the other volatile flags on power-off and
switched off again while suspend pauses scanning, and a peer's IRK is only
programmed along the accept list path, so a direct-connect target, a peer
without HCI_CONN_FLAG_ADDRESS_RESOLUTION, and one that did not fit in a
full list are all absent from it.

With the peer programmed, the identity address stays in conn->dst and the
controller translates it: measured on an Intel controller, the host dials
the identity and LE Enhanced Connection Complete reports Resolved Public
with the peer's RPA in the separate peer resolvable private address field.
With the peer absent from the list the same setup dials the RPA itself.

Everything downstream already copes with an RPA in conn->dst: it is what
every outgoing LE connection stored before 14b06c3a88f7, the connection
complete event names the address that was dialled, and
le_conn_complete_evt() resolves it back to the identity once the link is
up. ISO links keep the unconditional conversion: they are created from an
existing ACL or a periodic sync and never dial this address themselves.

Keeping the RPA is only right while the peer is still using it, which is
why the preceding patch drops the cached RPA as soon as the peer is seen
advertising its identity address. Without that, a peer that turns privacy
off would be dialled on the address it abandoned rather than the one it
is answering on.

Fixes: 14b06c3a88f7 ("Bluetooth: HCI: Always use the identity address when initializing a connection")
Assisted-by: Claude:claude-opus-5
Assisted-by: Claude:claude-fable-5
Signed-off-by: Radek Podgorny <radek@podgorny.cz>
---
 net/bluetooth/hci_conn.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8de98af2fb58..c9466cb2c7c0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1023,6 +1023,19 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
 		if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU)
 			return ERR_PTR(-ECONNREFUSED);
 		irk = hci_get_irk(hdev, dst, dst_type);
+		/* An identity address only reaches a peer advertising an RPA
+		 * if the controller translates it. Unless address resolution
+		 * is enabled and this peer is programmed into the resolving
+		 * list, keep the RPA the peer is on air with;
+		 * le_conn_complete_evt() resolves it back once the link is
+		 * up.
+		 */
+		if (irk &&
+		    (!hci_dev_test_flag(hdev, HCI_LL_RPA_RESOLUTION) ||
+		     !hci_bdaddr_list_lookup_with_irk(&hdev->le_resolv_list,
+						      &irk->bdaddr,
+						      irk->addr_type)))
+			irk = NULL;
 		break;
 	case SCO_LINK:
 	case ESCO_LINK:

-- 
2.55.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with
  2026-09-08 22:29 [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with Radek Podgorny
  2026-09-08 22:29 ` [PATCH v3 1/2] Bluetooth: forget a peer's RPA once it advertises its identity address Radek Podgorny
  2026-09-08 22:29 ` [PATCH v3 2/2] Bluetooth: put the peer's on-air address on air when we cannot resolve Radek Podgorny
@ 2026-09-09 19:40 ` patchwork-bot+bluetooth
  2 siblings, 0 replies; 4+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-09 19:40 UTC (permalink / raw)
  To: Radek Podgorny
  Cc: marcel, luiz.dentz, linux-bluetooth, linux-kernel, luiz.von.dentz

Hello:

This series was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Wed, 09 Sep 2026 00:29:35 +0200 you wrote:
> An identity address only reaches a peer advertising an RPA if the
> controller resolves it for us; where it cannot, the host has to dial the
> peer's on-air address itself. v2 did that by keeping the RPA in conn->dst
> when the controller will not translate, which Luiz asked for and which
> holds for as long as the peer is still using that RPA.
> 
> It does not hold once a peer turns privacy off. hci_connect_le() swaps in
> the cached RPA whenever the IRK has one, nothing ever clears that cache,
> and a peer that stops sending RPAs stops producing the reports that would
> refresh it. Before this series the conversion in __hci_conn_add() hid the
> problem by turning the stale RPA back into the identity address; keeping
> the RPA exposes it, and every connect attempt goes to an address the peer
> has abandoned.
> 
> [...]

Here is the summary with links:
  - [v3,1/2] Bluetooth: forget a peer's RPA once it advertises its identity address
    https://git.kernel.org/bluetooth/bluetooth-next/c/fb3a817741b1
  - [v3,2/2] Bluetooth: put the peer's on-air address on air when we cannot resolve
    https://git.kernel.org/bluetooth/bluetooth-next/c/e35544d23359

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-09 19:41 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-08 22:29 [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with Radek Podgorny
2026-09-08 22:29 ` [PATCH v3 1/2] Bluetooth: forget a peer's RPA once it advertises its identity address Radek Podgorny
2026-09-08 22:29 ` [PATCH v3 2/2] Bluetooth: put the peer's on-air address on air when we cannot resolve Radek Podgorny
2026-09-09 19:40 ` [PATCH v3 0/2] Bluetooth: dial the address the peer is actually on air with patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®