bluez / bluez

Main BlueZ tree
https://bluez.github.io/bluez/
GNU General Public License v2.0
738 stars 274 forks source link

[BlueZ 5.72] `LE Set Scan Parameters` sets Own address type as Random when it should be Public #761

Closed gudnimg closed 3 months ago

gudnimg commented 7 months ago

This is more of question than a bug report, I've been investigating this problem for a while now and am desperate for advice. I've seen the problem since BlueZ 5.43, 5.50 and now 5.65 and also in both Linux kernels 4.9 and 6.1. I had hoped the kernel upgrade would resolve this but it didn't. So I'm left with a theory that this has to be some configuration issue.

Setup:

Steps to reproduce:

  1. Plug into a USB port the BT851 adapter: https://www.lairdconnect.com/bt850-bt851-and-bt860-series-modules-adapter-and-dvks it is a dual-mode adapter.
  2. In bluetoothctl run the following in order:
power on
advertise on
scan on

The result happens every time.

image

Problem:

So far I've found from the Bluetooth specification that the problem is that the advertisement is advertising the public address. And when the scan is enabled, BlueZ attempts to scan using a random address. But because advertisement is ongoing, setting the random address is forbidden.

What I don't understand is WHY is the scan trying to use a random address? In my application, using Public address is fine. I don't see any way to configure this via D-bus interface.

< HCI Command: LE Set Advertising Parameters (0x08|0x0006) plen 15                                                                                                                                  #97 [hci0] 17.260192
        Min advertising interval: 1280.000 msec (0x0800)
        Max advertising interval: 1280.000 msec (0x0800)
        Type: Connectable undirected - ADV_IND (0x00)
        Own address type: Public (0x00)                       <--- PUBLIC, this is good
        Direct address type: Public (0x00)
        Direct address: 00:00:00:00:00:00 (OUI 00-00-00)
        Channel map: 37, 38, 39 (0x07)
        Filter policy: Allow Scan Request from Any, Allow Connect Request from Any (0x00)
@ MGMT Command: Start Discovery (0x0023) plen 1                                                                                                                                                {0x0001} [hci0] 22.185908
        Address type: 0x07
          BR/EDR
          LE Public
          LE Random
< HCI Command: LE Set Scan Parameters (0x08|0x000b) plen 7                                                                                                                                         #101 [hci0] 22.186155
        Type: Active (0x01)
        Interval: 11.250 msec (0x0012)
        Window: 11.250 msec (0x0012)
        Own address type: Random (0x01)                        <--- RANDOM, Why is this not PUBLIC?
        Filter policy: Accept all advertisement (0x00)

btmon logs

btmon_log.txt

gudnimg commented 7 months ago

To add to this, I have found so far the hcitool lescan does scan using the public address. I don't understand what is different when using bluetoothctl (even after looking at the source code).

image

Vudentz commented 7 months ago

@gudnimg do you have Privacy enabled by any chance (main.conf:Privacy)? Btw, 6.1 is not that recent by any means:

tag v6.1 Tagger: Linus Torvalds torvalds@linux-foundation.org Date: Sun Dec 11 14:15:18 2022 -0800

Btw, have you tried just doing > scan on rather than doing >advertise on as well? Or you really want to be both central and peripheral at the same time?

gudnimg commented 7 months ago

@Vudentz thanks for the reply :)

do you have Privacy enabled by any chance (main.conf:Privacy)

No, I've tried to add main.conf to set Privacy to off explicitly, but it doesnt impact the issue. Using btmon, while bluetoothctl power on is called, I can verify that Privacy is set off (I assume by BlueZ or the Linux Kernel).

Btw, have you tried just doing > scan on rather than doing >advertise on as well? Or you really want to be both central and peripheral at the same time?

Yes, the problem only appears when I call advertise on, before scan on. We use the RegisterAdvertisement. Meanwhile the user can trigger a scan anytime.

When advertise on is omitted, I can see that scan on will trigger a call to set the random address command, but for our use case the public address is fine (for both LE and BR/EDR) and would actually resolve this issue. As far as I can see there is no way to change this via the D-bus interface.

Btw, 6.1 is not that recent by any means:

tag v6.1 Tagger: Linus Torvalds torvalds@linux-foundation.org Date: Sun Dec 11 14:15:18 2022 -0800

I'm aware of that 😄 , for now I'm migrating a project from Kernel 4.9 which is quite ancient, but the BlueZ issue I'm seeing behaves the same on Kernel 4.9 and 6.1. Also between BlueZ 5.43 and 5.65.

