PFSENSE - PFCTL Basic Usage
Packet Filter architecture
pfctl is the userland control utility for PF (Packet Filter), the stateful firewall subsystem originating from OpenBSD and integrated into pfSense (FreeBSD-based). pfSense dynamically generates PF rules from its configuration and loads them into the kernel via pfctl.
PF operates using:
- Stateful inspection
- Rule evaluation from top to bottom
- First matching rule (with quick exceptions)
- Separate rule sets for filtering, NAT, redirection, and normalization
- Anchors for modular rule management (heavily used by pfSense)
Rule evaluation flow
- Packet enters interface
- Normalization rules (scrub) applied
- NAT / RDR processed
- Filtering rules evaluated
- State table checked/updated
- Packet passed or blocked
Anchors and pfSense integration
pfSense uses anchors extensively to isolate automatically generated rules:
- pfSense
- pfSense/*
- relayd
Anchors allow dynamic insertion and removal of rules without reloading the entire ruleset.
Example:
pfctl -a pfSense -sr
State table management
PF is a stateful firewall; every allowed connection is tracked.
Display states:
pfctl -s state
Kill a specific state:
pfctl -k 192.0.2.10 -k 198.51.100.20
Clear all states:
pfctl -Fs
Tables and dynamic address management
PF tables provide high-performance lookups for large address lists.
List tables:
pfctl -s Tables
Show table content:
pfctl -t bogons -T show
Add an IP to a table:
pfctl -t blocked_hosts -T add 203.0.113.45
Remove an IP:
pfctl -t blocked_hosts -T delete 203.0.113.45
NAT and redirection inspection
Display NAT rules:
pfctl -s nat
Display redirection rules:
pfctl -s rdr
Show all translation rules:
pfctl -s rules | grep nat
Rule counters and performance metrics
PF tracks packets and bytes per rule.
Show rules with counters:
pfctl -vvsr
Reset counters:
pfctl -z
This is critical for traffic analysis and policy validation.
Normalization and packet scrubbing
Scrub rules normalize packets to prevent evasion techniques:
- MSS clamping
- Fragment reassembly
- Invalid flag dropping
Display scrub rules:
pfctl -s all | grep scrub
Security concepts
Stateful filtering
Only packets belonging to a valid state are allowed to pass, reducing attack surface.
Default deny policy
pfSense enforces an implicit block at the end of rule sets.
Verify block rules:
pfctl -sr | grep block
Antispoofing
PF can prevent IP spoofing on interfaces.
Example:
antispoof quick for em0
SYN flood protection
PF supports SYN proxies and connection rate limiting.
Example rule:
pass in proto tcp from any to any flags S/SA keep state (max-src-conn 100, max-src-conn-rate 50/10)
Table-based threat mitigation
Dynamic tables allow automatic blocking via IDS/IPS or scripts.
Example integration:
- Snort / Suricata populating PF tables
- Fail2ban-style blocking
Logging and diagnostics
PF logs packets to pflog interfaces.
Enable logging on a rule:
pass in log proto tcp from any to any port 22
View logs:
tcpdump -n -e -ttt -i pflog0
Troubleshooting
Rules not matching
- Verify rule order
- Check for quick rules
- Inspect active rules instead of GUI configuration
pfctl -sr
NAT not working
- Confirm NAT rules are loaded
- Ensure outbound NAT mode is correct
- Check rule auto-generation
pfctl -s nat
Traffic blocked unexpectedly
- Inspect states
- Check floating rules
- Analyze logs in pflog
pfctl -s state
Performance degradation
- Check state table size
- Inspect table sizes
- Reset counters and monitor
pfctl -si
Rules disappear after reload
pfSense regenerates rules automatically. Manual pfctl changes are ephemeral and overwritten on reload or reboot.
