OpenBSD PF - Tables [Contents]


A table is used to hold a group of IPv4 and/or IPv6 addresses. Lookups against a table are very fast and consume less memory and processor time than lists. For this reason, a table is ideal for holding a large group of addresses as the lookup time on a table holding 50,000 addresses is only slightly more than for one holding 50 addresses. Tables can be used in the following ways:

Tables are created either in pf.conf(5) or by using pfctl(8).


Tables are created using the table directive in pf.conf, The following attributes may be specified for each table: Example:
table <goodguys> { }
table <rfc1918>  const {,, }
table <spammers> persist
block in on fxp0 from { <rfc1918>, <spammers> } to any
pass  in on fxp0 from <goodguys> to any
Addresses can also be specified using the negation (or "not") modifier, such as:
table <goodguys> {, ! }
The goodguys table will now match all addresses in the network except for

Note that table names are always enclosed in < > angled brackets.

Tables can also be populated from text files containing a list of IP addresses and networks:

table <spammers> persist file "/etc/spammers"
block in on fxp0 from <spammers> to any
The file /etc/spammers would contain a list of IP addresses and/or CIDR network blocks, one per line.

Manipulating with pfctl

Tables can be manipulated on the fly by using pfctl(8). For instance, to add entries to the <spammers> table created above:
# pfctl -t spammers -T add
This will also create the <spammers> table if it doesn't already exist. To list the addresses in a table, run:
# pfctl -t spammers -T show
The -v argument can also be used with -T show to display statistics for each table entry. To remove addresses from a table, run:
# pfctl -t spammers -T delete
For more information on manipulating tables with pfctl, see the pfctl(8) man page.

Specifying Addresses

In addition to being specified by IP address, hosts may also be specified by their hostname. When the hostname is resolved to an IP address, all resulting IPv4 and IPv6 addresses are placed into the table. IP addresses can also be entered into a table by specifying a valid interface name, interface group, or the self keyword. The table will then contain all IP addresses assigned to that interface or group, or to the machine (including loopback addresses), respectively.

One limitation when specifying addresses is that and 0/0 will not work in tables. The alternative is to hard code that address or use a macro.

Address Matching

An address lookup against a table will return the most narrowly matching entry. This allows for the creation of tables such as:
table <goodguys> {, !, }

block in on dc0
pass  in on dc0 from <goodguys>
Any packet coming in through dc0 will have its source address matched against the table <goodguys>: