PPP over Bluetooth

A Poor Man's WiFi

I recently bought a new Asus Eee PC 1000HE. Most of the hardware works great under OpenBSD, except for the wireless chipset: an Atheros AR9821. There is a Linux driver for that chip: ath9k, written by former MadWifi developers who were hired by Atheros. It might be ISC licensed, but it makes little effort to help anyone understand how to talk to the device: the driver has few comments and plenty of giant tables of magic numbers that are copied into the device's memory-mapped I/O space. Since there's no real documentation on the hardware, I expect that extending ath(4) to support these devices would be non-trivial.

In the meantime, this netbook is a lot less portable if it has to trail an Ethernet cable all the time. Fortunately, the Eee also has on-board Bluetooth, and I happen to have a spare USB Bluetooth dongle to attach to a centrally-located desktop system. This won't help me on the road, but in my one-bedroom apartment, few locations are outside Bluetooth range. This page documents how I set up PPP over Bluetooth RFCOMM to allow wireless networking between the Eee PC and a Sun Ultra 10, both running OpenBSD-current from February 2009.

Note that I'm not well-versed in Bluetooth terminology and the details of the Bluetooth protocol, so much of what I say about the pairing process and about the significance of btconfig(8) options may be wrong.

Bluetooth Hardware Setup

USB Bluetooth adapters are supported by the ubt(4) driver. This is built into the GENERIC kernel in snapshots since 26 November 2008. Note that the Eee's onboard Bluetooth is also a USB ubt(4) device: it's just attached to an internal USB bus. This is probably the case for most other laptops with built-in Bluetooth as well.

Bluetooth Protocol Stack

Bluetooth support is built into the OpenBSD kernel, but there's no userland software to interact with it in the base system, so it's necessary to install the following packages, which are a port of NetBSD's userland Bluetooth stack:

We will be using the following userland tools from bluetooth-tools: To bring up a Bluetooth interface, invoke btconfig(8):
# btconfig ubt0 up pscan switch auth encrypt class 0x02010c
The pscan option, according to the man page, is necessary to allow incoming connections. This can be omitted on the client. The class identifies the type of station: 0x2010c is a laptop computer, and 0x20104 is a desktop.

The iscan option allows the host to the discovered by a Bluetooth inquiry, which is useful when starting out to determine if your hardware is working and your systems are within close enough range to communicate. Inquiries can be performed like so:

# btconfig ubt0 inquiry
Device Discovery from device: ubt0 .... 1 response
  1: bdaddr xx:xx:xx:xx:xx:xx
   : name "ISSCBTA"
   : class: [0x020104] Desktop Computer <Networking>
   : page scan rep mode 0x01
   : clock offset 0

The bthcid(8) and sdpd(8) daemons should be started in order for authentication (pairing) and service discovery to operate. All three steps can be done at boot-up in rc.local:

if [ $# -eq 0 -o x"$1" == x"bluetooth" ]; then
	echo -n ' bluetooth'
	/usr/local/sbin/btconfig ubt0 up switch auth encrypt class 0x02010c

Pairing Bluetooth Devices

Bluetooth authentication is known as pairing. The theory of operation as I understand it is that the initiating device generates a PIN, which must be transferred over a secure channel to the responding device. The PIN acts as a shared secret to establish trust during the authentication handshake. Since Bluetooth is a short-range "personal-area networking" protocol, the secure channel is usually the human user manually entering the PIN into the responding device.

This is accomplished using the btpin(8) command. To initiate a pairing, issue btpin(8) on one system (it doesn't matter which) to generate a random PIN for pairing with the other system:

# btpin -a <second device's address> -r -l <length>
The btpin(1) command prints the generated PIN to stdout. Then on the other system:
# btpin -a <first device's address> -p <pin>
This process must be completed within 5 minutes, before the PIN expires.

If the devices are paired and have exchanged link keys for encryption, you can view the keys using the btkey(1) command:

# btkey -l -d ubt0
    device: xx:xx:xx:xx:xx:xx (ubt0)

    bdaddr: yy:yy:yy:yy:yy:yy
  file key: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa


The Bluetooth RFCOMM service emulates an RS-232 serial connection over Bluetooth. Fancy high-end cellphones use this service to present themselves as dial-up modems; I'm using it to emulate a simple null modem cable between two hosts.

The rfcomm_sppd(1) program acts as both an RFCOMM server and a client. When issued with the -c option, it listens for connections on the given channel number and registers itself with the host's SDP daemon. When given a remote address using the -a option, it attempts to discover and connect to an RFCOMM service on the given remote device.

To test out RFCOMM, issue the following command on the server:

# rfcomm_sppd -c 1 -m encrypt
And on the client:
# rfcomm_sppd -a <server's address> -m encrypt
The -m option selects the security mode of the connection: auth for authentication only and encrypt for authentication and encryption. The manpage also lists a secure mode, which apparently would periodically rekey the encrypted channel: this sounds desirable, but I've never been able to get it to work.

If the connection is established, you should be able to type characters into one rfcomm_sppd(1) instance's standard input and see them appear in the other. There is no escape character, so kill the process with ^C when done.


When passed the -t option, rfcomm_sppd(1) attaches its virtual serial line to a pty device, such as /dev/ttyp0, which can then be passed to tip(1), getty(8), pppd(8), et al.

This is the tough part, as many no doubt recall from the days of dial-up modems: PPP offers a bewildering array of options which must be synchronized exactly on both ends. The man pages and sample configuration files are written with dial-up modems in mind, and discuss plenty of features in depth that don't apply here, such as authentication. I no longer care enough about PPP to spend time reading about its configuration file format; fortunately, Google turned up enough people trying to do similar things that I was able to piece together the following configuration files, which work for me.

/etc/ppp/options on the client:

<client's IP aadress>:<server's IP address>
And on the server:
<server's IP address>:<client's IP address> netmask

To bring the connection up, on the server:

# rfcomm_sppd -c 1 -m encrypt -t <pty device> && \
pppd <pty device>
rfcomm_sppd(1) blocks until a connection is established with the client. On the client:
# rfcomm_sppd -a <server's bluetooth address> -m encrypt \
-t <pty> && pppd 
This should bring up a ppp interface, which can then be set up as the client's default route. On the server, enable IP forwarding with sysctl and set up pf to allow the client access to the wider network, perhaps using NAT to avoid having to set up routes.


As is evident in the PPP configuration files, I'm establishing a 115200 baud connection. This is good enough for interactive SSH sessions, but bulk file transfer and even web browsing can be painful. On paper, Bluetooth offers 1 megabit of bandwidth, so it should be possible to scale up this baud rate, but I haven't experimented with that.


As one might expect, the reliability of this setup drops precipitously as the distance between the client and server increases. In my experience, it's quite usable at about seven feet, but at roughly twice that distance the connection drops frequently and is extremely difficult to establish. Connection drops are often accompanied by the following kernel diagnostic message on the client:

/bsd: ubt0: unknown handle 11! (losing track of 8 packet buffers)
Even at close range, rfcomm_sppd frequently fails to establish a connection for no readily apparent reason, which makes it difficult to automate the establishment of this setup.

Finally, under certain circumstances (e.g. when downloading a large image from a webpage), the link can stall until it is revived by unrelated Bluetooth traffic (such as an sdpquery(8)). This is almost certainly the result of some bug or limitation in the OpenBSD Bluetooth code.


There are known cryptanalytic attacks against the Bluetooth pairing process, which make it feasible to obtain the key used to protect subsequent traffic. The consequences are obviously greatly mitigated by Bluetooth's short range, but I still wouldn't advise using cleartext protocols over a Bluetooth link.