For every state pf creates up to two source nodes: a limiting one struct pf_kstate -> src_node and a NAT one struct pf_kstate -> nat_src_node. The limiting source node is tracking information needed for limits using max-src-states and max-src-nodes and the NAT source node is tracking NAT rules only.
On closer inspection three issues emerge:
- For route-to rules the redirection decision is stored in the limiting source node. Thus sticky-address and source limiting can't be used separately.
- Global source tracking, as promised in the man page, is totally absent from the code. Pfctl is capable of setting flags PFRULE_SRCTRACK (enable source tracking) and PFRULE_RULESRCTRACK (make source tracking per rule). The kernel code checks PFRULE_SRCTRACK but ignores PFRULE_RULESRCTRACK. That would make all source tracking always global but the code is written in a way that makes source tracking work per-rule only.
- Once OpenBSD syntax is imported, we might need even more SNs per rule. It should be possible to have limits, nat and route-to decisions in a single rule.
This patch is based on OpenBSD approach where source nodes have a type and each state has a list of source nodes instead of just two pointers.
The patch also fixes multiple regarding source nodes:
- Source nodes are not created anymore with zeroed address to be filled in later. The lock is held throughout the whole process of SN search, creation and LB decision.
- Existence of SNs created during ruleset evaluation is checked when creating a state, in case the SNs have been killed.
- Killing specified SNs has been merged with killing all SNs, which also solves locking issues in pf_clear_srcnodes().
- A minimal struct pf_test_ctx has been created, to be used more in the future, like in OpenBSD.
- The approach to make global tracking work is to attach global source nodes to the default rule. This functionality seems broken in OpenBSD too. I've checked their code history, there was a global flag to pf_insert_src_node() at some point but it was removed.
- Off-by-one errors for limits.