Tuesday, 28 August 2012

Nokia N9 Bluetooth PAN, USB & Dummy Networks

Please note: All of these instructions assume you have developer mode enabled and are familiar with using the Linux console. One of the variants of dummy networking I present here also requires a package to be installed with Inception or use of an open-mode kernel to disable aegis. I present an alternative method to use a pseudo-dummy network for people who do not wish to do that.

Background

Earlier this year I bought a Nokia N9 (then took it in for service TWICE due to a defective GPS, then returned it for a refund since Nokia had returned it un-repaired both times, then bought a new one for $200 less than I originally paid, then bought a second for my fiancé).

The SIM card I use in the N9 is a pretty basic TPG $1/month deal, which is fine for the small amount of voice calls I make, but it's 50MB of data per month is a not really enough, so I'd like it to use alternative networks wherever possible.

When working on another computer with an Internet connection, I could simply hook up the N9 via USB networking and have the computer give it a route to the Internet. That works well, but has the problem that any applications using the N9's Internet Connectivity framework (anything designed for the platform is supposed to do this via libconic) would not know that there was an Internet connection and would refuse to work - so I had to find a way to convince them that there was an active Internet connection using a dummy network. Also, this obviously wouldn't work when I was away from a computer.

I also happen to carry a pure data SIM card in my Optus MyTab with me all the time (being my primary Internet connection), so when I'm on the go I'd like to be able to connect to the Internet on the N9 via the tablet rather than use the small amount of data from the TPG SIM.

