Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MTU exchange takes affect under windows but not under linux #1227

Closed
zapta opened this issue Feb 18, 2023 · 14 comments
Closed

MTU exchange takes affect under windows but not under linux #1227

zapta opened this issue Feb 18, 2023 · 14 comments
Labels
3rd party issue The issue is with the Bluetooth stack, the BLE device, or other 3rd party code not with Bleak itself Backend: BlueZ Issues and PRs relating to the BlueZ backend

Comments

@zapta
Copy link

zapta commented Feb 18, 2023

bleak version: 0.19.5
Python version: 3.10.6
Operating System: Ubuntu 22.04.1 LTS
BlueZ version (bluetoothctl -v) in case of Linux: bluetoothctl: 5.64

Description

I encountered another difference between windows and linux. The test program below runs against a device that is supposed to set MTU to 247. The setting works well when I run the bleak test program on windows and I can see packets at size > 200, but when the bleak test program runs on linux, against the same device, the MTU increase doesn't take affect and the device fragments the packets into smaller ones. I don't not understand the BLE protocol well enough to pinpoint what exactly causes that difference in behavior.

What I Did

Test program

import asyncio
import platform
import sys
import time
import signal
from bleak import BleakClient, BleakScanner
import signal

# Adapt to your actual device.
device_address = "0C:8B:95:F2:B4:36"

signal.signal(signal.SIGINT, lambda number, frame: sys.exit())

print(f"OS: {platform.platform()}", flush=True)
print(f"Platform:: {platform.uname()}", flush=True)
print(f"Python {sys.version}", flush=True)

async def test():
    print(f"Trying to connect to {device_address}", flush=True)
    device = await BleakScanner.find_device_by_address(device_address, timeout=10.0)
    assert device
    async with BleakClient(device) as client:
        assert client.is_connected
        print(f"Connected", flush=True)
        service = client.services.get_service("6b6a78d7-8ee0-4a26-ba7b-62e357dd9720")
        assert(service)
        print(f"Found service", flush=True)
        command_chrc = service.get_characteristic("ff06")
        assert(command_chrc)
        capture_chrc = service.get_characteristic("ff07")
        assert(capture_chrc)
        print(f"Found characteristics", flush=True)
        for i in range(5):
          print(f"Sending command", flush=True)
          await client.write_gatt_char(command_chrc, bytearray([0x02]))
          print(f"Reading", flush=True)
          bytes = await client.read_gatt_char(capture_chrc)
          print(f"Read1: {len(bytes)}", flush=True)
          bytes = await client.read_gatt_char(capture_chrc)
          print(f"Read2: {len(bytes)}", flush=True)
          #await asyncio.sleep(3)

asyncio.run(test())

Logs

Logs when running the test program on linux. MTU doesn't take affect and packets are fragmented.
mtu_linux.zip
mtu_linux.txt

Logs when running the test program on windows. Packets are not fragmented (good).
mtu_windows.zip

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

The Linux packet capture shows the MTU exchange:

image

The remote device seems to be doing the fragmenting of packets.

I don't see how Bleak could have any affect on this.

@dlech dlech added 3rd party issue The issue is with the Bluetooth stack, the BLE device, or other 3rd party code not with Bleak itself Backend: BlueZ Issues and PRs relating to the BlueZ backend labels Feb 18, 2023
@zapta
Copy link
Author

zapta commented Feb 18, 2023

Thanks @dlech. Do you see anything out of the ordinary in the connection and MTU exchange packets that may hint why the device fragments the packets?

(Trying to find a hint, how to approach here. On the device, the BLE stack reports to the app an MTU update event with value of 247 as expected).

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

I would look into the Bluetooth stack on the remote device to see how it works.

@zapta
Copy link
Author

zapta commented Feb 18, 2023

I tested the same bleak test program but with a very different device and BLE stack (Nordic nRF52, vs ESP32) and am getting same results. With bleak/linux the device fragments. Here is the packets capture.

mtu_nrf52_linux.zip

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

On Windows, the remote device is sending a remote Connection Parameter Update Request which results in PDUs exchanged every 40ms after this.

image

I'm not seeing the same on Linux.

On Windows, the central is sending the MTU exchange first while on Linux the peripheral is initiating the exchange.

