Expiretable is a utility used to remove entries from a pf(4) table based on their age.

The age in question being the amount of time that has passed since the statistics for each entry in the target table was last cleared.

This program needs to be run as a user with read/write permission to /dev/pf. As with all unknown things that need to be run as the superuser or similar there is always the chance this one will cause death and destruction, so use at your own risk. It WorksForMe<tm>, don't blame me if your cat grows another eye, and so on and so forth.

If you find yourself using this utility, especially for a reason not mentioned here, please tell me about it.

The man-page:

Johan Fredin has been nice enough to provide a port for expiretable. He has also put together a how-to on how to use expiretable to block attempts to bruteforce ssh. Be sure to check out the work of Peter N. M. Hansteen on firewalling with PF, especially the section on bruteforcing. 'geek00L' has written a short piece on using Snort2c + Expiretable. Thanks to Samuel Ljungkvist and others for ideas.

The sources:




Tableutil is a utility for converting, aggregating and performing operations (currently unions, differences, complements and intersections) on lists of IP-addresses. Its primary use is to convert files into a format pfctl(8) can read, but if you find another use for it I'd really like to know about it. It can read plain-text-files with ranges (, CIDR-style networks ( single addresses ( or hostnames (one.two.com). It can also read p2b-files, the preferred file-format(s) of PeerGuardian. Tableutil has two modes of operation: Quick mode, which is used for converting files to pfctl-compatible tables and advanced mode, which is used to perform more advanced operations on files, and for greater flexibility of the output format.

For example, if you have three files, 'block1', 'block2' and 'exceptions' that is to be used in a table that blocks the hosts in 'block1' and 'block2', excepting the ranges in 'exceptions', create a file looking something like this:

# cat blockspec
$block1 = load(text, "block1"); # The block1-blocklist
$block2 = load(text, "block2"); # The block2-blocklist
$exceptions = load(text, "exceptions"); # List of exceptions

$block = difference(union($block1, $block2), $exceptions);

save(cidr, "blocklist", $block);

Or, if you just want to load a peerguardian blocklist (the text-kind), do something like this:

# cat update-blocklist.sh
#! /bin/sh

rm -f /tmp/blocklist

ftp -V -o - ${URL} 2> /dev/null | gunzip -c - | sed "s/.*:\([0-9.-]\)/\1/" | \
        tableutil -q text 2> /dev/null > /tmp/blocklist

if [ -s /tmp/blocklist ] ; then
        mv /etc/pfdata/blocklist /etc/pfdata/blocklist.old &&
        cp /tmp/blocklist /etc/pfdata/blocklist &&
        pfctl -f /etc/pf.conf -T load

The man-page:

In the next release, there will be a BNF:ish grammar that describes the config-file in the man-page. Until then I'll just put it here:

statements          ::= [ ( <table_statement> | <save_op> ) ";"
                        [ <statements> ] ]
table_statement     ::= <variable> | <table_literal> | <load_op> |
                        <assignment_op> | <union_op> | <difference_op> |
                        <intersection_op> | <complement_op>
variable            ::= VARIABLE_NAME
table_literal       ::= "{" [ <table_literal_entry> ] "}"
table_literal_entry ::= ( CIDR | RANGE | SINGLE_IP | HOSTNAME ) [ ,
                        <table_literal_entry> ]
load_op             ::= "load" "(" ( "text" | "p2b" ) ","
                        ( "stdin" , FILENAME ) ")"
save_op             ::= "save" "(" ( "cidr" | "range" | "single" ) ","
                        ( "stdout" | FILENAME ) ")"
assignment_op       ::= <variable> "=" <table_statement>
union_op            ::= "union" "(" <table_statement> "," <table_statement> ")"
difference_op       ::= "difference" "(" <table_statement> ","
                        <table_statement> ")"
intersection_op     ::= "intersection" "(" <table_statement> ","
                        <table_statement> ")"
complement_op       ::= "invert" "(" <table_statement> ")"


The sources: