2. The PacketFilter class

The pf module defines the PacketFilter class which represents the kernel's packet filtering subsystem and provides a set of methods that allow you to send commands to the kernel through the ioctl(2) interface provided by the pf(4) pseudo-device.

class pf.PacketFilter([dev])
The optional dev argument allows you to specify a different path for the pf(4) pseudo-device.

Basically, all methods in this class are just wrappers to ioctl(2) calls and therefore may raise an IOError exception if the ioctl(2) call fails. In this case, you can get information about what caused the exception by examining the value of the errno variable and the error string provided by the exception. For example:

#!/usr/bin/env python

import sys
import errno
import pf

filter = pf.PacketFilter()

try:
    # Enable packet filtering
    filter.enable()
except IOError, (err, msg):
    if err == errno.EACCES:
        sys.exit("Permission denied: are you root?")
    elif err == errno.ENOTTY:
        sys.exit("ioctl not supported by the device: is the pf device correct?")
    # And so on...

In the further examples, error handling code, as well as the initial pf import, will often be omitted for clarity and conciseness. PacketFilter objects have the following attributes and methods:

PacketFilter.dev
The path of the pf(4) pseudo-device (defaults to /dev/pf).
PacketFilter.enable()
Enable Packet Filtering, NAT and queueing.
PacketFilter.disable()
Disable Packet Filtering, NAT and queueing.
PacketFilter.set_debug(level)
Set the debug level. The debug level can be either a string ("emerg", "alert", "crit", "err", "warn", "notice", "info", "debug") or a constant (LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG); for example:
filter = pf.PacketFilter()
filter.set_debug("debug")
# Or alternatively:
filter.set_debug(pf.LOG_DEBUG)
PacketFilter.set_hostid(id)
Set the host ID, used by pfsync(4) to identify the host that created a state table entry. id must be a 32-bit unsigned integer.
PacketFilter.set_reassembly(reassembly)
Enable reassembly of network traffic. The reassembly argument specifies the flags for the reassembly operation; available flags are PF_REASS_ENABLED (to enable packets reassembly) and PF_REASS_NODF (to enable the reassembly of fragments with the dont-fragment bit set). Mulitple flags must be ORed together; for example:
# Enable the reassembly of fragmented packets
filter.set_reassembly(pf.PF_REASS_ENABLED|pf.PF_REASS_NODF)
PacketFilter.get_limit([limit])
Return the hard limits on the memory pools used by Packet Filter. limit can be either one of the PF_LIMIT_* constants or a string; return the value of the requested limit (pf.UINT_MAX means unlimited) or, if called with no arguments, a dictionary containing all the available limits.
PacketFilter.set_limit(limit, value)
Set hard limits on the memory pools used by Packet Filter. limit can be either one of the PF_LIMIT_* constants or a string; a value of pf.UINT_MAX means unlimited. Raise PFError if the current pool size exceeds the requested hard limit; for example:
filter = pf.PacketFilter()

# Decrease the maximum number of entries in the state table
try:
    filter.set_limit("states", 5000)
except pf.PFError:
    print "set_limit() failed"

# Don't limit the number of addresses that can be stored in tables
filter.set_limit("table-entries", pf.UINT_MAX)
PacketFilter.get_timeout([timeout])
Return the configured timeout values for states. timeout can be either one of the PFTM_* constants or a string; return the value of the requested timeout or, if called with no arguments, a dictionary containing all the available timeouts.
PacketFilter.set_timeout(timeout, value)
Set the timeout value for a specific state. timeout can be either one of the PFTM_* constants or a string; return the old value of the specified timeout; for example:
filter = pf.PacketFilter()

