Etiquetas

, , ,

Edited:
There is a version 2.0 available at the end of the post, but its still good to read through it to realize how it works and that it took some time for me to put this up 🙂

2nd Edit:
Fixed code (indentations were gone thanks to wordpress!, and they are needed for python) and made some small changes. Also included an udev rule file to automatically run the script when the phone is connected, and also take down dhclient when it is disconnected.

3rd Edit:
New udev rules are proposed, since the ones in the prev¡ous edit depend on the MAC address assigned to the phone, which changes everytime it is released. This new method is based on the serial number of the phone, and is therefore more reliable.

Motivation:

Hi. As I am now in a foreign country, I do not have 3G/UMTS/(whatever it is called) internet access on my HTC Desire S. Also, in the place I am living, there is no internet WiFi access available (only over wired LAN).

In these conditions, the only way to hook up my phone to the network is to boot into Window$ XP and use the standard HTC drivers to use the «connect to internet through pc» (also known as «reverse tethering» in some places) on the phone. BUT as the W. XP on my laptop is running slower every time I use it, I wanted to give it a try and set up the reverse tethering on linux. Oh, and one little thing more: myphone is not rooted (yet).

After a whole afternoon playing with perl (which I do not know), python (this I know just a little), wireshark and a bit of fumbling with iptables,dhclient, and some more, I got it working.

Hands on:

I had no problem in getting the phone recognized by the kernel (I am just archlinux; just by connecting the cable, a usb0 device is found and easily configured. At this point, you should set up dnsmasq (any other DNS server should do too) to provide the phone with a DNS server, and set ip masquerading over iptables. I will not describe these two procedures, since they are easy and are very well documented on the net. Also, I am no expert, and what worked for me might not do it for you; besides, I am not any kind of expert, and maybe would only make your troubles worse.

Once both of these things are done, you will need some kind of program to «talk» to your phone. This is the script that I made:

#! /usr/bin/python2

# fakehtcnat.py, by invik

import sys,socket

if len(sys.argv)<2:
  print "Usage: " + sys.argv[0] + " [phone IP]\n"
  exit()

HOST = sys.argv[1]
PORT = 6000
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_IP, socket.IP_TTL, 128 )
s.connect((HOST, PORT))
s.sendall(chr(0) + chr(2) + chr(0) + chr(0))
s.close()

print "Sent."

Script can be downloaded here.

It is a very simple script: it just connects to the phone and sends it some kind of «confirmation sequence» (in hex 00 02 00 00 to port 6000 on the phone) to allow the phone to recognize it is connected to the network. As you can see, you just have to invoke the script with the IP of the phone as only parameters.

But do not do it yet.

Once you have the script, you will also need some app in to access the console on your phone (I use ConnectBot, which is also a good ssh/telnet app). But you do not need to be root.

Ok, let us do it.

Connect your phone to the computer with the USB cable. (Probably) Your phone will ask you what you want to do: charge only, access its storage card, HTC Sync, use the phone as USB modem, or passthrough internet connection. Chose this last option.

Now, go to your linux box, open a console and (after a few seconds), type:

dhclient usb0

After a few seconds, the shell will come back, and usb0 should be up and with an IP in the private range.

By now, if you did well with the masquerading and the DNS server, your phone should already be able to access the internet… but it still does not realize about it. This means: you can go to any app and use it as it was connected to the network; it should work. The problem is that the phone thinks it is not connected yet; it still needs a particular signal to realize it, and this we will provide with my small script. If not, after some minutes, it will show an error message, and shut down the passthrough connection again (and then it will not be connected any more).

For this, we need the phone’s IP. This is NOT the IP assigned to usb0 in your computer. To find this out, go to your console app in the phone (in ConnectBot, open the «Local» connection) and write

ifconfig usb0

There it will tell you its IP. Lets suppose it said «192.168.99.245». Now, you return to your console and, if you called the script fakehtcnat.py, you should issue:

./fakehtcnat.py 192.168.99.245

It will instantly answer «Sent.» and exit. And your phone will show the «Connected to internet» message and the icon with the two arrows.

And this is all. If you have any questions or need help, just leave a comment and I will try my best.

Edited:

I had a second look at the script and improved it a little: now it puts up usb0 through dhclient, fins out the IP of the phone by reading the last entry in the dhcp lease table, so you do not need to do the ConnectBot thing to find the IP address (but Connect is still a nice app though!), and then sends the message.

This means you just have to set up the masquerade, the dns server, connect the phone and run the script.

Eventually, after disconnecting the phone you should kill the dhclient daemon, because it keeps running in background, do not know why, since it will not give an IP to the phone if is connected again.

