Friday, January 2, 2009

Bluetooth tethering from Ubuntu to Android G1

Update: Looks like this does not work with the new Cupcake OS :(

Yeah! I finally got this working. I got my Thinkpad T500 running Ubuntu Intrepid to connect via bluetooth to my Android G1. So the laptop connects to the internet through whatever internet connection the phone has (Edge, 3G, or Wifi). I'm writing this at a starbucks on my laptop, connected through my G1 to a TMobile hotspot (install the hotspot app on your G1 and reboot the phone. You'll see a bullseye in the notification bar when you wifi connect to the hotspot on the phone) Roughly the steps I did to tether were:

  1. Get 'adb' installed from the Android dev tools. I downloaded the whole sdk.
  2. The main starting point is the blog entry at: http://www.gotontheinter.net/content/second-g1-story-proper-bluetooth-tethering-how-short
  3. From there I got the files pand, iptables and bnep.ko and copy to /data/local/bin/, creating the dir if necessary.
  4. Get busybox from http://benno.id.au/blog/2007/11/14/android-busybox and install in /data/busybox (it looks like the directory is hard-coded in that binary somewhere)
  5. I did a bit of shell code to move to a subdirectory any binaries in busybox for which another version existed outside of busybox, except for ps. I figured I trust the android ones more, but Android's ps was missing the all important 'w' option
  6. Generate a file called /data/local/bin/tether.sh on the phone with the following contents:
    #!/system/bin/sh
    # first copy all files to /data/local/bin

    if ! `echo $PATH | /data/busybox/grep /data/busybox > /dev/null`; then
    export PATH=/data/busybox:$PATH
    fi

    pand_pidfile=/data/local/bin/pand.pid
    PAND_PID=0
    if [ -e $pand_pidfile ]; then
    PAND_PID=`cat $pand_pidfile`
    if [ "x" = "x$PAND_PID" ]; then
    PAND_PID=0
    else
    if ! (ps | grep /data/local/bin/pand | grep -v grep > /dev/null); then
    PAND_PID=0
    fi
    fi
    fi

    logfile=/data/local/bin/tether-log

    case "$1" in
    start)
    if ! lsmod | grep bnep > /dev/null; then
    echo "Adding bnep kernel module"
    insmod /data/local/bin/bnep.ko || exit
    fi
    if [ 0 -eq $PAND_PID ]; then
    echo "Starting pand"
    rm $pand_pidfile > /dev/null 2>&1
    PAND_PID=0
    rm $logfile > /dev/null 2>&1 # if it exists
    /data/local/bin/pand --listen --role NAP --devup /data/local/bin/blue-up.sh --devdown /data/local/bin/blue-down.sh --pidfile $pand_pidfile || exit
    fi
    echo "Success"
    ;;
    stop)
    if [ 0 -ne $PAND_PID ]; then
    echo "Killing pand at pid $PAND_PID"
    /data/local/bin/pand -K || exit
    kill $PAND_PID || exit
    rm $pand_pidfile > /dev/null 2>&1 # Looks like pand may have removed it for us
    sleep 1
    fi
    if lsmod | grep bnep > /dev/null; then
    echo "Removing bnep module"
    rmmod bnep || exit
    fi
    echo "Success"
    ;;
    *)
    echo "Usage: /data/local/bin/tether.sh {start|stop}"
    exit 1
    esac
    exit 0

  7. Then put the following into /data/local/bin/blue-up.sh:

    #!/system/bin/sh
    # blue-up.sh
    ifconfig bnep0 10.0.1.1 netmask 255.255.255.0 up
    /data/local/bin/iptables -F
    /data/local/bin/iptables -t nat -F
    /data/local/bin/iptables -t nat -A POSTROUTING -s 10.0.1.5 -j MASQUERADE
    /data/local/bin/iptables -t nat -A POSTROUTING -j ACCEPT
    echo 1 > /proc/sys/net/ipv4/ip_forward
    /data/busybox/udhcpd /data/local/bin/udhcpd.conf
    echo "Interface up " >> /data/local/bin/tether-log

  8. And the following goes into /data/local/bin/blue-down.sh:

    #!/system/bin/sh
    # blue-down.sh

    udhcpd_pidfile=/data/local/bin/udhcpd.pid
    UDHCPD_PID=0
    if [ -e $udhcpd_pidfile ]; then
    UDHCPD_PID=`cat $udhcpd_pidfile`
    if [ "x" = "x$UDHCPD_PID" ]; then
    UDHCPD_PID=0
    fi
    fi
    echo "udhcpd at pid $UDHCPD_PID" >> /data/local/bin/tether-log
    if [ 0 -ne $UDHCPD_PID ]; then
    echo "Trying to kill udhcpd at pid $UDHCPD_PID" >> /data/local/bin/tether-log
    kill -1 $UDHCPD_PID # 1 is SIGHUP
    rm $udhcpd_pidfile
    fi
    ifconfig bnep0 down
    echo 0 > /proc/sys/net/ipv4/ip_forward
    /data/local/bin/iptables -F
    /data/local/bin/iptables -F -t nat
    echo "Interface down " >> /data/local/bin/tether-log

  9. Make sure all '.sh' files and binaries in /data/local/bin have execute permissions. E.g., do:
    su
    chmod 755 /data/local/bin/*.sh
    chmod 755 /data/local/bin/pand
    chmod 755 /data/local/bin/iptables

    I think you can alternatively set execute permissions on your laptop before you copy the files over.
  10. And the following in /data/local/bin/udhcpd.conf:
    start 10.0.1.5
    end 10.0.1.5
    max_leases 1
    interface bnep0
    pidfile /data/local/bin/udhcpd.pid
    option dns 208.67.222.222 208.67.220.220 # Freedns dns servers
    option router 10.0.1.1
    option subnet 255.255.255.0
    option domain local
    option lease 1440 # 4 hours


  11. Next, on your laptop machine, add a line to /etc/network/interfaces to tell Ubuntu about the bnep0 interface:
    iface bnep0 inet dhcp

  12. After modifying /etc/network/interfaces, restart networking to pick up the change by executing:
    $ sudo /etc/init.d/networking restart

  13. Install the pand utility by adding bluez-compat:
    $ sudo aptitude install bluez-compat

  14. On my laptop, I have a script to start and stop things:
    #!/bin/bash
    case "$1" in
    start)
    if ps ax | grep sbin/NetworkManager | grep -v grep > /dev/null; then
    echo "Halting NetworkManager"
    /etc/init.d/NetworkManager stop || exit
    fi
    if ! (pand -l | grep bnep0 > /dev/null); then
    echo "Establishing bluetooth PAN connection"
    pand --connect **:**:**:**:**:** -n || exit
    if ! (pand -l | grep bnep0 > /dev/null); then
    echo "pand returned success but it looks like the connection was not made"
    exit 1
    fi
    sleep 1
    fi
    if ! (ifconfig | grep bnep0 > /dev/null); then
    echo "Bringing up interface bnep0 with dhcp"
    if cat /var/run/network/ifstate | grep bnep0; then
    echo "ifup/down thinks interface is still up. Down it first"
    ifdown bnep0
    fi
    ifup bnep0 || exit
    fi
    echo "Success"
    ;;
    stop)
    if (ifconfig | grep bnep0 > /dev/null); then
    echo "Taking down bnep0"
    ifdown bnep0 || exit
    fi
    pand -K || exit # MUST be run as sudo, though it does not error out otherwise.
    if ! ps ax | grep sbin/NetworkManager | grep -v grep > /dev/null; then
    echo "Restarting NetworkManager"
    /etc/init.d/NetworkManager start || exit
    fi
    echo "Success"
    ;;
    *)
    echo "Usage: sudo laptop-connect-tether {start|stop}"
    exit 1
    esac
    exit 0

    Where you replace the '**:**...' with your phones hardware bluetooth address. [ Update: 'thwarted' says You can get your phone's bluetooth address in Settings | About Phone (bottom) | Status | Bluetooth Address (3rd from bottom) ]


So to use this, run /data/local/bin/tether.sh start on your phone and then run the above script on your laptop with 'start' or 'stop' as desired. Note that to start tether.sh:
  • tether.sh file must be executable (see above)
  • you need to su before running
  • You must invoke the binary with an explicit pathname, e.g. './tether.sh' or '/data/local/bin/tether.sh'. Just cd'ing into the dir and typing 'tether.sh' does not work.


If you get a not found error when invoking tether.sh and you swear it really is there and you are trying this from windows, it may be that pasting the code to a file appended '^M' to the end of each line. Consider using dos2unix to remove the extra characters.



Sorry this is a bit rough, but let me know if you find this useful or find bugs.

38 comments:

  1. I stumbled upon this entry, and tried it out for myself.
    I got an odd output though, and have yet to be successful

    I am logged in as root
    # id
    uid=0(root) gid=0(root) groups=3003(ient)
    # tether.sh start
    tether.sh: permission denied
    # su tether.sh start
    Starting pand
    tether.sh: /data/local/bin/pand: permission denied



    any thoughts?

    ReplyDelete
  2. @Patrick: you have to make tether.sh executable:
    chmod 4755 tether.sh

    I'm stumbling about another problem: on my ADP1, udhcpd fails trying to open it's priviledged socket:

    > udhcpd (v1.8.1) started
    > udhcpd: socket: Operation not permitted

    did I miss something?

    ReplyDelete
  3. You can get your phone's bluetooth address in Settings | About Phone (bottom) | Status | Bluetooth Address (3rd from bottom)

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. # tether.sh start
    gives me permission denied

    # chmod 4755 tether.sh
    # tether.sh start
    gives me tether.sh: not found, but when doing ls the file is there

    # sh tether.sh
    script finally starts to run but then i get this error
    tether.sh: 14: Syntax error: "elif" unexpected (expecting "then")

    I looked at the script it looks the same as yours and it has the then that it is expecting

    ReplyDelete
  6. Thanks for the comments! I updated the posting. The most important update is that you need to make all '.sh' and binaries executable with a chmod 755. Also, invoke tether.sh via ./tether.sh or the full pathname. Just saying tether.sh does not work.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Moussam: Wow, maybe the versions of shell differ across the phones? I changed the script to replace 'elif' with 'else if'. See if that helps?

    ReplyDelete
  9. Ok I now tried the updated scripts with your suggestions and here is what I got. I am running all as su and I am in the data/local/bin directory...

    # ./tether.sh start
    ./tether.sh: not found

    # /data/local/bin/tether.sh start
    /data/local/bin/tether.sh: not found

    # sh tether.sh
    script finally starts to run but then i get this error
    tether.sh: 23: Syntax error: expecting "in"

    I also forgot to mention before I am running the ADP1 build not RC30. JFv1.31_ADP1.zip

    ReplyDelete
  10. Moussam,
    From the /data/local/bin directory, as su, can you post the output of 'ls -l' and 'pwd' ?

    ReplyDelete
  11. using adb shell ...

    # ls -l
    ls -l
    -rwxr-xr-x 1 0 0 662 Jan 19 19:05 blue-down.sh
    -rwxr-xr-x 1 0 0 419 Jan 19 19:04 blue-up.sh
    -rw-rw-rw- 1 0 0 356212 Nov 26 17:36 bnep.ko
    -rwxr-xr-x 1 0 0 109020 Nov 26 17:43 iptables
    -rwxr-xr-x 1 0 0 19784 Nov 27 02:56 pand
    -rwxr-xr-x 1 0 0 1492 Jan 19 19:03 tether.sh
    -rw-rw-rw- 1 0 0 265 Jan 19 19:12 udhcpd.conf
    # pwd
    pwd
    /data/local/bin
    #

    ReplyDelete
  12. I should also probably mention that I was able to get tethering over bluetooth working using the method described on the web page from the link you mentioned above...
    http://www.gotontheinter.net/content/second-g1-story-proper-bluetooth-tethering-how-short

    ReplyDelete
  13. Moussam, I'm stumped. That's really odd that you get the 'not found' error when executing '/data/local/bin/tether.sh'. The output of 'ls' looks fine. I'm also confused as to why prefixing the command with 'sh' makes it find it when it didn't before. Maybe restart the terminal emulator? Or try copying over a simple shell script to the phone and see if you can execute it?

    ReplyDelete
  14. This comment has been removed by the author.

    ReplyDelete
  15. I got it working finally. Stupid windows carriage returns. I ran dos2unix on the files and i can now run it. seems like sh can handle the windows carriage returns, but the shell couldn't or something.

    Now that I have it running, is there any way to create a shortcut that will running the script without having to open terminal and type it in?

    ReplyDelete
  16. It works when I manually enter in the IP settings, DHCP does not seem to be working for me. I didn't use your laptop script though, I am in a windows environment and just connected to the device doing the windows method with Automatically get an IP address (DHCP) enabled. An IP would not get resolved and then windows defaults to an autoIP of 169.*.*.* So I set a static IP of 10.0.1.5/24 with the 10.0.1.1 gateway and it worked.

    ReplyDelete
  17. Moussam, I'm glad you figured out the windows carriage return issue. Not sure why dhcp didn't work on windows...,

    ReplyDelete
  18. i finally got dhcp working. I could not get it to work with udhcpd, so I instead used dnsmasq, which happens to have a smaller footprint anyways. I had to alter blue-up.sh and blue-down.sh slightly so that the scripts executed and killed dnsmasq isntead of udhcpd. below is the dnsmasq.conf file config info i used:
    no-resolv
    no-poll
    server=208.67.222.222
    server=208.67.220.220
    interface=bnep0
    dhcp-range=10.0.1.5,10.0.1.5,4h
    dhcp-leasefile=/data/local/bin/dnsmasq.leases
    pid-file=/data/local/bin/dnsmasq.pid

    in the blue-up.sh file I had to put this line in place of the udhcpd line:
    /data/local/bin/dnsmasq --conf-file=/data/local/bin/dnsmasq.conf

    ReplyDelete
  19. I think udhcpd is part of the busybox binary, so I'm not sure it costs any extra space. Though, dnsmasq I think can proxy DNS requests as well, which would eliminate the need to hardcoded DNS servers in the dhcp config. It'd be sweet to wrap this stuff in an android app.... I've downloaded the SDK, but haven't had any time to dig into it.

    ReplyDelete
  20. I have been using the SDK for a little bit now. I am not sure if you can execute a script or any shell command from the SDK. I haven't tried it, but I don't remember seeing it anywhere. I will look into it though.

    ReplyDelete
  21. I got an android application running that will execute the start and stop script for you from a touch of a button. I just need to clean up a couple of things. The standard SDK does not include running shell commands, but someone found a work around. Take a look at http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App

    ReplyDelete
  22. Josh: FYI moussam was seeing a type of "not found" error that confuses just about everyone. If you run a script with a #! line, and the interpreter after the #! line is missing, you will get the same "not found" error as though the script itself was missing. (This combines badly with the dos2unix problem he was having, which causes the interpreter to be "/bin/sh^M".)

    ReplyDelete
  23. Thanks, gwillen, that makes sense, unfortunate though it is. Talk about an unhelpful error message :(. Added a sentence about this to the post.

    ReplyDelete
  24. moussam, that's exciting! Thanks for posting the link as well - that's cool. Let me know if there's anything I can do to help on my end.

    ReplyDelete
  25. I made an update to the app. It now downloads all the necessary files from my webserver if they have not already been downloaded. So there is no need to copy any files over, all you got to do is install and run my app. I am currently working on two issues before I release the app. Josh if you could help me with one of them that would be great. The one I need help on is that right now the app requires su access to run the insmod command of bnep.ko. Is there any other way around this so that we do not need su access? If we can figure a way around it, this app could then run on nonrooted phones. The other issue that I am working on is automatically turning bluetooth on and off on the phone if it is not running when you try to start the service.

    ReplyDelete
  26. A few thoughts:
    1. poked around on web. not sure how to avoid root requirement for insmod. insmod stuffs code into the kernel, so I imagine it's pretty particular about privilege level. Maybe post on xda-developers?
    2. Is it possible to package all necessary files along with the app? That might be more convenient and flexible, should you want to host the app somewhere else at some point. Or, maybe you could bundle up all necessary files into a library package? I think a saw a few of those on the marketplace.
    3. My email is gmail.com a-t redstone, but reversed, if that's easier.

    ReplyDelete
  27. This seems like it would work for a PAN environment. Any idea what I should do to make it work for a DUN setup? I tried making changes to use dund, but unlike pand there aren't any up/down delegates.

    I also played around with sdptool and found hcitool reporting some promising results... still couldn't quite get my TomTom to pair as a data connection, which is what I was trying to do.

    ReplyDelete
  28. Sorry, I'm haven't played around with DUN much. Do you need the up/down delegation feature with DUN?

    ReplyDelete
  29. This comment has been removed by the author.

    ReplyDelete
  30. Note that in order to get the client side running with ubuntu 8.10 I had to do add the following line to my /etc/netwok/interfaces:

    iface bnep0 inet dhcp

    The error message without the line in interfaces was "Ignoring unknown interface bnep0=bnep0.".

    Also, the package bluez-compat needs to be apt-get install'd.

    Posting this through my G1 on 3G, yay.
    Huge thanks!

    ReplyDelete
  31. Hi Kap,
    Yes, I'd forgotten those bits. Thanks for the correction! I've updated the post.
    Josh

    ReplyDelete
  32. I'm not exactly sure what I need to make DUN work... I just noticed that it is different than PAN, both in implementation details and support scripts.

    ReplyDelete
  33. Hi Josh,
    I do not know how to do the step 4 above.
    Would you like to show me the steps in details?

    ReplyDelete
  34. Looks like this doesn't work anymore with updated Android OS's, including Cupcake. I had switched to:

    http://code.google.com/p/android-wifi-tether/

    but that also seems to not work with Cupcake.

    ReplyDelete
  35. aNetShare should work with Cupcake
    But I can not manage to connect my linux to Ad-Hoc network :(

    ReplyDelete
  36. Awesome Ubuntu script, works perfectly for me using Wireless Tether for Root Users (google it) on my G1, even with Android 1.5. Posting this from my EEE tethered to my G1 right now, thanks for posting this!

    ReplyDelete
  37. How can one grant the (bash) script SuperUser context like one can applications?

    Then I can crontabs iptables rules addition every boot

    ReplyDelete
  38. I can confirm this still works. Similar setup to above poster: using Ubuntu 11.04, a rooted Droid 1 + Wireless tether for root users and cyanogenmod 7.1.

    ReplyDelete