The MyTab is running CyanogenMod 7 (I'm not a fan of Android, but at $130 to try it out the price was right), so I am able to switch on the WiFi tethering on the tablet and connect that way, but it has a couple of problems:

  • It needs to be manually activated before use
  • It needs to be manually deactivated to allow the bluetooth tethering to work
  • It isn't very stable (holding a wakelock helps a lot - the terminal application can be used for this purpose)
  • It's a bit of a battery drain (at least the tablet has a huge battery)

The MyTab also supports tethering over bluetooth PAN (which I regularly use at home), so it made a lot of sense to me to connect the N9 to the tablet using that as well when I am out and about. Unfortunately, the N9 does not come with any software to connect to a bluetooth network, and I couldn't manage to find anyone else who had successfully done this (There are a couple of threads discussing it).

Fortunately, the N9 has a normal Linux userspace under the hood (one reason I'd take this over Android any day), which includes bluez 4.x and as such I was able to use that to make it do bluetooth PAN.

USB Network

Let's start with USB Networking since it is already supported on the N9 and works out of the box once developer mode is enabled (select SDK mode when plugging in).

Here's a few tricks you can do to streamline the process of using the USB network to gain an Internet connection. You will also want to follow the steps under one of the Dummy Networking sections below to allow applications (such as the web browser) to use it.

On the host, add this section to your /etc/network/interfaces (this is for Debian based distributions, if you use something else you will have work out the equivalent):

allow-hotplug usb0
iface usb0 inet static
    address 192.168.2.14
    netmask 255.255.255.0
    up iptables -t nat -I POSTROUTING -j MASQUERADE
    up iptables -A FORWARD -i usb0 -j ACCEPT
    up iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
    up echo 1 > /proc/sys/net/ipv4/ip_forward
    down echo 0 > /proc/sys/net/ipv4/ip_forward
    down iptables -F FORWARD
    down iptables -t nat -F POSTROUTING

Next, modify the same file on the N9 so that the usb0 section looks like this (this section already exists - I've just extended it a little):

auto usb0
iface usb0 inet static
    address 192.168.2.15
    netmask 255.255.255.0
    gateway 192.168.2.14
    up /usr/lib/sdk-connectivity-tool/usbdhcpd.sh 192.168.2.14
    down /usr/lib/sdk-connectivity-tool/usbdhcpd.sh stop
    up echo nameserver 208.67.222.222 >> /var/run/resolv.conf
    up echo nameserver 208.67.220.220 >> /var/run/resolv.conf
    down rm /var/run/resolv.conf

Now whenever you plug in the N9 and choose SDK mode it should automatically get an Internet connection with no further interaction required and you should be able to ping hosts on the Internet :)

But, you will probably notice that most applications (like the web browser) will still bring up the "Connect to internet" dialog whenever you use them and will refuse to work. To make these applications work we need to create a dummy network that they can "connect" to, while in reality they actually use the USB network.

    USB Networking Notes:
  • The iptables commands on the host will alter the firewall and routing rules to allow the N9 to connect to the Internet through the host. If you use your own firewall with other forwarding rules you may want to remove those lines and add the appropriate rules to your firewall instead.
  • The above commands will turn off all forwarding on the host and purge the FORWARD and POSTROUTING tables when the N9 is unplugged - if your host is a router for other things you definitely will want to remove those lines.
  • The two IP addresses used for the DNS lookups on the N9 are those of OpenDNS.org - you might want to replace them with some other appropriate servers. OpenDNS should be accessible from any Internet connection, which is why I chose them.
  • The N9 will use the most recently modified file under /var/run/resolv.conf* (specifically those listed in /etc/dnsmasq.conf) for DNS lookups. Which means that connecting to a WiFi/3G network AFTER bringing up the USB network would override the DNS settings. I suggest setting the DNS settings for your dummy network to match to avoid that problem.
  • The N9 doesn't run the down rules when it should, rather they seem to be delayed until the USB cable is plugged in again, when they are run immediately before the up rules. Because of the previous note, this isn't really an issue for the dnsmasq update, but it may be an issue if you wanted to do something more advanced.
  • Alternatively, there is an icd2 plugin for USB networking for the N900 available on gitorious. I haven't had a look at this yet to see if it works on the N9 or how it compares to the above technique. This would require installation with Inception.

Dummy Network

This approach to setting up a dummy network isn't for everyone. You are going to need to compile a package in the Harmattan platform SDK (or bug me to upload the one I built somewhere) and install it on the device with Inception, or use an open mode kernel. If you don't feel comfortable with this, you might prefer to use the technique discussed in the Alternative Dummy Network section instead.

First grab the dummy icd plugin from https://maemo.gitorious.org/icd2-network-modules

[host]$ cd /scratchbox/users/$USER/home/$USER
[host]$ git clone git://gitorious.org/icd2-network-modules/libicd-network-dummy.git
[host]$ scratchbox
[sbox]$ sb-menu
 Select -> HARMATTAN_ARMEL
[sbox]$ cd libicd-network-dummy
[sbox]$ dpkg-buildpackage -rfakeroot

Now copy /scratchbox/users/$USER/home/$USER/libicd-network-dummy_0.14_armel.deb to the N9, then install and configure it on the N9 with:

[N9]$ /usr/sbin/incept libicd-network-dummy_0.14_armel.deb

[N9]$ gconftool-2 -s -t string /system/osso/connectivity/IAP/DUMMY/type DUMMY
[N9]$ gconftool-2 -s -t string /system/osso/connectivity/IAP/DUMMY/name 'Dummy network'

[N9]$ devel-su
[N9]# /sbin/initctl restart xsession/icd2

Next time the connect to Internet dialog appears you should see a new entry called 'Dummy network' that you can "connect" to so that everything thinks there is an Internet connection, while they really use your USB or bluetooth connection.

Alternative Dummy Network

This isn't ideal in that it enables the WiFi & creates a network that nearby people can see, but it does have the advantage that it works out of the box and does not require Inception or Open Mode.

Open up settings -> internet connection -> create new connection

Fill out the settings like this:

Connection name: dummy
Network Name (SSID): dummy
Use Automatically: No
network mode: ad hoc
Security method: None

Under Advanced settings, fill out these:

Auto-retrieve IP address: No
IP address: 0.0.0.0
Subnet mask: 0.0.0.0
Default gateway: 0.0.0.0

Auto-retrieve DNS address: No
Primary DNS address: 208.67.222.222
Secondary DNS address: 208.67.220.220

These are the OpenDNS.org DNS servers - feel free to substitute your own.

Then if the 'Connect to internet' dialog comes up you can connect to 'dummy', which will satisfy that while leaving your real USB/bluetooth network alone.

Bluetooth Personal Area Networking (PAN)

This is very much a work in progress that I hope to polish up and eventually package up and turn into an icd2 plugin so that it will nicely integrate into the N9's internet connectivity framework.

First thing's first - you will need to enable the bluetooth PAN plugin on the N9, by finding the line DisabledPlugins in /etc/bluetooth/main.conf and removing 'network' from the list so that it looks something like:

[General]

# List of plugins that should not be loaded on bluetoothd startup
# DisablePlugins = network,hal
DisablePlugins = hal

# Default adaper name
...

Then restart bluetooth by running:

[N9]$ devel-su
[N9]# /sbin/initctl restart xsession/bluetoothd

Until I package this up more nicely you will need to download my bluetooth tethering script from:

https://raw.github.com/DarkStarSword/junk/master/blue-tether.py

You will need to edit the dev_dbaddr in the script to match the bluetooth device you are connecting to. Note that I will almost certainly change this to read from a config file in the very near future, so you should double check the instructions in the script first.

Put the modified script on the N9 under /home/user/blue-tether.py

You first will need to pair with the device you are connecting to in the N9's bluetooth GUI like usual.

Once paired, you may run the script from the terminal with develsh -c ./blue-tether.py

The bluetooth connection will remain up until you press enter in the terminal window. Currently it does not detect if the connection goes away, so you would need to restart it in that case.

For convenience you may create a desktop entry for it by creating a file under /usr/share/applications/blue-tether.desktop with this contents:

[Desktop Entry]
Type=Application
Name=Blue Net
Categories=System;
Exec=invoker --type=e /usr/bin/meego-terminal -n -e develsh -c /home/user/blue-tether.py
Icon=icon-m-bluetooth-lan

Again, this is very much an active work in progress - expect to see a packaged version soon, and hopefully an icd2 plugin before not too long.

One Outstanding Graphical Niggle

You may have noticed that the dummy plugin doesn't have it's own icon - in the connect to Internet dialog it seems to pick a random icon, and once connected the status bar displays it as though it was a cellular data connection. As far as I can tell, the icons (and other connectivity related GUI elements) are selected by /usr/lib/conniaptype/lib*iaptype.so which is loaded by /usr/lib/libconinetdui.so which is in turn used by /usr/bin/sysuid. I haven't managed to find any API references or documentation for these and I suspect being part of Nokia's GUI that they fall into the firmly closed source side of Harmattan. This would be nice to do properly if I want to create my own icd2 plugins, so if anyone has some pointers for this, please leave a note in the comments.

Why is Inception required for real dummy networking?

Well, it's because the Internet Connectivity Daemon requests CAP::sys_module (i.e. The capability to load kernel modules):

~ $ ariadne sh
Password for 'root':

/home/user # accli -I -b /usr/sbin/icd2
Credentials:
        UID::root
        GID::root
        CAP::kill
        CAP::net_bind_service
        CAP::net_admin
        CAP::net_raw
        CAP::ipc_lock
        CAP::sys_module
        SRC::com.nokia.maemo
        AID::com.nokia.maemo.icd2.
        icd2::icd2
        icd2::icd2-plugin
        Cellular

Because of this, aegis will only allow it to load libraries that originated from a source that has the ability to grant CAP::sys_module, which unfortunately (but understandably given what the capability allows) is only the system firmware by default, so attempting to load it would result in this (in dmesg):

credp: icd2: credential 0::16 not present in source SRC::9990007
Aegis: credp_kcheck failed 9990007 libicd_network_dummy.so
Aegis: libicd_network_dummy.so verification failed (source origin check)

Ideally the developers would have thought of this and separated the kernel module loading out into a separate daemon so that icd2 would not require this credential and therefore would allow third-party plugins to be loaded, but since that is not the case we have to use Inception to install the dummy plugin from a source that has the ability to grant the same permissions that the system firmware enjoys (Note that the library does not actually request any permissions because libraries always inherit the permissions of the binary that loaded them - it just needs to have come from a source that could have granted it that permission).

Also, if anyone could clarify what the icd2::icd2-plugin credential is for I would appreciate it - I feel like I've missed something because it's purpose as documented (to load icd2 plugins) seems rather pointless to me (icd2 loads libraries based on gconf settings, which it can do just as well without this permission... so what is the point of this?).

16 comments:

Jarek Jaworowski said...

when trying to run blue-tether.py a recive message about line 3 import dbus "no module named dbus", can u help ?

DSS said...

Hey Jarek,

You need to install python-dbus to get it to work. You should be able to install it by running this command as root:

apt-get install python-dbus

Cheers,
-Ian

FlyingSheep said...

Hi

Great description!

Would you by any chance know where the N9 stores bluetooth settings? And if I can access / change these on the command line?

I paired a new headset some days ago, which worked fine. But as of today bluetooth refuses to start, and without starting, I can't remove the Bluetooth device via the Settings app! (Chicken and Egg...)

I have tried starting both from the N9 GUI, and on the command line via SSH (as you describe). In both cases the phone freezes for a while, then unfreezes, but bluetooth never starts.

If I can't find a way of removing the bluetooth device at command line, then I guess I will have to reflash, which I am keen to avoid.

Thanks for any help

Chris

DSS said...

Hey Chris,

Under the hood the N9 uses bluez, which stores it's state (paired/known devices, visibility, etc) under /var/lib/bluetooth/<adapter bluetooth address>/. There is also some static configuration under /etc/bluetooth

If I recall correctly anything listed in the linkkeys file is considered paired, and if it is also listed in the trusts file it is considered trusted. You can grep for the bluetooth address of the offending device and remove it from any file you find it in.

If you want to try a slightly more heavy hammer, it should be safe to delete or rename the entire adapter directory (ie, the one named something like 40:01:23:45:67:89), as bluez should recreate it from scratch when it is restarted.

You might also try running the bluetooth daemon manually to see if it prints out any useful debugging info (it doesn't seem to print out anything if it's working normally):
$ devel-su
# /sbin/initctl stop xsession/bluetoothd
# /usr/sbin/bluetoothd -d -n

I hope this helps you out.
-Ian

Jed said...

So did you end-up completing a "much slicker" solution here?
http://darkstarshout.blogspot.com.au/2012/08/nokia-n9-bluetooth-pan-usb-dummy.html#blue_pan

Jed said...

test.

DSS said...

Hey Jed - no, I never got back to packaging that up and now that the screen on my tablet is dead I'm not using this on the N9 anymore. I still use the script as is on my Linux boxes.

Jed said...

+1

Ben Lin said...

Hi Ian,

when I run blue-tether.py, I get the message:

Traceback (most recent call last):
File "./blue-tether.py", line 10, in
import glib, gobject
ImportError: No module named glib

I have 2 files under directory /usr/lib/glib-2.0
gio-querymodules glib-compile-schemas

How do I fix the import error in ./blue-tether.py?

Thank you,

Ben

DSS said...

Hi Ben,

The python glib module is generally bundled in the same package that provides the gobject module.

On the N9 that's the python-gobject package, while on my Debian system it's in the python-gobject-2 package.

Hope that helps,
-Ian

Ben Lin said...
This comment has been removed by the author.
Ben Lin said...

Thanks Ian. I installed the python-gobject module you suggested and got rid of the dependency Import Error.

I am still not able to get the successfully paired devices to work using bluetooth PAN, even with
the proper bluetooth host address in the config file or with the "-b" flag option.

I get the error "Error Connecting: org.bluez.Error.NotSupported: Operation is not supported".

I'm thinking if there is some place I need to configure in order for this to work.

Can you help?

Thanks,

Ben

DSS said...

Hi Ben,

I'm not certain what would cause that specific error - if it got that far than bluez is exporting the org.bluez.Network interface for it, but it didn't like the attempt to actually connect.

One thought I have is the device you are connecting to may be using the Group Ad-hoc network role instead of the Network Access Point role. I haven't played around with that role myself, so I don't know exactly what I'd need to change to get it to work.

Can find this line in the script (in the BluezNetMonitor _connect() method):

self.Interface = self.dev_network.Connect('NAP') # 'GN' / 'NAP' ?

And change it to this:

self.Interface = self.dev_network.Connect('GN')

Try using the script again and let me know what error you get with that change.

DSS said...

Also, what is the result of running this, replacing the address with that of the device you are trying to connect to:

sdptool browse 00:00:00:00:00:00|grep Name

When I run that against my tablet, I see "Service Name: Network Access Point" in the output - I'm curious to know if you also see that or if you see something else indicating it may be using GN or DUN instead.

Ben Lin said...

Thanks for your help on this.

I had to find and install bluetooth explorer app on my OSX box since it doesn't have sdptool commandline.

However, the text output of the SDP query from the tool was not in a easily readable format.


In any case, using the system information tool included with OSX, I was able to obtain the bluetooth services provided by my MacOSX machine.


Services:
Bluetooth File Transfer:
Folder other devices can browse: ~/Public
When receiving items: Prompt for each file
State: Disabled
Bluetooth File Exchange:
Folder for accepted items: ~/Downloads
When other items are accepted: Ask
When receiving items: Prompt for each file
State: Disabled
Bluetooth Internet Sharing:
State: Enabled
Incoming Serial Ports:
Bluetooth-Incoming-Port:
RFCOMM Channel: 3
Requires Authentication: No
Outgoing Serial Ports:
Bluetooth-Modem:
Address: 40-98-4E-28-D4-6C
RFCOMM Channel: 1
Requires Authentication: No


Modifying self.Interface = self.dev_network.Connect('NAP') in bluetooth-tether.py script, to self.dev_network.Connect('GN') and running the script again gave the following output:


~ $ ./blue-tether-new.py
Connecting...
bnep0 created
Press enter to close connection
Property Changed: Connected: 1
UP
Starting DHCP client...
Property Changed: Interface: bnep0
udhcpc (v0.9.9-pre) started
Property Changed: UUID: 00001117-0000-1000-8000-00805f9b34fb
socket failed!: Operation not permitted

Stopping DHCP client...
~ $
~ $ Write failed: Host is down



Do you see any clues from the output that could help get the bluetooth tether on the MacOSX machine working?

Ben

Michael Hill said...

Hi Ian,

Thanks for sharing your work, and sorry for the long-term support request (I just got my N9). Have you seen the error... 'Error Connecting: org.freedesktop.DBus.Error.UnknownMethod: Method "Connect" with signature "s" on interface "org.bluez.network" doesn't exist' ? It looks a bit like this bug: https://bugs.tizen.org/jira/browse/TIVI-1130 but I'm not sure how to proceed.

Thanks,

Mike