Port forwarding

I'm trying to understand iptables on Linux 2.4 or 2.6, for a fairly simple task, but it doesn't seem to do anything.

My desktop machine at work is behind a firewall. As a department, we have control over just one server that has a range of ports open to the wider internet. So I either ssh twice, or tunnel as needed to access services on my own machine. I also wrote a tiny C program to open a socket and forward all traffic back and forth; I run it from inetd on the server, so now if I ssh to port XYZ on the server, that will be forwarded directly to my desktop.

Unfortunately, things aren't working so well lately, and I think it might be the fault of my little C program. I will ssh through it, only to have the connection reset after a few idle minutes. This doesn't happen when I ssh directly to the server, only through my forwarder program.

It's so bad that when trying to synchronize between home and work with unison, my home machine takes much longer to look for changes, and by the time it tries to communicate changes to the work machine, the connection has died.

So it seems like the right way to do this is throw away my crummy C program and just do port forwarding in the kernel. The tutorials and FAQs make it seem easy:

  iptables -t nat -A PREROUTING -p tcp --dport PORT \
      -j DNAT --to ADDRESS:22
The rule shows up in the tables just fine, but it doesn't seem to change anything when I try to connect to the specified port. I wrote a 1 to /proc/sys/net/ipv4/ip_forward, as suggested. I checked that the modules ip_tables and iptable_nat were loaded. I've tried it both on 2.4 and 2.6 kernels. Still no changes.

My Linux kernel knowledge is fairly good these days, but networking is certainly the weak spot. In fact, in obtaining my 3 degrees in CS, I don't think I ever once took a networking course! Not that it would necessarily help me now...

Update (23:35) Some tracing with tcpdump revealed what was happening. I compared the packet-slinging going on with a successful connection to that with my faulty iptables rules. It turns out (I presume) that the DNAT (destination network address translation) needs a corresponding SNAT (source NAT). I thought I gathered from the various tutorials and docs that iptables does the reverse translation for you. Ah, but now I understand... this isn't really the reverse translation; it's the same packet, but now the other end will know where to send the ACK. Blah, at least it works now. Here's the successful configuration, with the actual IP addresses replaced by SERVER and DESKTOP.

# Generated by iptables-save v1.2.11 on Wed Jul 26 23:30:21 2006
*nat
:PREROUTING ACCEPT [7757:741448]
:POSTROUTING ACCEPT [4166:279366]
:OUTPUT ACCEPT [4175:279906]
-A PREROUTING -p tcp -m tcp --dport 2000 -j DNAT --to-destination DESKTOP:22
-A POSTROUTING -d DESKTOP -p tcp -m tcp --dport 22 -j SNAT --to-source SERVER
COMMIT
# Completed on Wed Jul 26 23:30:21 2006
# Generated by iptables-save v1.2.11 on Wed Jul 26 23:30:21 2006
*filter
:INPUT ACCEPT [939121233:266118375723]
:FORWARD ACCEPT [316:50624]
:OUTPUT ACCEPT [1194943253:1032490859121]
COMMIT
# Completed on Wed Jul 26 23:30:21 2006
So now I can throw away my little clunky inetd-spawned C program. But still the connection isn't staying alive long enough for unison to do its thing. ARGH.

©20022015 Christopher League