It seems that you have had no response to this so I thought I would have a try to see how far I got. These are my notes from the journey. This was not a successful journey for me but I'm hoping by sharing my failure it will help someone build on it.
From searching around it is clear that how to configure Bluetooth Personal Area Networks with BlueZ has changed dramatically over the last few years. Most of the tutorials I found used tools that have since been deprecated.
As I could not find a good tutorial I went and looked at more formal documentation.
The Bluetooth SIG has a document on the PAN profile at: https://www.bluetooth.com/specifications/profiles-overview/
and the PAN Profile overview on the Symbian Developer Library I found helpful:
From: https://docs.huihoo.com/symbian/s60-5th-edition-cpp-developers-library-v2.1/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/sdk/doc_source/guide/Short-Link-Services-subsystem-guide/ShortLinkServices/BluetoothProfiles/BluetoothPAN/BTPANProfileOverview.html
It appears there are three main variations on a PAN. This is backed up when looking in the BlueZ Network DBus API. However, the BlueZ API seems to have client/server of either GN, NAP or PANU whereas the above documents talk about the PANU always being the client. I did some drawnings of how I think it is in BlueZ:
Network Bridge - Client Raspberry Pi - Server Phone
Network Bridge (Network Aggregation Point) [nap]
Group Ad-hoc Network [gn]
Peer to Peer [panu]
In the BlueZ readme it suggests the networking profiles are enabled by default.
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/README#n188
The networking API is documentated at:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/network-api.txt
They have an example/test Python script for this client functionality at:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/test-network
And for a PAN Network server:
https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/test-nap
There is a thread about using this on the developer mailing list.
https://marc.info/?l=linux-bluetooth&m=145504188107174&w=2
There seems to be good information in the thread but (spoiler alert) they didn't get it working but that was a few years ago.
However, I did find a follow-up that is disconnected from the main thread:
https://marc.info/?l=linux-bluetooth&m=149129232432090&w=2
Not a conclusive answer but there was a reply from one of the main developers suggesting it should work.
This last thread talked about running a test-gn
script which I could not find.
For the server, the NetworkServer1
Register
method is called from the adapter device object path. Using bluetoothctl show
before and after this Register
command will show that a PAN UUID has been added.
For the client, the Network1
Connect
method is called from the remote device object path. For this path to be there, the client and server device need to have been paired.
I have been using bluetoothctl
to do the pairing.
I edited /etc/dbus-1/system.d/bluetooth.conf
to include permission for the Network1
and NetworkServer1
interfaces. I did this on both my test Raspberry Pi's
<policy user="root">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
<allow send_interface="org.bluez.Agent1"/>
<allow send_interface="org.bluez.MediaEndpoint1"/>
<allow send_interface="org.bluez.MediaPlayer1"/>
<allow send_interface="org.bluez.Profile1"/>
<allow send_interface="org.bluez.GattCharacteristic1"/>
<allow send_interface="org.bluez.GattDescriptor1"/>
<allow send_interface="org.bluez.LEAdvertisement1"/>
<allow send_interface="org.bluez.Network1"/>
<allow send_interface="org.bluez.NetworkServer1"/>
<allow send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
Tethering to phone
I tested tethering to my phone as this just required me to get the client working. This worked well. I enabled Bluetooth tethering on my Android phone. Then on the Raspberry Pi, using bluetoothctl, scanned, paired, and trusted before using the following commandline to connect:
busctl call org.bluez /org/bluez/hci0/dev_xx_xx_xx_xx_xx_xx org.bluez.Network1 Connect s pan_type
[xx_xx_xx_xx_xx_xx being the BD_ADDR of my phone with : replaced with _]
[pan_type is one of gn, panu, or nap]
I have been using a python script to do the same [network_client.py
]:
import argparse
from signal import pause
import pydbus
parser = argparse.ArgumentParser()
parser.add_argument('d', help='BD_ADDR of network server')
parser.add_argument('u', help='Server type to connect to [gn, panu, nap]')
args = parser.parse_args()
device = args.d
device_path = f"/org/bluez/hci0/dev_{device.replace(':', '_')}"
bus = pydbus.SystemBus()
network = bus.get('org.bluez', device_path)['org.bluez.Network1']
print(f'Connecting to {args.d} as a {args.u}')
network.Connect(args.u)
try:
print('Press CTRL-C to disconnect')
pause()
except KeyboardInterrupt:
print('Disconnecting from network')
network.Disconnect()
Using the command line:
python3 network_client.py xx:xx:xx:xx:xx:xx nap
These instructions remain valid for the different client types you may want to connect to whether that is on a phone or a Raspberry Pi.
Setting up Bluetooth PAN Server.
I have found this to be the more difficult part because of my lack of knowledge on setting up network bridges and understanding what BlueZ is doing for me and what I need to do. I have found the following documents useful references for creating network bridge on the Bluetooth PAN server. :
Iproute2 tutorial
https://www.howtogeek.com/657911/how-to-use-the-ip-command-on-linux/
Iproute2 examples
https://baturin.org/docs/iproute2/
As I don't have this fully worked out I am using iproute2 on the command line so settings aren't saved. This means when I make errors, a reboot resets things to a working state
Common server setup for all PAN types
- Modify
/etc/dbus-1/system.d/bluetooth.conf
to include Network1
and NetworkServer1
interfaces (same as client setup)
- Create the network bridge before launching the Bluetooth PAN server
- Ensure the PAN is registered before the client pairs with the server
- use
trust
in bluetoothctl to ensure the server and client trust each other
- If things don't appear to be working, look for error messages on the server with
service bluetooth status
Common error messages in service bluetooth status
[server]
Bridge specified in Register command does not exist
BNEP server cannot be added
bnep: Can't add bnep0 to the bridge
Connection from client without server registered (0x1116 = NAP, 0x1117 = GN etc) or permissions in /etc/dbus-1/system.d/bluetooth.conf
Server error, bridge not initialized: (0x1116)
Jul 26 10:43:11 raspberrypi bluetoothd[581]: BNEP server cannot be added
Common script exit error messages [client]
PAN type not registered on Server
gi.repository.GLib.Error: g-io-error-quark: GDBus.Error:org.bluez.Error.Failed: Input/output error (36)
Starting a client for a PAN type that hasn't been paired with on specified server or permissions in /etc/dbus-1/system.d/bluetooth.conf
hasn't been set.
gi.repository.GLib.Error: g-io-error-quark: GDBus.Error:org.bluez.Error.NotSupported: Operation is not supported (36)
Basic form for BlueZ register of PAN on server
This can be done on the commandline with one of the DBus tools. e.g.:
busctl call org.bluez/ org/bluez/hci0 org.bluez.NetworkServer1 Register ss pan_type bridge_name
Or I have been using a Python script [network_server.py
]:
import argparse
import pydbus
from gi.repository import GLib
bus = pydbus.SystemBus()
server = bus.get('org.bluez', '/org/bluez/hci0')['org.bluez.NetworkServer1']
parser = argparse.ArgumentParser()
parser.add_argument('u', help='Server role [gn, panu, nap]')
args = parser.parse_args()
if args.u == 'gn':
bridge = 'bluez_gn'
elif args.u == 'panu':
bridge = 'bluez_panu'
else:
bridge = 'bluez_nap'
server.Register(args.u, bridge)
print(f'Service {args.u} is registered with bridge {bridge}')
mainloop = GLib.MainLoop()
print('Press CTRL-C to disconnect')
try:
mainloop.run()
except KeyboardInterrupt:
mainloop.quit()
Pairing process
The server needs to be started before the client
Care needs to be taken when pairing two Raspberry Pi's together for PAN to ensure things are done in the correct order
On server:
server: Define bridge for PAN type
server: start server script so PAN UUID is in list of services
server[bluetoothctl]: `default-agent`
server[bluetoothctl]: `discoverable on` <leave bluetoothctl running>
On client
client[bluetoothctl]: `default-agent`
client[bluetoothctl]: `scan on`
client[bluetoothctl]: `scan off`
client[bluetoothctl]: `pair xx:xx:xx:xx:xx:xx` <need to type yes on both machines>
client[bluetoothctl]: `trust xx:xx:xx:xx:xx:xx`
On Server
server[bluetoothctl]: `trust yy:yy:yy:yy:yy:yy`
On Client
client: start client script
Peer to Peer [panu
]
Work In Progress
Group Network [gn
]
sudo ip link add name bluez_gn type bridge
sudo ip link set bluez_gn up
python3 network_server.py gn
I then use the pairing process outlined above before starting the client. After ~30 seconds I can use ip addr show
to see that client bnep0
has an ip address and I can ping it. However, I can't ping the server from the client. I suspect this is because I have created the bridge incorrectly.
Network Bridge (Network Aggregation Point) [nap
]
On the server RPi, I have wired internet on eth0
so this is how I setup the bridge and start the server
sudo ip link add name bluez_nap type bridge
sudo ip link set bluez_nap up
sudo ip link set dev eth0 master bluez_nap
python3 network_server.py nap
I then use the pairing process outlined above before starting the client. This give my PAN client access to the internet over bluetooth. However, I do loose network access to the PAN server. I suspect this means my bridge setup is not not complete.
Command to get status information
Commands I've been using to check on status etc are:
service bluetooth status
sudo btmon -t
ip address show
ip route show
ip link show
dmesg -w
If anyone understands why BNEP server is not being created then please let me know.