Wednesday, January 16, 2013

Router Breaks SIP? Work Around It.

Recently I had the misfortune of dealing with a Cisco WRVS4400N router in a medium-sized business environment. It worked fine for almost everything, but as it turns out, an external SIP client (Aastra phone in this case) was not among the working. No matter what I tried, I couldn't get traffic to come to the Asterisk PBX behind the router on UDP 5060 from the internet, neither from that phone, nor other devices on its network, nor from other phones and other networks. Apparently the router simply couldn't properly handle packets coming to UDP 5060.

As it turned out, this router is known for giving trouble with SIP; the older version of the firmware had SIP ALG built in, but it couldn't even be disabled! Apparently the new version has the option to toggle SIP ALG off, but it still doesn't work for whatever reason.

This post isn't about that particular router model, though. Sadly, this is far from the only equipment out there that plays havoc with what one should expect to be a run-of-the-mill remote phone implementation. Fortunately, there's a relatively easy workaround you can use, and I think it's fairly elegant, if a bit hackish.

First, I should cover what I tried and failed at, because it would be easy for anyone without an in-depth understanding of the SIP protocol to make the same mistake.

SIP is notorious for NAT traversal problems, and there are a variety of solutions out there that can be had, from SIP proxies to STUN servers to SIP ALG. I'm not covering those here, but I'm touching on them because the problems are due to a peculiarity of the SIP protocol -- the contents, not the metadata, of SIP messages describe how a connection should be set up. So if something about the connection, be it port or IP address, changes when traversing NAT, then the message itself has to contain information about those changes. The tools I mentioned above take one approach or another to modify how the messages traverse NAT, or they simply rewrite the message contents to align with the differences in the network. I wanted to avoid implementing any new software solutions in this particular situation, though.

I initially tried to set the phone up to use a different UDP port, then forward that port at the router to 5060 on the Asterisk box. This SORT OF worked... the phone was able to register and calls would even go through ... but only for 20 seconds until the connection timed out. This happened because the Asterisk machine was still sending out packets telling the phone to use port 5060 (instead of my substitute port), and when the phone responded on port 5060, the packets couldn't make it to the PBX; it was the same problem I started with. Using 5060 just wasn't an option, and I realized I would need to use some other port altogether.

Ultimately, I set the Asterisk instance to use a randomly-selected, high-numbered UDP port using the bindport directive in sip.conf (In FreePBX, you'll find this setting in the "Asterisk SIP Settings" section under the Tools tab):
(Of course, Asterisk has to be reloaded or restarted after a change like this). Then, on the router, I forwarded incoming traffic on that UDP port directly to the Asterisk server on the same port.

Of course, the already-installed phones on the local network (not traversing the brain-dead router) were still configured to use 5060, and that couldn't work anymore, because Asterisk was no longer listening on 5060. I considered changing the configuration on all those phones, but I realized it was going to be a huge pain to do for 20 or more devices. Since that's the default setting for SIP phones, it's also much easier not to have to change it on any new phones either!

So I didn't. Instead, I set up an IPtables rule on the Asterisk machine to forward incoming traffic on UDP 5060 to the new port, like this:
iptables -A PREROUTING -t nat -i eth0 -p udp --dport 5060 -j REDIRECT --to-port 47320
You'll need to adjust the interface definition (-i eth0) and port number (--to-port 47320) according to your configuration.

This command can be added to an existing IPtables configuration to run automatically on boot (/etc/init.d/iptables, e.g.), or it can be set up as a standalone one-liner by adding it to the /etc/rc.local, /etc/inittab, or some other init script.

So if you have a router giving you fits over SIP traversal, the "correct" solution is probably to replace the router with something that doesn't break standard protocols. But the "right" solution for you might be to take the simple steps I've outlined here.

Let me know if you have any questions!