Besides the main ruleset,
pfctl(8) can load rulesets into
anchor attachment points. An
anchor is a container that can hold rules, address tables, and other anchors.
An
anchor has a name which specifies the path where
pfctl(8) can be used to access the anchor to perform operations on it, such as attaching child anchors to it or loading rules into it. Anchors may be nested, with components separated by ‘/' characters, similar to how file system hierarchies are laid out. The main ruleset is actually the default anchor, so filter and translation rules, for example, may also be contained in any anchor.
An anchor can reference another
anchor attachment point using the following kinds of rules:
nat-anchor <name>
Evaluates the nat rules in the specified anchor.
rdr-anchor <name>
Evaluates the rdr rules in the specified anchor.
binat-anchor <name>
Evaluates the binat rules in the specified anchor.
anchor <name>
Evaluates the filter rules in the specified anchor.
load anchor <name> from <file>
Loads the rules from the specified file into the anchor name.
When evaluation of the main ruleset reaches an
anchor rule,
pf(4) will proceed to evaluate all rules specified in that anchor.
Matching filter and translation rules marked with the
quick option are final and abort the evaluation of the rules in other anchors and the main ruleset. If the
anchor itself is marked with the
quick option, ruleset evaluation will terminate when the anchor is exited if the packet is matched by any rule within the anchor.
anchor rules are evaluated relative to the anchor in which they are contained. For example, all
anchor rules specified in the main ruleset will reference anchor attachment points underneath the main ruleset, and
anchor rules specified in a file loaded from a
load anchor rule will be attached under that anchor point.
Rules may be contained in
anchor attachment points which do not contain any rules when the main ruleset is loaded, and later such anchors can be manipulated through
pfctl(8) without reloading the main ruleset or other anchors. For example,
ext_if = "kue0"
block on $ext_if all
anchor spam
pass out on $ext_if all
pass in on $ext_if proto tcp from any \
to $ext_if port smtp
blocks all packets on the external interface by default, then evaluates all rules in the
anchor named "spam", and finally passes all outgoing connections and incoming connections to port 25.
# echo "block in quick from 1.2.3.4 to any" | \
pfctl -a spam -f -
This loads a single rule into the
anchor, which blocks all packets from a specific address.
The anchor can also be populated by adding a
load anchor rule after the
anchor rule:
anchor spam
load anchor spam from "/etc/pf-spam.conf"
When
pfctl(8) loads
pf.conf, it will also load all the rules from the file
/etc/pf-spam.conf into the anchor.
Optionally,
anchor rules can specify the parameter's direction, interface, address family, protocol and source/destination address/port using the same syntax as filter rules. When parameters are used, the
anchor rule is only evaluated for matching packets. This allows conditional evaluation of anchors, like:
block on $ext_if all
anchor spam proto tcp from any to any port smtp
pass out on $ext_if all
pass in on $ext_if proto tcp from any to $ext_if port smtp
The rules inside
anchor spam are only evaluated for
tcp packets with destination port 25. Hence,
# echo "block in quick from 1.2.3.4 to any" | \
pfctl -a spam -f -
will only block connections from 1.2.3.4 to port 25.
Anchors may end with the asterisk (‘*') character, which signifies that all anchors attached at that point should be evaluated in the alphabetical ordering of their anchor name. For example,
will evaluate each rule in each anchor attached to the
spam anchor. Note that it will only evaluate anchors that are directly attached to the
spam anchor, and will not descend to evaluate anchors recursively.
Since anchors are evaluated relative to the anchor in which they are contained, there is a mechanism for accessing the parent and ancestor anchors of a given anchor. Similar to file system path name resolution, if the sequence “..” appears as an anchor path component, the parent anchor of the current anchor in the path evaluation at that point will become the new current anchor. As an example, consider the following:
# echo ' anchor "spam/allowed" ' | pfctl -f -
# echo -e ' anchor "../banned" \n pass' | \
pfctl -a spam/allowed -f -
Evaluation of the main ruleset will lead into the
spam/allowed anchor, which will evaluate the rules in the
spam/banned anchor, if any, before finally evaluating the
pass rule.
Filter rule
anchors can also be loaded inline in the ruleset within a brace ('{' '}') delimited block. Brace delimited blocks may contain rules or other brace-delimited blocks. When anchors are loaded this way the anchor name becomes optional.
anchor "external" on egress {
block
anchor out {
pass proto tcp from any to port { 25, 80, 443 }
}
pass in proto tcp to any port 22
}
Since the parser specification for anchor names is a string, any reference to an anchor name containing solidus (‘/') characters will require double quote (‘"') characters around the anchor name.