# Decrease the interval between purges of expired states and packet fragments
new_intv = 5
old_intv = filter.set_timeout("interval", new_intv)
print "Interval changed from {} to {} seconds".format(old_intv, new_intv)
PacketFilter.set_optimization([opt])
Optimize state timeouts based on the network environment (similarly to the set optimization directive in pf.conf(5)); valid options are "aggressive", "conservative", "high-latency", "normal" (default) and "satellite".
PacketFilter.get_optimization()
Return the name of the current state timeouts optimization profile.
PacketFilter.get_ifaces([ifname])
Get the list of interfaces and interface drivers known to pf(4). Return a tuple of PFIface objects, or a single PFIface instance if a specific ifname is specified.
PacketFilter.set_ifflags(ifname, flags)
Set the user settable flags on the interface ifname; the only valid flag is PFI_IFLAG_SKIP, which tells PF to skip filtering on the interface.
PacketFilter.clear_ifflags(ifname[, flags])
Clear the specified user settable flags on the interface ifname; if no flags are specified, clear all flags. The only valid flag is PFI_IFLAG_SKIP, which, if set, tells PF to skip filtering on the interface.
PacketFilter.set_status_if([ifname])
Specify the interface for which statistics are accumulated. If no ifname is provided, turn off the collection of per-interface statistics. Raise PFError if ifname is not a valid interface name; for example:
ifname = "rl0"
filter = pf.PacketFilter()

try:
    filter.set_status_if(ifname)
except pf.PFError:
    print "Not a valid interface: '{}'".format(ifname)
PacketFilter.get_status()
Return a PFStatus object containing the internal PF statistics.
PacketFilter.clear_status([ifname])
Clear the internal packet filter statistics. An optional ifname can be specified in order to clear statistics only for a specific interface.
PacketFilter.set_syncookies(mode)
Set the syncookies mode. Valid modes are "never", "always" and "adaptive", or the corresponding constants: PF_SYNCOOKIES_NEVER, PF_SYNCOOKIES_ALWAYS, PF_SYNCOOKIES_ADAPTIVE.
PacketFilter.set_synflood_watermarks([start[, end]])
In "adaptive" mode, Packet Filter will enable syncookies when a given percentage of the state table is used up by half-open TCP connections. This method allows you to set the percentage at which to start and end syncookie mode; percentageis on a 1-to-1000 scale:
# set syncookies adaptive (start 25%, end 12%)
filter.set_syncookies("adaptive")
filter.set_synflood_watermarks(2500, 1200)
PacketFilter.get_synflood_watermarks()
In "adaptive" mode, retrieve the current percentages for entering and leaving syncookie mode.
PacketFilter.get_states()
Retrieve Packet Filter's state table entries. Return a tuple of PFState objects representing the states currently tracked by PF.
PacketFilter.clear_states([ifname])
Clear all states. If an interface name is provided, only states for that interface will be cleared. Return the number of cleared states; for example:
ifname = "rl0"
filter = pf.PacketFilter()
num_states = filter.clear_states(ifname)
print "Cleared {} states on {}".format(num_states, ifname)
PacketFilter.kill_states([af[, proto[, src[, dst[, ifname[, label[, rdomain]]]]]]])
Clear states matching the specified arguments. States can be specified by address family (af), layer-4 protocol (proto), source (src) and destination (dst) addresses, interface name (ifname), label and routing domain (rdomain); the source and destination addresses must be PFRuleAddr objects. Return the number of killed states; for example:
import socket

filter = pf.PacketFilter()

# Kill all IPv6 states on a specific interface
filter.kill_states(af=socket.AF_INET6, ifname="rl0")

# Kill all states between two specific hosts
www_client = pf.PFAddr("10.0.0.4")
www_server = pf.PFAddr("1.2.3.4")
www_port   = pf.PFPort(80, socket.IPPROTO_TCP, pf.PF_OP_EQ)

filter.kill_states(src=pf.PFRuleAddr(www_client),
                   dst=pf.PFRuleAddr(www_server, www_port))
