QoS And Traffic Shaping For VoIP Users Using iproute2 And Asterisk
The quality of my VoIP phone calls suffered whenever I was downloading or uploading anything. This was irritating, especially for those calling me (I heard them better than they heard me). So I poked at Iproute2 and other howtos, especially with regard to VoIP traffic, but I couldn't find anything that worked well. After some playing around, I've found settings that were right for me: consistant VoIP quality, regardless of any activities on the wire.
A note regarding my VoIP system: I'm using Asterisk on a spare laptop (IBM T20), and my VoIP provider is the excellent Unlimitel, in Canada. Since I host my own Asterisk box, I can pretty much configure it to do whatever I want. I also bought an IAX hardphone which resides on my intranet.
I assume you already have in place your Linux firewall. VoIP packets transverse your firewall and your NAT is already set and working. We won't even touch your firewall with this setup. All you need to do is to create the right qdisc, classes and filters to prioritize your VoIP packets. Iproute2's tc command is here for that.
Let's pretend your network is as such:
Internet <------> Cable modem <-------> Linux Firewall <-----> Intranet <-----> VoIP phone
Since I'm using Asterisk with IAX, the port of interest is port 4569. If you're using SIP, adapt the port in the following lines.
Some Background Information
IP packets have an area where you can preset QoS (quality of Service). While your ISP will probably ignore them, you can make use of them in your local network for some traffic shaping. The 4 QoS bits are:
0x02: Minimize Monetary Cost
0x04: Maximize Reliability
0x08: Maximize Throughput
0x10: Minimize Delay
Officially, you are allowed to turn on at most one of those bits. Your firewall can then look at the packets as they come and determine which ones to forward first, usually Minimize Delay.
Unofficially, you can turn on any of those bits at once. So you could have a packet with both Minimize Delay and Maximize Throughput. With this in mind, let's look at some of the features of Iproute2.
Iproute2 And tc
By default, packets are sent First In-First Out. With tc, you can alter that dramatically. Let's use a very simple queuing system: let's have 3 "pipes" (or queues) and assign them a priority. That is, as long as there is something in queue 1, we don't empty (or dequeue) queue 2, and as long as there is something in queue 2, we don't dequeue queue 3.
This is done with the following command (note: eth1 is the external interface):
tc qdisc add dev eth1 root handle 1: prio priomap 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 0
The 16 values for priomap are important. It basically says that most packets end up in the 3rd queue (since we start at zero, it's called "2"), except the packets with QoS Minimize-Delay, which are put in the 2nd queue, except the packets with every QoS bits on, which are put in the 1st queue. (the prio queuing discipline sets 3 queues by default)
The 16 values are in order for the following QoS:
1: 0x00: no QoS is set -> to 3rd queue (2)
2: 0x02: Mimimize Monetary Cost (MMC) (2)
3: 0x04: Maximize Reliability (MR) (2)
4: 0x06: MMC + MR (2)
5: 0x08: Maximize Throughput (MT) (2)
6: 0x0a: MT + MMC (2)
7: 0x0c: MT + MR (2)
8: 0x0e: MT + MR + MMC (2)
9: 0x10: Minimize Delay (MD) (1)
10: 0x12: MD + MMC (1)
11: 0x14: MD + MR (1)
12: 0x16: MD + MMC + MR (1)
13: 0x18: MD + MT (1)
14: 0x1a: MD + MT + MMC (1)
15: 0x1c: MD + MT + MR (1)
16: 0x1e: MD + MT + MR + MMC (0)
That was our first step.
Next, we will adjust each queue so they themselves have some queuing discipline:
tc qdisc add dev eth1 parent 1:1 handle 10: sfq limit 3000
tc qdisc add dev eth1 parent 1:2 handle 20: sfq
tc qdisc add dev eth1 parent 1:3 handle 30: sfq
This gives the first queue a supposed capacity of 3000 packets. In reality, the size will be 128 packets as it is hard coded in the tc program as being the maximum size possible.
At this point, most of the packets will go through the 3rd queue, except those with Minimize-Delay QoS bit set.
If you adjust your iax.conf on your Asterisk box to say the following, some packets may end up in the first queue:
However, since this isn't a guarantee, we will force them to queue 1 using another trick in tc's sleeve, filters.
Remember, IAX uses port 4569, so we will force any packet to and from this port to go through the first queue:
tc filter add dev eth1 protocol ip parent 1: prio 1 u32 match ip dport 4569 0xffff flowid 1:1
tc filter add dev eth1 protocol ip parent 1: prio 1 u32 match ip sport 4569 0xffff flowid 1:1
tc filter add dev eth1 protocol ip parent 1: prio 1 u32 match ip tos 0x10 0xff flowid 1:2
Technically, the last line isn't required but it doesn't hurt to leave it in.
And now, you're done! All your VoIP packets are dequeued first and foremost. Even during a heavy download or upload, you won't lose interactivity for your telephone calls.
To see some statistics, run the following command:
tc -s qdisc ls dev eth1
To remove your queues and return to the normal state, run:
tc qdisc del dev eth1 root
It's now up to you to automatically run the appropriate commands at boot.