The script now looks like this:

#! /usr/bin/python2

# fakehtcnat.py, version 2.1 by invik

import io,socket,subprocess,sys, time

dhcpfile = "/var/state/dhclient/dhclient.leases"
srvrstr = "option dhcp-server-identifier"

print "Waiting for the phone to come up...\n"

# To send things to trash
o = file("/dev/null","w")

# Wait up to 10 seconds waiting for usb0 to show up
i=0
while (i<10):
  rtn = subprocess.call(["/sbin/ifconfig","usb0"], stdout = o, stderr=o)
  if (not rtn): break
  time.sleep(1)
  i += 1

if (rtn):
  print "ERROR: Timeout waiting for usb0 to come up\n"
  sys.exit(1)

# Get a dhcp address
rtn = subprocess.call(["sudo","/usr/sbin/dhclient","usb0"], stdout = o, stderr=o)
if (rtn):
  print "ERROR: not able to get IP from DHCP\n"
  sys.exit(2)

# Check that usb0 is up and has IP
pipeout = subprocess.Popen(["/sbin/ifconfig","usb0"],stdout = subprocess.PIPE,stderr=subprocess.STDOUT)
rtn = pipeout.communicate()[0].find("192.168.99")
if (rtn<0):
  print "ERROR: usb0 is not up or has not IP\n"
  sys.exit(3)

# Read dhcp lease file and get phone's address
f = open(dhcpfile,'r')
d = f.read()
u = d.rfind("interface \"usb0\"")
l = d.find(srvrstr,u)
e = d.find(';',l)
host = d[l+len(srvrstr)+1:e]

print "Phone is up at IP = " + host + "\n"

# Send activation command to phone
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_IP, socket.IP_TTL, 128 )
s.connect((host, 6000))
s.sendall(chr(0) + chr(2) + chr(0) + chr(0))
s.close()

print "Connection signal sent.\n"

Script should be preferraby downloaded from here.

2nd Edit:
By putting the previous script into /usr/bin, and the following rule file into /etc/udev/rules, your phone should automatically configured when connected and set to use pass through connection. It should also kill dhclient when the phone is disconnected again.

For this rule to be specific (and usable) for your phone, you should change «xx:xx:xx:xx:xx:xx» to the MAC address of yor phone. If you have more than one phone, duplicate the first line starting with «KERNEL» as many times as needed (and replace the «xx:xx:xx:xx:xx:xx» for each MAC!). If you have many phones, you could also delete the ‘ATTR{address}==»xx:xx:xx:xx:xx:xx»,’ part in the first line for it to be unspecific about what is connected (be careful: if you have any other usb device which hooks into the «net» subsystem, the script will be called too!).

The MAC address can be found by issuing:

udevadm info -a -p /sys/class/net/usb0

The MAC address will be referred as $ATTR{address}.

# 02-htcnet.rules - by invik
KERNEL=="usb[0-9]*", SUBSYSTEM=="net", ATTR{address}=="xx:xx:xx:xx:xx:xx", ACTION=="add", RUN+="/usr/bin/fakehtcnat.py"
KERNEL=="usb[0-9]*", SUBSYSTEM=="net", ACTION=="remove", RUN+="/usr/sbin/dhclient -x $kernel"

# Remember to change the "xx:xx:xx:xx:xx:xx" address to the MAC addres of your phone!

This can also be downloaded here.

3rd Edit:
After some time using the udev rules described in the previous edit, I found out that the MAC address of the phone changed every time it is rebooted (!!!), therefore making the previous rules useless unless edited with the new MAC address. This edition had to be redone everytime the phone was rebooted.

A better approach is to use the internal serial number of the phone, which can be found by connecting the phone, activting the «direct internet connection» feature, and issuing the command:

udevadm info -a -p /sys/class/net/usb0 | grep serial

The right serial number is the one starting with «HT». Once you have it, substitute it in this udev rules:

KERNEL=="usb[0-9]*", SUBSYSTEM=="net", ENV{ID_SERIAL_SHORT}="HTxxxxxxxxxx", ACTION=="add", RUN+="/usr/local/bin/fakehtcnat.py"
KERNEL=="usb[0-9]*", SUBSYSTEM=="net", ENV{ID_SERIAL_SHORT}="HTxxxxxxxxxx", ACTION=="remove", RUN+="/usr/sbin/dhclient -x $kernel"

# Be sure to change the "xxxxxxxxxx" to the right serial number!

It can also be downloaded here.

As with previous rules, make sure that the fakehtcnat.py script is in the right directory, and has proper execution rights.