PacketFilter.clear_rules([path])
Clear all rules contained in the anchor path; if no anchor is specified, clear all rules from the main ruleset.
PacketFilter.get_queues()
Return a tuple of PFQueue objects representing the currently loaded ALTQ disciplines and queues.
PacketFilter.load_queues(*queues)
Load a set of queues on an interface; queues must be PFQueue objects. Note: this method doesn't allow you to load just one queue rule on the fly: you need to (re)load the complete set of queue rules for the interface.
PacketFilter.get_ruleset([path[, clear[, **kw>]]])
Return a PFRuleset object containing the active ruleset; path is the name of the anchor to retrieve rules from and defaults to the main ruleset. If set to True, clear will cause the per-rule statistics to be cleared. Additional keyword arguments can be passed in order to retrieve only rules with specified attribute values; for example:
filter = pf.PacketFilter()

# Retrieve the main ruleset
filter.get_ruleset()

# Retrieve the "ftp-proxy" ruleset
filter.get_ruleset("ftp-proxy")

# Retrieve all rules on interface "em0"
filter.get_ruleset(ifname="em0")

# Retrieve all rules assigned to queue "dns_out"
filter.get_ruleset(qname="dns_out")
PacketFilter.load_ruleset(ruleset[, path[, *tr_type]])
Load the given ruleset. ruleset must be a PFRuleset object; path is the name of the anchor where to load rules. tr_type specifies the transaction type(s); valid values are PF_TRANS_TABLE for loading address tables and PF_TRANS_RULESET for loading rules; if omitted, both tables and rules will be loaded.
PacketFilter.add_tables(*tables)
Create one or more tables. tables must be PFTable objects. Return the number of tables effectively created; for example:
goodguys = pf.PFTable("goodguys", "192.168.0.0/24", "1.2.3.4")
filter = pf.PacketFilter()
filter.add_tables(goodguys)
PacketFilter.clear_tables([filter])
Clear all tables in a specified anchor (/ by default); filter is a PFTable object that allows you to specify the anchor in which to delete all tables. Return the number of tables deleted; for example:
filter = pf.PacketFilter()
# Clear the "goodguys" table
filter.clear_tables(pf.PFTable(name="goodguys"))
# Clear all tables contained in the /relayd anchor
filter.clear_tables(pf.PFTable(anchor="/relayd"))
PacketFilter.del_tables(*tables)
Delete one or more tables; tables must be PFTable objects. Return the number of tables deleted.
PacketFilter.get_tables([filter])
Get all the tables in the specified anchor (/ by default); filter is a PFTable object that allows you to specify the anchor from which to retrieve the tables. Return a tuple of PFTable objects containing the currently-loaded tables.
PacketFilter.add_addrs(table, *addrs)
Add one or more addresses to a table; table can be either a PFTable instance or a string containing the table name; addrs can be either PFTableAddr instances or strings. Return the number of addresses effectively added; for example:
filter = pf.PacketFilter()
filter.add_addrs("mytable", "192.168.30.18", "192.168.30.19")
PacketFilter.clear_addrs(table)
Clear all addresses in the specified table; table can be either a PFTable instance or a string containing the table name. Return the number of addresses removed.
PacketFilter.del_addrs(table, *addrs)
Delete one or more addresses from the specified table. table can be either a PFTable instance or a string containing the table name; addrs can be either PFTableAddr instances or strings. Return the number of addresses deleted.
PacketFilter.set_addrs(table, *addrs)
Replace the content of a table. table can be either a PFTable instance or a string containing the table name; addrs can be either PFTableAddr instances or strings. Return a tuple containing the number of addresses deleted, added and changed.
PacketFilter.get_addrs(table)
Get all the addresses of the specified table; table can be either a PFTable instance or a string containing the table name. Return a tuple of PFTableAddr objects.
PacketFilter.get_tstats([filter])
Get statistics information for tables in a specified anchor (/ by default); filter is a PFTable object that allows you to specify the anchor containing the table(s) to retrieve statistics for. Return a tuple of PFTStats objects; example:
filter = pf.PacketFilter()
tstats = filter.get_tstats(pf.PFTable("goodguys"))
PacketFilter.clear_tstats(*tables)
Clear the statistics of one or more tables; tables must be PFTable objects specifying the tables for which statistics should be cleared. Return the number of tables effectively cleared.