Vonage & PF: Prioritizing VoIP TrafficEric M. Johnston
23 October 2005, Initial revision.
31 July 2007, Updated Vonage FAQ link.
For the impatient: sample
OverviewMy home network consists of an ADSL connection to the Internet and a FreeBSD firewall and router. A default FreeBSD install actually includes multiple firewall packages; I've chosen to use OpenBSD's Packet Filter (PF) firewall. It's relatively new software, but I find its codebase clean, the ruleset easy to understand, it integrates nicely with Alternate Queuing (ALTQ), it's BSD licensed, and those OpenBSD guys have a pretty good track record for writing secure software.
I used to use a little Netgear RP114 router. It's a fine product, but my networking needs outgrew its capabilities. One feature I wanted was the ability to prioritize my voice over IP traffic over other data transfer activity. I use Vonage's VoIP service, which ordinarily provides nice, clear connections over my relatively slow (768 kbps down/384 kbps up) connection. However, I found it all too easy to saturate the upload pipe and cause choppy, unintelligible phone calls.
PF with ALTQ is the perfect combination of tools for solving this problem with its Quality of Service (QoS) features. While there is plenty of well-written documentation, specific even to FreeBSD, I had a hard time finding complete information regarding VoIP traffic prioritization. A number of examples I ran across discussed prioritizing traffic based on the phone adapter's IP address, but I'm interested in a more general solution than a hard-coded address. This article attempts to fill that gap by detailing how I finally got things to work as expected.
The Vonage ServiceVonage actually provides a "bandwidth saver" feature for adjusting sound quality and thus bandwidth consumed. This option may work for some people, especially those with very poor connectivity; however, it didn't make any difference for me and I wasn't really willing to compromise sound quality to accommodate the few times that network traffic interfered with my calls.
Vonage's bandwidth saver control.
Additionally, Vonage works with some analog telephone adapters with built-in routers that include QoS capabilities. The Linksys RT31P2 is one of these. However, I wasn't much interested in an "all-in-one" device: I already had a router/firewall and wanted a stand-alone phone adapter. The RT31P2 is a great device (though I don't really like the Linksys router/firewall feature set) and is perfect for folks not interested in the contortions I detail here to get QoS (like my dad). Note, however, that multiple all-in-one devices sometimes won't mix: if you've got a router with a built-in wireless access point or print server and want to add the RT31P2 or similar device to the mix, you might end up having to sacrifice either QoS features or wireless/printer connectivity to your wired devices. Hence my preference for stand-alone devices...
The Linksys RT31P2 router & phone adapter.
So, I ended up with the Linksys PAP2. It's a fairly small device, making it nicely portable for use on the road. The power supply, while external, is compact, light, and supports up to 240 volts. (I should note that a friend of mine in Thailand uses one with Vonage -- 7-digit dialing to Bangkok!) My only complaint thus far is that it's got four gratuitously bright blue LEDs. I don't believe Vonage offers the PAP2 when you sign up with them directly. However, they're usually available essentially free after rebates at BestBuy, Office Depot, etc.
The Linksys PAP2 phone adapter.
PF under FreeBSDAt the time of this writing, my firewall runs FreeBSD 5.4, which includes the version of PF released with OpenBSD 3.5. While this article should largely apply to other versions of FreeBSD and PF, as well as PF under other operating systems, keep in mind that some details may be different for your specific configuration.
To get started with PF and ALTQ, the FreeBSD Handbook has some good information. Specifically, the section titled "The OpenBSD Packet Filter (PF) and ALTQ." Additionally, OpenBSD's PF FAQ is invaluable to anyone getting started with PF. It includes a number of helpful examples and tips.
To use ALTQ, you're going to have to recompile your kernel. The handbook section from above covers this pretty well, but following are the additions I made to mine:
man page explains the options a little. I thought about trying to figure out
exactly what each one does and if I needed them all for my application, but
that thought didn't last very long and I just commented-out the "meaningless"
one and the SMP one. Note that you don't have to add the
With console access available, configure your
Configuring PFIn a nutshell, my configuration prioritizes outbound UDP traffic on Vonage's VoIP-specific ports. With this approach, the firewall configuration has no dependency on my phone adapter's IP address, nor the number of phone adapters on my internal network. Note, however, that I do not attempt to restrict prioritization to traffic headed to Vonage servers: with only anecdotal information on the possible destinations for my Vonage traffic, I didn't think this would be a reliable optimization. Therefore, any traffic on the prioritized ports -- whether by happenstance or avarice -- will trump "default" traffic.
There are a couple of additional "features" in my PF configuration that don't directly apply to the VoIP project: interoperability with IPSec (Cisco's VPN Client) and PPTP VPNs, prioritized TCP acknowledgment packets, and port forwarding/redirection for externally available services. I explain all of these below, but feel free to ignore them if all you're interested in is voice traffic.
Getting started, pretty much all the configuration you have to do is contained
To load your configuration, you don't have to stop and restart PF or even interrupt open network connections; just use the command:
First, a little background about my configuration:
The internal 10.99.1.0/24 network is mapped to the static Internet IP using Network Address Translation (NAT). Additionally, I've got some external ports redirected to internal IPs so I can provide services from machines other than the firewall.
So, let's get to the meat of the issue. Following is a line-by-line
explanation of the sample
These macros define my internal and external interfaces and a couple of hosts
on the internal network with static IP addresses. Next is:
This part is irrelevant to our VoIP project, but I'm including it for the sake of completeness. I use Cisco's VPN Client to connect to three different networks using IPSec over UDP. The line above simply defines a list of (again, obfuscated) IP addresses to my VPN servers. The firewall treats these sorts of connections a little differently than normal network traffic, as you'll see below.
Here I've got a list of UDP ports my Vonage phone adapter wants to use for the Session Initiation Protocol (SIP) and the Real-time Transport Protocol (RTP). Vonage details which ports its service uses in an FAQ article. Note that other VoIP providers might use different ports.
These are some PF runtime
Here we're normalizing packets as they come in. The PF documentation has specifics on each option; this set works well for me and doesn't seem to interfere with VoIP traffic.
ALTQ lets us do packet queuing and prioritization. It's important to note that the router can only prioritize outgoing traffic; we can't really do much with incoming traffic. On an asymmetric connection (e.g., my ADSL service) where the download speed is typically a couple times faster than the upload, this shouldn't be too much of an issue. But, be aware that this setup won't prevent a saturated download pipe from affecting VoIP call quality.
I've chosen to use the Priority Queue (
For my network, I've configured three queues:
The second NAT rule is the more important one: it maps your internal network to
your Internet connection's address. Note that if your external IP is
dynamic (assigned via DHCP), you might want to put parenthesis around the
These macros define lists of ports through which I offer services on the external interface. As noted in the comments, though the firewall machine is offering some of these services itself, I've chosen to redirect traffic to its internal address, primarily for ease of maintenance.
Here, my traffic redirection rules forward the ports I defined in the macros above. (None of these are applicable to the Vonage VoIP service.) We'll use those macros later in some filter rules. Note that I'm redirecting two different ports for the Remote Desktop Protocol (RDP). TCP port 3389 is the standard port for this protocol, but since I've got two Windows computers I'd like to reach from the Internet, I changed the listen port on one of them. Now, I could have simply redirected traffic to TCP port 8080 to the default RDP port on the 2nd Windows machine, but I wanted access to the machine to be consistent both internally and externally.
At this point, we're actually starting to firewall something. By default,
I'm blocking everything. It's only by exception (the
This snippet is where I use the macros defined above for the redirected TCP and
UDP ports. All these rules do is allow traffic in from the external interface
destined for the specified ports. Note, however, the
I'm not a big fan of firewalls that totally block ICMP traffic, or "pings".
Now that inbound traffic on the external interface has been taken care of, let's turn our attention to the internal interface. The first rule is pretty straightforward: I let all traffic from my internal network into the firewall. I trust all the users on my network, and since the firewall also acts as a file server, among other things, there's little value in trying to lock things down further. If you've got a dedicated firewall or a more open internal network (e.g., a publicly accessible wireless access point), you'll probably want to be more restrictive with internal interface traffic.
The second rule is a little more interesting: here I use my
The third rule simply lets traffic out on the internal interface. This can include both traffic from the firewall machine itself (remember, it's also a file server) and traffic from the outside world that's already been vetted by my earlier rules involving the external interface.
And, finally, these rules take care of outbound traffic on the external
interface. Recall that ALTQ only queues outgoing traffic. Consequently, these
rules are where I assign packets to the appropriate queues for prioritization.
The first rule lets TCP packets out: normal packets are assigned to the
Next, I let out regular UDP, ICMP, and GRE traffic. Again, letting ICMP
traffic flow allows the likes of
Last, but not least, the third rule looks for my specially-tagged
To cap things off, I had to add two more rules for IPSec VPN packets. The Encapsulating Security Payload (ESP) protocol is used for both tunneling and encryption. Also, I need to explicitly open up the ISAKMP port on the external interface for key exchange, despite already having the NAT rule above.
ConclusionMy approach to ensuring quality voice over IP phone calls using PF and ALTQ by tagging packets is only one of many ways it can be done. I chose it because it results in a fairly simple, easy to understand ruleset. Simply trying to intercept packets as they leave the external interface destined for specific ports is not as straightforward as it sounds: address translation mucks with things, causing the obvious rules to fail. By tagging the packets before NAT gets involved, it's very clear exactly which are being bumped-up in priority by the time they reach the external interface.
Nevertheless, whether you use my approach or experiment with your own, you'll want to test things to verify that your configuration works. Fortunately, PF lets you look at statistics for each ALTQ queue defined:
If you find any glaring errors, opportunities for optimization, or just want
to comment, please let me know!
|« Return to Home|
|© 2006 Ętherwide, LLC. All rights reserved.|