Perl and Net::Telnet to the rescue of failing hardware
A good friend of mine donated me an old Flybook V33i, completely functional except for the screen inverter. This faulty part makes the whole laptop unusable to normal people.
I installed Debian 7 on this laptop (plugging it into an external monitor) and decided to leave it in Lecce (I like to have some sort of server in every place I am somehow connected).
After the hardware/software setup, I configured dynamic dns on my home router, a TP-LINK TD8841. It is a cheap piece of hardware that served me very well in the past years, but today I discovered something very bad: after several years of service, some internal long-term memory components reached end-of-life point and got unwritable, resulting in my router resetting all of its settings upon reboot.
Ok, how do I solve this? I thought I could mechanize it web interface, but it’s a mess.
Luckily this is a GNU/Linux-based router (although not advertised on the package) and a stripped-down environment is available via the telnet administration “interface”.
The telnet administration interface is actually a camouflaged root shell.
There is no ls, but there is echo and cat, and that’s quite enough to go digging the filesystem and see what’s available. Interesting files are also /proc/cpuinfo, /proc/meminfo and /proc/mounts (there is no ‘mount’ utility, apparently).
Being a GNU/Linux-based router basically means that all the routing things are handled via the iptables command-line utility, the very same utility found in GNU/Linux based computer (the ones we all use day to day). It’s fancy, because I can mess up ip routing the very same way I do on my laptop.
Setting up DNAT is simple (two rules: a rule in FORWARD chain and a rule in PREROUTING), but configuration is going to get lost upon reboot.
How do I solve this problem?
On one hand, my router is going to be rebooted and forget routing settings just every time. On the hand, routing setting are not needed when my laptop is not connected to the network, so the most effective way to handle this situation is to write a Perl script to add network routing rules automagically when they are missing.
And that’s what I did!
It’s been fun to write, because I used Net::Telnet to simulate a telnet client, and figured out two things:
- There’s a whole IPTables namespace on CPAN (think of IPTables::Parse, IPTables::rule and other). And it could be fun to use
- Net::Telnet is quite funny to use, and highly effective.
So, for short, the script is on my github profile.
But in order to do things the right way, some thinking is required:
- My laptop has a battery, my router does not. This means that my router could be rebooted but while my laptop could be still alive. This means I can’t just run the script on boot.
- I can’t just add routing rules upon pre-existing routing rules: duplicate rules would slow down the whole routing process and, taken to the extreme, fill all the memory of the router. That’s no good.
Solution to this problem is:
- Run the script as a job in cron
- Look at the status of the routing table before taking any action, in order to avoid adding the same routing rules multiple times.
How to implement solution #2 ?
It’s simple: destination nat rules reside in the PREROUTING chain withing the nat table and have a very specific format:
DNAT tcp -- anywhere anywhere tcp dpt:xxxx to:192.168.xx.xx:yy
In particular, the later part (tcp dpt:xxx to: …) is very distinctive. It can be a good pattern to look for. I decided to use it as test pattern, and worked pretty well.
What else? Nothing in particular yet, but feel free to ask any question.