Building a Bluetooth network with Linux

Ángel Ortega

Building a Bluetooth wireless network with Linux is easier than it may seem. This document is about connecting several computers in a TCP/IP network; it does not talk about other devices as phones, PDAs or printers.

As a difference from WiFi hardware, where Linux support is a nightmare (blame the hardware companies, that keep their crappy specifications as military secrets), Bluetooth hardware support is pretty complete; you have a plethora of cheap and easy to find hardware in its USB or PCMCIA variants. See the complete list.

In my particular case, I've bought two Conceptronic CBT100U USB dongles (the ones with a little antenna), and a Conceptronic CBT100C PCMCIA card. All of them are on the Class1 family, with a maximum range of 100 meters; more than enough for my home.

I wanted to connect just two machines, a headless server and a laptop; I suppose this document applies to networks having more nodes (didn't try).

The server runs Debian Linux with a 2.4.x kernel, and the laptop, Debian Linux with a 2.6.x. I compiled all Bluetooth options in the kernel as modules, and installed the following packages on both machines:

bluez-hcidump - Analyses Bluetooth HCI packets
bluez-pcmcia-support - PCMCIA support files for BlueZ 2.0 Bluetooth tools
bluez-pin - Bluetooth PIN helper with D-BUS support
bluez-utils - Bluetooth tools and daemons
libbluetooth1 - Library to use the BlueZ Linux Bluetooth stack
bluez-firmware - Firmware for Bluetooth devices

As a first step, I tried with the CBT100C USB dongles. Just by inserting them, hotplug detected them and loaded all the appropriate modules; after that, I could run the hcitool scan command on either system, and in some seconds both found each other.

The PCMCIA was a bit more complicated; this particular model needs the hciattach command to be ran after the serial_cs module from pcmcia tools is loaded, but the bluez-pcmcia-support Debian package didn't have the necessary information for it to work. So, after a bit of investigation, I needed to add the following to the /etc/pcmcia/bluetooth.conf file:

card "Conceptronic CBT100C Bluetooth"
  version "PCMCIA", "Bluetooth Card", "", ""
  manfid 0x0279, 0x950b
  bind "serial_cs" class "bluetooth"

This way, the pcmcia stack can recognize the card as a Bluetooth one, and run the /etc/pcmcia/bluetooth helper script.

But this was not sufficient; though correctly detected, the command didn't work as expected. Digging in the http://www.bluez.org mailing list archives (a very interesting source of information), I got the exact arguments to add to hciattach, so I patched the /etc/pcmcia/bluetooth script substituting the line

/usr/sbin/hciattach $DEVICE $MANFID

with

sleep 5 ; /usr/sbin/hciattach $DEVICE bcsp

I found the 'sleep 5' also necessary; don't know of a good reason.

All this work is just to stablish a link layer; over that, we have to create a TCP/IP stack to use our favourite programs.

Bluetooth networks are structured in a similar manner as the WiFi ones; there are ad-hoc (point to point) and access point / clients topologies. See the Personal Area Network HOWTO for all the gory details. I decided to run my server as a NAP (Network Access Point) and my laptop as a PANU (PAN User).

The daemon that builds an interface (that will be managed by good old ifconfig) is called pand. On Debian, you can tweak /etc/defaults/bluez-utils to select running pand on startup; I configured it on my server this way:

/usr/bin/pand -s -M --role=NAP

And on my laptop:

/usr/bin/pand --role PANU --search --persist --encrypt

I found the --persist necessary; the connection between pairs is slow (almost one minute) and, without that argument, pand surrenders in a short number of seconds.

When the connection is stablished, pand creates the bnep0 interface. You can use ifconfig to configure that interface the same as eth0 and friends, so you can manually set IPs to each one, and after that, you have a working TCP/IP network.

I found rather tricky to make this network to correctly configure on startup; I filled a 'stanza' in /etc/network/interfaces for each one but, whenever the pand daemons in both machines made the connections, the interfaces didn't automatically go up. Finally, I found somewhere on the Internet the solution; when _pand_ decides the bnep0 is ready, it executes the /etc/bluetooth/pan/dev-up script, that didn't exist on my system. I created it with the following:

#!/bin/sh
ifup bnep0

And now everything sets up automatically.

Bluetooth allows traffic to be encrypted and connections be established using a rudimentary, PIN based authorization. When asked for authorization and encryption, the whole system (as installed from Debian) didn't work reliably; sometimes, an X11 window was popped up asking me for the PIN; other times, it failed miserably without warning. Though I always have an X11 session opened, I don't like spurious windows popping up asking me for passwords; it seems to me like an invitation to security intrusions. Fortunately, the bluez people added a hook to change the PIN requester program; what I wanted, and probably everyone of you do, is to store the PIN to connect to your server somewhere and feed it to the underlying system whenever needed. So, I modified the /etc/bluetooth/hcid.conf config file this way:

# PIN helper
# pin_helper /usr/bin/bluez-pin;
pin_helper /usr/local/sbin/mypin

and created /usr/local/sbin/mypin, containing

#!/bin/sh
PIN=`cat /etc/bluetooth/pin`
echo "PIN:$PIN"

This is only needed on the client; the server seem to find the PIN in /etc/bluetooth/pin without problems. Also, and though you'll see in examples everywhere 4 number PINs, it seems they can be arbitrarily long, so use longer strings.

Now all that I want is to get rid of the mandatory bluez-pin Debian package, that carries all X11 libraries and crappy GNOME stuff into my headless server for a /usr/bin/bluez-pin program that will never be called.

I don't know if Bluetooth encryption is really secure or is a joke; I don't even know if network packets are really travelling encrypted. But the line of faith must be drawn somewhere.

DHCP leases are useful from a sysadmin point of view: you can forget about maintaining boring lists of IPs and make interfaces configure themselves. Bluetooth networks aren't different, so you can set your dhcpd to serve IPs for your bnep clients (if authorization is active, they are served only to PIN-authenticated clients, so you can feel relatively safe). One caveat; bnep0 does not exist until a connection is established, and dhcpd refuses to monitor interfaces that don't exist. Even more, if the interface disappears because of a connection loss, dhcpd surrenders and stop listening to it. So, I had to add this hack to /etc/bluetooth/pan/dev-up in my access point server, that also serves as DHCP server:

#!/bin/sh
ifup bnep0
 
# hack to force dhcpd to serve IPs on bnep0
/etc/init.d/dhcp3-server restart 2>&1 > /dev/null

Hope it's obvious; when the interface appears, the dhcp3-server is restarted so it always find a usable network interface to serve DHCP leases on.

This network is not fast; I got approximately 80k/s, but I consider it more than sufficient for normal work, and the possibility of working from the garden wins everything.

There are many more things you can do with a Bluetooth network, as printing, syncing PDAs, use wireless keyboards and mice, backup cellular phones' addressbooks and such, but I still didn't try.