gudnimg commented 7 months ago

Or you really want to be both central and peripheral at the same time?

@Vudentz Sorry I didn't answer this question before. But I believe the answer is yes.

The Bluetooth adapter is Central in order to connect to legacy bluetooth classic devices. It is also Peripheral because a BLE Android device can pair with it (through the LE advertisement)


A minor update on my use case:


Is there anything else I could try? I'd be happy to share more logs/info if that helps.

gudnimg commented 4 months ago

Minor update. I have updated my project to the latest Yocto LTS (5.0) which installs BlueZ 5.72 and Linux kernel 6.6. No change in behavior.

gudnimg commented 4 months ago

Attached is the btmon log from BlueZ 5.72 setup when I run from bluetoothctl:

power on
advertise on
scan on

bluez_5_72_btmon.log

Below is the main.conf file I'm using in case it provides context. I had to zip it because Github didnt accept a .conf file main_conf.zip

gudnimg commented 4 months ago

I think I may have found something, but I could be totally wrong though.

This if statment in the Linux kernel looks suspicious to me: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/bluetooth/hci_sync.c?h=v6.6.34#n5652 I would think scanning wouldnt require a random address at all. Privacy feature is explicitly disabled, but this code will be run anyway from what I can see.

        /* All active scans will be done with either a resolvable private
     * address (when privacy feature has been enabled) or non-resolvable
     * private address.
     */
    err = hci_update_random_address_sync(hdev, true, scan_use_rpa(hdev),
                         &own_addr_type);
    if (err < 0)
        own_addr_type = ADDR_LE_DEV_PUBLIC;

Since I'm advertising, hci_update_random_address_sync will return 0.

This code has been unchanged between Linux kernels 4.9 and 6.6.34... so matches all the kernels I've tested. But now I wonder if I'm the one who is wrong, surely if this was incorrect it would be caught already.

Just for reference, here is the same if statement in Linux kernel 4.9: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/bluetooth/hci_request.c?h=v4.9#n2025

I looked up the Bluetooth 5.0 Core specification and it explicitly says this only applies when Privacy feature is enabled: image


I'll enable debug logs in my kernel later and add a patch in my local Yocto build to see if this theory is correct. I'm in no way a Bluetooth expert, but I'm curious.

Maybe this isn't a BlueZ specific bug after all. 🤔

gudnimg commented 4 months ago

This patch resolves the problem when I test on Linux 6.6 with BlueZ 5.72:

0001-Bluetooth-hci_sync-Fix-own-address-type-in-active-sc.patch

