5. Packet Queueing and Prioritization

Packet Filter allows you to manage bandwidth by creating queues and assigning packets to them; queues must be attached to a specific network interface and are organized in a tree hierarchy, i.e. each queue can have further child queues.

5.1 PFQueue objects

Packet queueing is managed through the pf.PFQueue class:

class pf.PFQueue(queue[, **kw])
The queue argument is a string containing the name of the queue; the **kw parameter allows you to specify the value of any attribute by passing it as a keyword.

PFQueue instances have the following attributes:

PFQueue.qname
String containing the name of the queue.
PFQueue.parent
String containing the name of the parent queue, or an empty string if it is a root queue.
PFQueue.ifname
String containing the name of the interface on which this queue is activated.
PFQueue.flags
A bitmask containing additional options for the queue; the only valid flag is pf.HFSC_DEFAULTCLASS (treat queue as default queue).
PFQueue.qlimit
The maximum number of packets held in the queue; the default is pf.DEFAULT_QLIMIT.
PFQueue.qid
The numeric ID of the queue.
PFQueue.parent_qid
The numeric ID of the parent queue.
PFQueue.linkshare
A ServiceCurve object specifying the target bandwitdh for the queue.
PFQueue.realtime
A ServiceCurve object specifying the minimum (reserved) bandwidth for the queue.
PFQueue.upperlimit
A ServiceCurve object specifying the maximum bandwidth that should be assigned to the queue
PFQueue.flowqueue
A FlowQueue object specifying the parameters for the flow queue manager.

5.2 ServiceCurve objects

ServiceCurve objects allow you to assign the target, minimum and maximum bandwidth to a queue:

class pf.ServiceCurve(bandwidth[, burst[, time]])
The bandwidth argument is the bandwidth specification in bytes; when a burst of traffic comes through, the queue can temporarily assign a higher bandiwdth (burst) for time milliseconds.

ServiceCurve instances have the following attributes:

ServiceCurve.bandwidth
An integer specifying the bandwidth in bytes.
ServiceCurve.burst
An integer specifying the higher bandwidth (in bytes) that can be assigned to the queue in case of a burst of traffic.
ServiceCurve.time
An integer specifying the time, in milliseconds, for which the burst bandwidth must be assigned.

5.3 FlowQueue objects

FlowQueue objects allow you to tweak how multiple connections, assigned to the same queue, share the queue bandwidth.

class pf.FlowQueue(flows[, quantum[, target[, interval]]])
The flows argument is an integer containing the number of flows, quantum specifies the quantum of service for the flow queue manager, target is the queueing delay target in milliseconds, interval is the tracking interval in milliseconds.

FlowQueue instances have the following attributes:

FlowQueue.flows
An integer specifying the number of flows.
FlowQueue.quantum
An integer specifying the quantum of service for the flow queue manager. The lower the quantum size the more advantage is given to streams of smaller packets at the expense of bulk transfers.
FlowQueue.target
Queueing delay target in milliseconds.
FlowQueue.interval
Tracking interval in milliseconds.

PFQueue instances have the following attributes:

PFQueue.qname
String containing the name of the queue.

5.4 Packet queueing in action

When adding queues to Packet Filter, please keep in mind that queues are arranged in a hierarchical manner (i.e. in a inverted-tree structure); therefore, you need to create an additional (root) queue that will be parent for the other queues: pfctl(8) manages this automatically by creating a parent queue named "root_ifname", so you may want to stick to the same naming convention.

For child queues, the parent attribute (i.e. the name of their parent queue) must be explicitly set, for PF to correctly build the queue tree; also note that the hierarchy can be further expanded by defining queues within queues.

# Enable queueing on the external interface to control incoming traffic.
ifname = "em0"
root = "root_" + ifname
MB = 10**6

# Let's create the queues as a list:
#   queue std on em0 bandwidth 100M
#   queue ssh parent std bandwidth 10M burst 90M for 100ms
#   queue mail parent std bandwidth 10M, min 5M, max 25M
#   queue http parent std bandwidth 80M default
queues = [pf.PFQueue(qname=root, ifname=ifname,
                     linkshare=pf.ServiceCurve(bandwidth=100*MB),
                     flags==pf.PFQS_ROOTCLASS),                 # Root queue
          pf.PFQueue(qname="std", parent=root, ifname=ifname,
                     linkshare=pf.ServiceCurve(bandwidth=100*MB)),
          pf.PFQueue(qname="ssh", parent="std", ifname=ifname,
                     linkshare=pf.ServiceCurve(10*MB, 90*MB, 100)),
          pf.PFQueue(qname="mail", parent="std", ifname=ifname,
                     linkshare=pf.ServiceCurve(bandwidth=10*MB),
                     realtime=pf.ServiceCurve(bandwidth=5*MB),
                     upperlimit=pf.ServiceCurve(bandwidth=25*MB)),
          pf.PFQueue(qname="http", parent="std", ifname=ifname,
                     linkshare=pf.ServiceCurve(bandwidth=80*MB),
                     flags=pf.PFQS_DEFAULT)]

# Load the queues on the system
filter = pf.PacketFilter()
filter.load_queues(*queues)