This is going to be the Ultimate Wi-Fi Hitchhiker’s Guide to Tunnelling.
I’m going to start with the basics, and end up presenting my own scripts and my own setup to create an Ultimate, almost 100% transparent tunnelling setup, without root permissions on the gateway.
What SSH Provides
Situation 1: A –> B –> C
If you are A and you want to connect to an HTTP proxy running on C on port number 3128, but you have access only to B, and B has access to C, this is what you would do:
arnold@A:~$ ssh -L 8000:C:3128 B -N
(B/C are IP address or host names.) This creates a port 8000 locally which maps to port 3128 on C.
Situation 2: C —> A, but A –/–> C
Ok, suppose C is behind a complete firewall, and you can’t even access B, or C directly from A. But C can access A. Well, let’s assume now that you have physical access to C (or ssh access via some long tedious route), well, then connect to C and do:
arnold@C:~$ ssh -R 8000:A:3128 A
Very useful if somebody behind a firewall wants you to help out with system administration remotely.
Situation 3: You just want a “transparent” proxy, and you already have a connection from A –> C
What we do here, is create a SOCKS proxy, which is a kind of super-proxy, that can proxy just about any kind of connection, as long as your client program supports SOCKS. Typically this is what you’d do:
arnold@A~$ ssh -D 9000 arnold@C -N
Now go to your Gnome Network Proxy setting and set your proxy to use SOCKS, with localhost:9000. You might have to manually set it for applications that don’t lookup the Gnome proxy settings. If you’ve done this right you should be able to do almost everything like chatting, browsing etc.
ssh -D is useful, but it’s not convenient when you want a “permanent” transparent solution. For one thing many command line applications don’t support SOCKS by default. We can sort of fix this: Install tsocks, edit the proxy settings in /etc/tsocks.conf, and run tsocks bash. Any command you run now will transparently use the SOCKS proxy you set up.
But that’s not good enough.
Situation 4: A –> C, and you have root access on both A and C
… and you’re running Linux/BSD etc.
arnold@A~$ sudo ssh -w 0 root@C -N
This creates a “virtual” networking device (tun0) on both ends, and connects them, basically creating a VPN. But this step just creates the connection, it does not set up the routing. I can’t show you all the steps here, but this is basically what you do:
arnold@A~$ ifconfig tun0 10.0.0.1 pointtopoint 10.0.0.2
arnold@C~$ ifconfig tun0 10.0.0.2 pointtopoint 10.0.0.1
You should now be able to ping 10.0.0.2. Now set up iptables and NAT on C. (Sigh, I can’t explain those here, and really I don’t know much of it. But you’ll see a few examples of iptables further down.) Once this is set up, you need to set the route on A:
arnold@A~$ sudo route del default dev eth0
arnold@A~$ sudo route add dev eth0 # you still need your SSH to run
arnold@A~$ sudo route add default gw 10.0.0.2
Too messy? Of course it is. Plus, you need root access on the server. We can do better.
There are multiple “solutions” to the problem. The first one that came to my mind, was to create a tun0 virtual device locally, which maps each connection to a local SOCKS proxy. Fairly simple idea, turned out messy to code because I didn’t know how to easily write a userspace TCP stack that changed the raw packets from the tun device to a stream. I googled a lot but found just one program that claimed to do this, and that was written in C#, and had some GUI code in it, and I definitely didn’t feel comfortable running it. (for the record, socks-tun on Google code.)
So here’s what I finally did: Let’s use iptables to reroute all outgoing TCP streams to a local daemon. This daemon at this point does not know the destination of this stream, but with a little effort it can find that out by looking at /proc/net/ip_conntrack. Now it just uses tsocks to finally route this connection via SOCKS. Simple enough, the code is here (and git clone git://github.com/tdrhq/tunnel-scripts.git), and here’s what I did:
arnold@laptop:~/tunnel$ gcc lcat.c io_loop.c my_socket.c -g -o lcat.
For the following example, I shall use 220.127.116.11 as an example, since it’s CMI’s IP address, although I would hope that the rest of you reaching here via Google don’t just copy and paste this.
Enable ip_forward: (I’m not sure if you need this step, but still…)
sysctl -w net.ipv4.ip_forward=1
Reroute all connections to destinations other than 18.104.22.168 to a local port:
sudo iptables -t nat -A OUTPUT -p tcp -d \! 22.214.171.124 -o wlan0 -j DNAT --to-destination 127.0.0.1:9000
(Change wlan0 to your interface.)
Now create your SSH socks proxy:
ssh -D 3128 126.96.36.199 -N
Now edit /etc/tsocks.conf with the proxy as localhost and port as 3128. Also set the SOCKS version to 4. We’re almost done:
arnold@laptop:~/tunnel$ sudo tsocks bash
root@laptop:/home/arnold/tunnel# ./lcat -p 9000 -t
And from now on all TCP connections are 100% tunnelled.
Caveats: DNS lookup is not TCP based. The iptables command above routes only TCP connections. It should be possible to route UDP via SOCKS too, however I don’t have support for that in my code yet. So, right now, you better have a valid nameserver to use. Also, because of this same reason, ping won’t work, and so don’t expect to use it to test connectivity during any of the steps.
More notes: SSH tunnels time out if inactive.
Being a responsible Tunneller
As a tunneller, you don’t want to be misusing your gateway’s bandwidth. Let’s all just be resonsible tunnellers. Also, what’s your reason for tunnelling? Right now, mine is that I’m using my neighbor’s wireless, and I don’t want to get caught. (While watching videos, you don’t really need the full bandwidth, but it uses it anyway in pointless buffering.) My lcat script can be used for bandwidth throtling too. You need to run an additional lcat, before the ssh step. To limit the speed to 100 kbps:
arnold@laptop:~/tunnel$ ./lcat -p 6000 -h 188.8.131.52 -s 100
arnold@laptop:~/tunnel$ ssh -D 3128 -p 6000 arnold@localhost
While lcat is running, you can also type in a new speed to change the speed on-the-fly.
And for completeness…
… let me mention Corkscrew for tunnelling an SSH session over an HTTP proxy.