---
 net/bluetooth/hci_sync.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index d7ca5bd8ba3b..7b96f5a9c12c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5617,7 +5617,8 @@ static int hci_inquiry_sync(struct hci_dev *hdev, u8 length)

 static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
 {
-   u8 own_addr_type;
+   /* Own address type is Public by default */
+   u8 own_addr_type = ADDR_LE_DEV_PUBLIC;
    /* Accept list is not used for discovery */
    u8 filter_policy = 0x00;
    /* Default is to enable duplicates filter */
@@ -5645,14 +5646,16 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
    if (err)
        goto failed;

-   /* All active scans will be done with either a resolvable private
-    * address (when privacy feature has been enabled) or non-resolvable
+   /* Active scans with privacy enabled will be done with either a resolvable
+    * private address (when privacy feature has been enabled) or non-resolvable
     * private address.
     */
-   err = hci_update_random_address_sync(hdev, true, scan_use_rpa(hdev),
-                        &own_addr_type);
-   if (err < 0)
-       own_addr_type = ADDR_LE_DEV_PUBLIC;
+   if (use_ll_privacy(hdev)) {
+       err = hci_update_random_address_sync(hdev, true, scan_use_rpa(hdev),
+                            &own_addr_type);
+       if (err < 0)
+           own_addr_type = ADDR_LE_DEV_PUBLIC;
+   }

    if (hci_is_adv_monitoring(hdev)) {
        /* Duplicate filter should be disabled when some advertisement
-- 
2.34.1

image

Vudentz commented 4 months ago

We use a private address not to blast our own address over the air thus the use of NRPA, Im not sure why it was not clear from the comments.

Vudentz commented 4 months ago

This patch resolves the problem when I test on Linux 6.6 with BlueZ 5.72:

0001-Bluetooth-hci_sync-Fix-own-address-type-in-active-sc.patch

---
 net/bluetooth/hci_sync.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index d7ca5bd8ba3b..7b96f5a9c12c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5617,7 +5617,8 @@ static int hci_inquiry_sync(struct hci_dev *hdev, u8 length)

 static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
 {
- u8 own_addr_type;
+ /* Own address type is Public by default */
+ u8 own_addr_type = ADDR_LE_DEV_PUBLIC;
  /* Accept list is not used for discovery */
  u8 filter_policy = 0x00;
  /* Default is to enable duplicates filter */
@@ -5645,14 +5646,16 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
  if (err)
      goto failed;

- /* All active scans will be done with either a resolvable private
-  * address (when privacy feature has been enabled) or non-resolvable
+ /* Active scans with privacy enabled will be done with either a resolvable
+  * private address (when privacy feature has been enabled) or non-resolvable
   * private address.
   */
- err = hci_update_random_address_sync(hdev, true, scan_use_rpa(hdev),
-                      &own_addr_type);
- if (err < 0)
-     own_addr_type = ADDR_LE_DEV_PUBLIC;
+ if (use_ll_privacy(hdev)) {
+     err = hci_update_random_address_sync(hdev, true, scan_use_rpa(hdev),
+                          &own_addr_type);
+     if (err < 0)
+         own_addr_type = ADDR_LE_DEV_PUBLIC;
+ }

  if (hci_is_adv_monitoring(hdev)) {
      /* Duplicate filter should be disabled when some advertisement
-- 
2.34.1

use_ll_privacy is not the same as local privacy, it refer to link-layer privacy or address resolution in the controller. Anyway I don't think there is anything wrong with usage of NRPA even in case of local privacy is not enabled, anyway Im planning to enable privacy by default now that most controllers seems to be working properly.

gudnimg commented 4 months ago

I’m not sure using a random address / privacy will work in my case. I’m using a dual mode controller to support BR/EDR. It’s my understanding dual mode controller should always use the public address.

gudnimg commented 4 months ago

Now that I know where the “problem” is in the code. I’ll read more about the privacy feature.

I don’t think scanning with a random address is a problem for the LE side. But I require the LE advertisement to use Public address. If I recall correctly that didnt work at all on BlueZ 5.43… I should re-test it on BlueZ 5.72. I can also upgrade BlueZ further if it builds without issues on Yocto. :)

Vudentz commented 4 months ago

Now that I know where the “problem” is in the code. I’ll read more about the privacy feature.

I don’t think scanning with a random address is a problem for the LE side. But I require the LE advertisement to use Public address. If I recall correctly that didnt work at all on BlueZ 5.43… I should re-test it on BlueZ 5.72. I can also upgrade BlueZ further if it builds without issues on Yocto. :)

Afaik if you set the advertising instance as connectable, and don't have privacy enabled, then it shall use the public address. If you mark as connectable and have privacy enabled then it would use an RPA.

gudnimg commented 3 months ago

@Vudentz I haven't tested Privacy on BlueZ in very much detail yet, specifically with Bluetooth classic. A bit off topic but I accidentally broke the USB interface on my Linux kernel after I tried to optimize the kernel size a bit too aggressively, I got it fixed now though (this is why I haven't replied sooner).

Anyway, back to the problem/topic, it seems that enabling local Privacy on the dual controller fixes the main problem I described in this Github issue. I can at least confirm all the LE functionality is working great -- that is I can advertise, scan, pair and connect etc.. Although it does feel like it's only a workaround IMO since the problem returns if Privacy is turned off.

I'm thinking it's time to close this ticket since Public address in BLE active scanning isn't really supported? I'm not sure how to move this issue forward. You said earlier Privacy should be enabled by default, I will trust your judgement.


Idea

I had one idea which could maybe resolve this issue... but I'm not sure if it violates the Bluetooth specification:

When hci_active_scan_sync() is executed, always disable advertisement while changing the scan parameters. Currently it is only done if link layer privacy is enabled (see hci_pause_addr_resolution()). When Privacy=off in main.conf the link layer privacy is off too and so advertising is not disabled and active scan attempts to scan without any random address set.

Disabling advertising will allow the kernel to set the random address in hci_update_random_address_sync() and allow the active scan to start. If advertisement is not disabled, then the random address is never set (it's forbidden by the specification) and the error I described in this Github issue appears.

gudnimg commented 3 months ago

Closing this for now. The issue is gone once I enabled Privacy in main.conf.

From my understanding this is not a BlueZ specific issue, but an issue on the kernel side. I don’t want to clutter the issue tracker here :)