The problem was that I needed to make sure that all UDP traffic for my SIP VoIP phones was handled with a higher priority than all the normal low priority internet traffic such as web requests. Altq is a very powerful that can do this and a lot of other things. It was invented as part of the KAME project, and it's implementation is different across the BSD world. In OpenBSD for example, it's integrated with the packet filter (pf). On NetBSD it works by enabling it in the kernel and then running a user-space daemon to implement your shaping rules.
You'll need to make sure your kernel supports ALTQd. It almost certainly will not do so by default. The GENERIC kernel or any of it's variants like GENERIC.MP will not support altq out of the box. You'll need to create a new kernel with all the ALTQ features enabled. Recompiling your kernel is not really hard, but it's outside the scope of what I'm trying to explain here. You can find detailed descriptions of how to do it on this wiki or in the NetBSD guide. The way to enable ALTQ in your new kernel is to uncomment all the ALTQ lines you'll find a block there. Once you have installed the new kernel. Reboot your computer.
You'll need to create an empty /etc/altqd.conf file for starters, then edit your /etc/rc.conf and add a line at the bottom that says "altqd=yes". Reboot or run "/etc/rc.d/altqd start"
You now want to setup your altqd classes and filters. The class tells altqd what kind of traffic shaping you want to do. There are about five different types and they all have their uses for various situations. In my case I knew that SIP traffic was all UDP, and since people are mostly too stupid these days to use FTP or other non-web services that also use UDP (after all "the web == internet" right?), I didn't bother with any kind of complex filters. I would notice that SIP audio quality would fall anytime people were really cranking on web pages and downloading stuff. So here is what I used in my /etc/altqd.conf
# We have a 5mbit link to our ISP over a fiber-ethernet connection # interface bge0 bandwidth 5M priq # Create a class for important traffic class priq bge0 high_class NULL priority 2 # We raise the priority of all UDP traffic. The only significant # type we will see is VoIP and that should be fine. filter bge0 high_class 0 0 0 0 17 # All other traffic is lower priority than VoIP class priq bge0 low_class NULL priority 0 default
Okay, let's face it. The configuration is somewhat less than straightforward. So let's break down what we did.
interface bge0 bandwidth 5M priq
This line says that we intend on using a the first broadcom ethernet interaface as the outgoing WAN interface to shape the traffic on. In order for for ALTQ algorithms to work, most of them need to know how much bandwidth that line has. Even though that interface is capable of 1000mbit (it's a gigabit copper port) it's connected to a fiber optic media converter which is connected to my ISP. The ISP only gives me the capability to transmit at 5mbit. So, I don't want ALTQ to assume we are dealing with a gigabit link here. Otherwise, I suspect our shaping rules wouldn't work at all. Also, you'll notice that I keep talking about this being the WAN interface. Shaping traffic on the LAN interface of this firewall (it does NAT using IP filter) might work as well. However, I prefer to shape it at the last possible moment before it leaves my control. The authors of the altq.conf(4) man page seem to agree with this approach.
class priq bge0 high_class NULL priority 2
This line says that we want to create a new parent-class for ALTQ. The parent class tells ALTQ what kind of shaping we'll be doing. In this case it's "priq" which is shorthand for priority queuing. This algorithm makes sure that network buffers with my chosen type of traffic will be separated out by using filters, and the ones with higher priorities will be emptied first. Moving on we see that the class applies to the interface "bge0" which is good since we just defined that above. Next is a simple user-defined label for this class. I chose the string "high_class" so it would be clear. You can call it anything you want. I could have chose "voip_class" or "udp_traffic". It doesn't matter. You'll notice the next item is "NULL". That's because this class is a parent class and thus it has no parent class of it's own. The man-page was helpful enough to tell me what to use there. The last two items "priority 2" are pretty clear. I'm simply telling this class what priority it is. The higher the value, the more priority it has. In most cases I believe you can assign an integer value of 0-15 with 15 being the highest priority.
Now, we need a way to add certain types of traffic into this class. We'll use the altq filter syntax to do this. That's where the next line comes in
filter bge0 high_class 0 0 0 0 17
The first three items should be clear enough. We are writing a filter for interface bge0 and we want traffic that matches the filter to fall under the pervue of the "high_class" class. The integers that follow are the part were I wish the author would have been just a tad more IP-Filterish or PF-like. Here is the template for those from the altq.conf(4) man page:
dst_addr [netmask mask] dport src_addr [netmask mask] sport proto [tos value [tosmask value]] [gpi value
The value "0" means "any". All the stuff in square braces is optional and not used in my example. So I'm saying "from any host on any port to any host on any port using protocol UDP". What about that spurious 17 at the end you say? It's the protocol number for UDP. You can find all such numbers in your /etc/protocols file. Now let's move on to the last line:
class priq bge0 low_class NULL priority 0 default
With the 'priq' algorithm there must be a default class. This is because you aren't going to want to have a filter for every conceivable type of traffic. We assign this class a lower priority than the class we just made for UDP. Also note that you don't have to write a filter for the default class as that would kind of defeat the whole purpose of having a default class.
You can use the tool "altqstat" to see traffic flowing through your classes in realtime. I'd highly recommend doing so and testing to make sure things are getting filtered where you want them. Just kick off "altqstat" from the command line and let it run. The second time it spits out some data, it'll start giving you kb/s figures which are very useful. Here is an example from mine:
bge0: [high_class] handle:0x3 pri:2 measured: 32.46kbps qlen: 0 period:4955623 packets:4971367 (563368265 bytes) drops:0 [low_class] handle:0x1 pri:0 measured: 439.47kbps qlen: 0 period:8783713 packets:9110912 (3917080357 bytes) drops:1338
You can see that my phones are taking 32.45kbps and everything else is consuming 439.47kbps. Had I not setup altq with 'priq', my UDP phone traffic would be drowning in a sea of http request. Instead, my calls are getting their packets out the door precious fractions faster and that's making a big difference in my call quality. Yeah!