Use your Bluetooth phone as a modem when Wi-Fi isn’t available.
No doubt the novelty of being able to scan for nearby Bluetooth devices from your Linux machine will wear off all too soon, and you’ll be wanting to actually do things with your shiny new Bluetooth connection. Being able to use your cell phone as a modem from all those places you can’t pull in a Wi-Fi signal would be pretty cool, wouldn’t it?
Bluetooth supports a number of
“profiles,” which define the way
that Bluetooth devices can communicate with each other. In this case,
we want to make use of the
Dial-up Networking (DUN) profile,
which relies on a protocol called
RFCOMM to emulate
a serial link between two devices. You can use RFCOMM to connect your
Linux box to your phone, and then run pppd
over
the link to get access to the Internet. This works using GPRS, or
even an ordinary Internet dial-up.
Assuming you’ve got Bluetooth working [Hack #16], you should be able to bring your phone within range of your computer and scan for it using hcitool. We’ll presume that you’ve done this, and that hcitool reports a BD address for your phone of 00:11:22:33:44:55.
You can also use sdptool to verify that there’s a device in range that supports the DUN profile:
$ sdptool search DUN
Inquiring ...
Searching for DUN on 00:11:22:33:44:55 ...
Service Name: Dial-up Networking
Service RecHandle: 0x10001
Service Class ID List:
"Dialup Networking" (0x1103)
"Generic Networking" (0x1201)
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 1
Note this channel number, because you’ll need it later. As you can see, hcitool and sdptool offer a lot of other useful Bluetooth diagnostic functions, which you can read more about on their respective manpages.
Before you can actually connect to the phone, however, you may need
to set up what’s referred to as device
pairing
between your Linux box and your phone,
so that your phone knows to allow your computer access to its
services, and possibly vice versa. Your computer’s
PIN can be found in
/etc/bluetooth/pin
, and you will want to alter
this to a unique value that only you know.
Most phones have a Bluetooth PIN that you can configure within the
phone itself. BlueZ comes with a little Python
utility called bluepin that pops up a GTk+
dialog to ask for your phone’s PIN as needed; this
utility apparently doesn’t work out of the box
requires in some Linux distributions. More to the point, who wants to
be asked for your phone’s PIN every time you use the
thing, anyway? The following Perl script can be saved to
/etc/bluetooth/pindb
, and you can use it store
PINs for multiple
Bluetooth devices:
#!/usr/bin/perl while ( ) { print "PIN:$1 " if /^$ARGV[1]s+(w+)/o; } __DATA_ _ # Your Bluetooth PINs can go here, in BD address / PIN pairs, # one to a line, separated by whitespace. # # ## etc. 00:11:22:33:44:55 11111
Make sure that /etc/bluetooth/pindb
is owned by
root and chmod 0700—you don’t want ordinary
users being able to look up your Bluetooth device PINs. The options
section of your /etc/bluetooth/hcid.conf
should
accordingly look something like this:
options { autoinit yes; security auto; pairing multi; pin_helper /etc/bluetooth/pindb; }
This ensures that HCI devices are configured at boot, that pairing is
allowed, and that hcid will ask pindb
for your PINs on a per-device basis. Be sure to restart
hcid by running
/etc/rc.d/init.d/bluetooth restart
if you made any
changes to your /etc/bluetooth/hcid.conf
.
Now that your computer is set up for pairing, you’ll
have to set up your phone similarly, for which
you’ll need to refer to your user’s
manual. This set-up process often requires that the phone can scan
for your computer’s Bluetooth adapter, so be sure
that your computer is within range with a working Bluetooth adapter.
The interface will probably come up as “BlueZ
(0)”, or something similar, unless you changed the
“name” option in your
hcid.conf
. You probably want to set up the
pairing on the phone as “trusted,”
or the moral equivalent, so that the phone doesn’t
ask you to verify the connection each time you try to dial out from
your Linux box.
Now that I’ve shown that there’s a
device in range that offers dial-up networking, and set up
pairing with it, the next step is to
bind an RFCOMM interface to that device. First, make sure that there
are RFCOMM entries in your /dev
directory, using
ls -l /dev/rfcomm*
. If ls
reports “No such file or
directory”, you can trivially create 64 RFCOMM
device entries by switching to the superuser and doing the following:
#for n in `seq 0 63`; do mknod -m660 /dev/rfcomm$n c 216 $n; done
#chown root:uucp /dev/rfcomm*
If you’re running Debian, you probably want to
chown
your RFCOMM devices to
group dialout
, instead of
uucp
.
Now, as the superuser, bind /dev/rfcomm0
to your
phone on the channel reported for DUN by sdptool
earlier, using the
rfcomm
utility from
bluez-utils:
# rfcomm bind /dev/rfcomm0 00:11:22:33:44:55:66 1
You’ll know that the device was bound successfully if, like any good Unix utility, rfcomm just returns silently. You can demonstrate that it did actually work, however, by running rfcomm without any arguments:
# rfcomm
rfcomm0: 00:11:22:33:44:55 channel 1 clean
Now you can pretty much just treat this serial device as if it were
an ordinary modem. Just to prove it, try running
minicom
as root, and switch the serial device to
/dev/rfcomm0
. When the terminal loads, type
AT
and press Enter. If the phone responds
OK
, then congratulations are in
order—you’re talking to your cell phone over a
Bluetooth connection.
Before going any further, you may want to add the following to your
/etc/bluetooth/rfcomm.conf
, so that the RFCOMM device is
configured by default when Bluetooth loads:
rfcomm0 { # Automatically bind the device at startup bind yes; device 00:11:22:33:44:55; channel 1; comment "My Phone"; }
If you’re running bluez-utils 2.3 or earlier on a
non-Debian-based distro, you may need to add
rfcomm
bind
all
to the start( )
section of your
/etc/rc.d/init.d/bluetooth
for this to work
right.
From here, it’s just a short hop to getting your
computer on the Net. Put the following
into
/etc/ppp/peers/gprs
:
/dev/rfcomm0 connect '/usr/sbin/chat -v -f /etc/ppp/peers/gprs.chat' noauth defaultroute usepeerdns lcp-echo-interval 65535 debug
Then save the following as
/etc/ppp/peers/gprs.chat
:
TIMEOUT 15 ECHO ON HANGUP ON '' AT OK ATZ OK ATD*99#
Alternately, if you prefer using
wvdial
, try adding the following to your
/etc/wvdial.conf
:
[Dialer gprs] Modem = /dev/rfcomm0 Phone = *99# Username = foo Password = bar
Note that while European providers give you a username and password,
in the U.S. you still need to supply dummy values to satisfy
wvdial. Consult your network
provider’s web site for details about what values
you may need to use. Your GPRS is actually already authenticated by
your very presence on the cellular network, so you
don’t have to re-auth just to use PPP. The
“phone number” listed is the
standard GPRS dial-up number, which may work for you right off the
bat if your phone is configured properly. Most
GSM phones support multiple GPRS access
points, however—so, if the default for your phone
doesn’t work for you, try going into minicom and
typing AT+CGDCONT?
followed by a carriage
return. Your phone should respond with a list of available
Packet Data Protocol (PDP)
contexts. Pick the one that seems the most appropriate, and then set
your GPRS phone number in /etc/wvdial.conf
to
*99***
n
#
,
replacing n
with the
number of the PDP profile you want to use. Failing that, try
contacting your service provider for advice!
You can test this setup as root by running either
pppd
call
gprs
or wvdial
gprs
, depending on your setup, and watching
/var/log/messages
in another window. The only
hitch with this setup is that it doesn’t set up your
nameservers in /etc/resolv.conf
by default. The
way around this on Red Hat is to store the following in
/etc/sysconfig/network-scripts/ifcfg-ppp0
(or
ppp1, ppp2, etc. as you
prefer):
# comment out CHATSCRIPT and uncomment WVDIALSECT if you're using wvdial DEVICE=ppp0 MODEMPORT=/dev/rfcomm0 CHATSCRIPT=/etc/ppp/peers/gprs.chat # WVDIALSECT=gprs
This way you can just use
ifup
ppp0
and ifdown
ppp0
to bring the link up and down. To get the
identical result on Debian, use the pppd
configuration just shown, and add the following to your
/etc/network/interfaces
:
iface ppp0 inet ppp provider gprs
If you’re not using a Red Hat- or Debian-like
distribution, you can always just add the following additional lines
to your
/etc/ppp/peers/gprs
to make DNS work right, and use
pppd
call
gprs
and killall pppd
to bring
the link up and down:
welcome 'cp -b /etc/ppp/resolv.conf /etc/resolv.conf' disconnect 'mv /etc/resolv.conf~ /etc/resolv.conf'
That’s just about all you need to know to get online from anywhere you can get GSM service. Just don’t expect blistering speeds from it: as of this writing, GPRS ranges in speed from under 5k/s to just over 20k/s, depending on your service—not exactly high speed by modern standards, but amazing where you would otherwise have nothing at all.
As a little bonus, here’s a short
iptables
script to let
you share that GPRS with anyone in Wi-Fi range, which could be stored
as or called from /etc/ppp/ip-up.local
:
# Enable IP forwarding and rp_filter (to kill IP spoof attempts). echo "1" > /proc/sys/net/ipv4/ip_forward echo "1" > /proc/sys/net/ipv4/conf/all/rp_filter # Load relevant kernel modules, if necessary. for i in ip_tables ipt_MASQUERADE iptable_nat ip_conntrack ip_conntrack_ftp ip_conntrack_irc ip_nat_irc ip_nat_ftp; do modprobe $i 2>/dev/null; done # Masquerade anything that's not from a PPP interface # (e.g. ethernet, Wi-Fi, etc.) iptables -t nat -A POSTROUTING -o ppp+ -j MASQUERADE
But what, you ask, about regular
dial-up connections? How about faxes? Well, it turns out that
you’re in luck: simply replace the GPRS access
number with any regular phone number of your choice and (on most
phones) you get a 9,600 baud data connection to that line.
Configuring efax
or mgetty-sendfax
to
use Bluetooth to fax from a GSM phone in this manner is therefore
left as an exercise for the reader.
—Schuyler Erle
3.144.17.91