Directing traffic with ALTQ

Table of Contents
ALTQ - prioritizing by traffic type
ALTQ - allocation by percentage
ALTQ - handling unwanted traffic

ALTQ - short for ALTernate Queueing - is a very flexible mechanism for directing network traffic which lived a life of its own before getting integrated into PF. Altq was another one of those things which were integrated into PF because of the additional convenience it offered when integrated.

Altq uses the term queue about the main traffic control mechanisms. Queues are defined with a defined amount of bandwidth or a specific part of available bandwidth, where a queue can be assigned subqueues of various types.

To complete the picture, you write filtering rules which assign packets to specified queues or a selection of subqueues where packets pass according to specified criteria.

Queues are created with one of several queue disciplines. The default queue discipline without ALTQ is FIFO (first, in first out).

A slightly more interesting discipline is the class based discipline (CBQ), which in practical terms means you define the queue's bandwidth as a set amount of data per second, as a percentage or in units of kilobits, megabits and so on, with an additional priority as an option, or priority based (priq), where you assign priority only.

Priorities can be set at 0 to 7 for cbq queues, 0 to 15 for priq queues, with a higher value assigning a higher priority and preferential treatment. In addtion, the hierarcical queue algoritm "Hierarchical Fair Service Curve" or HFSC is available.

Briefly, a simplified syntax is

altq on interface type [options ... ] main_queue { sub_q1, sub_q2 ..}
  queue sub_q1 [ options ... ]
  queue sub_q2 [ options ... ]
[...]
pass [ ... ] queue sub_q1
pass [ ... ] queue sub_q2

If you will be using these features in you own rule sets, you should under any circumstances read the pf.conf man page and the PF user guide. These documents offer a very detailed and reasonably well laid out explanation of the syntax and options.[1] [2]

ALTQ - prioritizing by traffic type

Our first real example is lifted from Daniel Hartmeier's web. Like quite a few of us, Daniel is on an asymmetric connection, and naturally he wanted to get better bandwidth utilization.

One symptom in particular seemed to indicate that there was room for improvement. Incoming traffic (downloads) apparently slowed down outgoing traffic.

Analyzing the data indicated that the ACK packets for each data packet transferred caused a disproportionately large slowdown, possibly due to the FIFO (First In, First Out) queue discipline in effect on the outgoing traffic.

A testable hypothesis formed - if the tiny, practically dataless ACK packets were able to slip inbetween the larger data packets, this would lead to a more efficient use of available bandwidth. The means were two queues with different priorities. The relevant parts of the rule set follows:

ext_if="kue0"

altq on $ext_if priq bandwidth 100Kb queue { q_pri, q_def }
queue q_pri priority 7
queue q_def priority 1 priq(default)

pass out on $ext_if proto tcp from $ext_if to any flags S/SA \
        keep state queue (q_def, q_pri)

pass in  on $ext_if proto tcp from any to $ext_if flags S/SA \
        keep state queue (q_def, q_pri)

The result was indeed better performance.

So why does this work?

So why does this work?[3] The reason lies in how the ALTQ code treats subqueues with different priorities. Once a connection is assigned to the main queue, ALTQ inspects each packet's type of service (ToS) field. ACK packets have the ToS Delay bit set to 'low', which indicates that the sender wanted the speediest delivery possible.

When ALTQ sees a low delay packet and queues of differing priorities are available, it will assign the packet to the higher priority queue. This means that the ACK packets skip ahead of the lower priority queue and are delivered more quickly, which in turn means that data packets are serviced more quickly, too.

Daniel's article is available from his web site at http://www.benzedrine.cx/ackpri.html

Notes

[1]

On FreeBSD, ALTQ requires the ALTQ and queue discipline options for the disciplines you want to use to be compiled into the running kernel. Refer to the PF chapter of the FreeBSD Handbook for further information.

[2]

At the time of writing, ALTQ isn in the process of getting integrated in the NetBSD 4.0 PF implementation. For earlier NetBSD versions, Peter Postma maintains a patch to enable PF/ALTQ functions. Up to date information on this, including how to get the ALTQ patch via pkgsrc, is available from Peter Postmas PF on NetBSD pages, http://nedbsd.nl/~ppostma/pf/ and The NetBSD PF documentation.

[3]

Earlier versions of this tutorial left the explanation pretty much as an exercise to the reader.