So I'm guessing there is some sort of timing difference or hardware difference between Windows and Linux that triggers a different behavoir on the remote device.

@zapta
Copy link
Author

zapta commented Feb 18, 2023

Thanks @dlech . I also tested the ESP32 device with a Nordic phone app as a client and no fragmentation.

Considering that the exact same problem exist with both ESP32 stack and nRF stack suggests that it's not the device but something on the linux side.

Are you aware of any BLE related forum where I can post the packets and ask for another set of eyes to look at it?

I will also try the zephyr forums since the nRF device is zephyr based. They have a few BLE experts there.

mtu_with_nrf_client.zip

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

I think I found the answer now. On Linux, the adapter does not have the "LE Data Packet Length Extension" feature flag

image

But the adapter on Windows does.

image

This flags controls whether data has to be fragmented or not according to https://stackoverflow.com/a/71512081/1976323.

A controller might also receive a large L2CAP packet from the host and then split it into multiple fragments to be sent over the Link Layer, if the remote device does not support the LE Data Length extension.

(the other flags also explain the other differences I noted)

@zapta
Copy link
Author

zapta commented Feb 18, 2023

I think you are right. If I got it correctly, you are referring to the feature negotiation.

On linux it is initiated by the PC
image

And on windows it is initiated by the device
image

Assuming that on Linux it's the PC that starts the feature negotiation, any idea how to make it to allow data packet length extension? I presume that this is not hardware restriction, since the hardware is new and this sounds as basic functionality.

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

I'm pretty sure anything starting with "LL" (link layer) is something that is manged by the radio itself and not generally controlled by the host, at least not directly.

@zapta
Copy link
Author

zapta commented Feb 18, 2023 via email

@dlech
Copy link
Collaborator

dlech commented Feb 18, 2023

Yes. BlueZ also has a github page. However, I already look in the BlueZ source code and LL_FEATURE_REQ is not used other than by the packet monitor for decoding packets. And it doesn't seem to be used in the Linux kernel either.

@zapta
Copy link
Author

zapta commented Feb 18, 2023

Thanks @dlech. I just filed this bluez issue bluez/bluez#480

@zapta
Copy link
Author

zapta commented Feb 19, 2023

Digging more brought up the hciconfig -a Linux commands. The Features line may indicate the capabilities of linux interface. Bits may correspond to the 8 bytes that are sent in the LL_FEATURE_REQ but I am not sure.

$ hciconfig -a
hci0: Type: Primary Bus: USB
BD Address: EC:63:D7:F1:C7:8B ACL MTU: 1021:5 SCO MTU: 96:6
UP RUNNING
RX bytes:812 acl:0 sco:0 events:67 errors:0
TX bytes:5420 acl:0 sco:0 commands:67 errors:0
Features: 0xff 0xfe 0x0f 0xfe 0xdb 0xff 0x7b 0x87
Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
Link policy: RSWITCH HOLD SNIFF
Link mode: PERIPHERAL ACCEPT
Name: 'linux'
Class: 0x7c0104
Service Classes: Rendering, Capturing, Object Transfer, Audio, Telephony
Device Class: Computer, Desktop workstation
HCI Version: 4.2 (0x8) Revision: 0x1000
LMP Version: 4.2 (0x8) Subversion: 0x1000
Manufacturer: Intel Corp. (2)

This is the feature bit specification from BLE 4.2. Version 5.x uses more bits.

image

@zapta
Copy link
Author

zapta commented Feb 22, 2023

Apparently the issue is the builtin BT controller in my computer which doesn't support the Data Packet Length Extension feature. It's an old controller. I plugged in this adapter https://www.amazon.com/dp/B08M1VJHVD and, disabled the internal one, packets are not fragmented anymore. Thanks for the help @dlech.

NOTE: to disable the builtin interface I followed the answer here https://unix.stackexchange.com/questions/242937/i-have-two-usb-bluetooth-adapters-one-doesnt-work-hci1-but-seems-to-be-the-de

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3rd party issue The issue is with the Bluetooth stack, the BLE device, or other 3rd party code not with Bleak itself Backend: BlueZ Issues and PRs relating to the BlueZ backend
Projects
None yet
Development

No branches or pull requests

2 participants