Now that I’ve got my new router set up (it’s working fine btw) I decided to dig a bit deeper into the firmware possibilities. Warning: this post is mostly martian, and it’s long.
The first thing I wanted to investigate was the possibility of port triggering. Port triggering is an alternative to static port forwarding: when a packet goes out on a given port, it can trigger the opening of one or more inbound ports. This is useful if you have several machines behind the NAT box and you want them all to use apps like say, Bittorrent (which communicates initially on 6969 and listens on 6881-6889 or so).
So I did a bit of research. The router runs the 2.4.30 kernel with iptables-1.3.3 (I’m using the OpenWRT whiterussian firmware). So I downloaded the OpenWRT source and started spelunking. It became clear that there were a couple of ways to achieve smooth Bittorrenting.
The first way would be to build a netfilter kernel module to track bittorrent connections. There are similar modules for several protocols aready. Perhaps the canonical example is FTP: when a connection goes out on TCP 21, the module tells the kernel to expect TCP 20 incoming. I poked around the web for a while and found that Jonathan Bastien-Filiatrault is (or was?) developing just such a module over at x2a.org. I found his code in Google caches and took a look. But it seemed to be unfinished. I may still pursue this avenue.
The next way to achieve my goal is of course the aforementioned port triggering approach. And in fact since this approach is applicable in general to more things than Bittorrent, I decided to tackle it. After reading lots of web material, I decided the best way to go was to download the stock Linksys firmware source and try to add its triggering functionality to the OpenWRT source I had.
It took me a while to understand how the OpenWRT build process works. Then I had to figure out how to add the required code and make sure that the build process pathways through Makefiles etc were correct. The easy part was iptables – all I had to do was add a source file in the extensions directory, add a header file in the linux headers tree (of which iptables has a separate copy – it doesn’t share the linux headers used in the kernel build, which made it easier), and make a trivial Makefile change.
So I did that, and built. It didn’t build my new .c file. No .so file was forthcoming. Hmm. A dependency issue? So I tried a make dep. No dice. Make clean it is then. And poof, the directory vanished. Not just the .o files, but the entire output directory. This was the point where I really grokked how the OpenWRT build process works. It downloads tarfiles for each package and keeps them in a dl directory. When it builds, it untars them to a build directory, applies patches, and then builds. OK, well then all I had to do was apply my changes to the iptables tarfile, and it should work. At this point I did not try to create a patch to achieve the same result – I went for the easy option. And it worked! I had libipt_TRIGGER.so in my iptables directory.
Next I looked through the kernel source aiming to do the same thing. First I tried the easy option. But I ran afoul of the many patches that OpenWRT applies to the kernel before building. I’d changed something (Config.in, Makefile, a header) and I confused the patch process which wasn’t expecting to see my changes in there and therefore couldn’t work out how to add its thing. So I had to do this the harder way: by making my own patches.
While spelunking through the Linksys kernel code, I also noticed ip_conntrack_sip.c and ip_nat_sip.c which I thought would be useful, so I went ahead and included them in my patching. In retrospect, this made the job harder. It was easy enough to put the code and Makefile changes etc into the kernel tree. The harder part was putting the Makefile and Config.in lines in the right place so that other patches to the same files would not get confused. After a couple of hours of mucking about I had a solution. This, unlikely as it may seem, was also the first time I’d really used diff and patch in anger. They’re useful!
So, now I had iptables working, and a patch for the kernel that was successfully applied by the OpenWRT build process and that didn’t cause anything else to barf. And when I ran make, somewhere in the flurry of output I was prompted to select my new code: I got the Y/n/m (include, don’t include, include as module) prompt! So I selected the new stuff as modules of course. And in the fullness of time, everything was happily built.
Next job was to try to use it. I scp‘ed the files over to the router and put them in the right place. First I tried out loading the iptables trigger module by giving it a rule with -j TRIGGER. Rightly, it complained that there was no such target. Then I tried to load the ipt_TRIGGER kernel module. It told me that it would taint the kernel because of having no license, but otherwise was OK. The next try with -j TRIGGER was accepted by iptables! So apparently now I have port triggering working successfully.
This was last night. This morning I’ve cleaned up my hacks and I have the trigger stuff as two patch files that get cleanly applied by OpenWRT during the build. The stage I’m at now is figuring exactly how to instruct iptables to do what I want.