BSD vs. Linux Firewalling
If you've used pf on OpenBSD and then had to write iptables rules on Linux, the culture shock is real. The tools reflect the different philosophies of the systems they come from — BSD tends toward integrated, opinionated design; Linux tends toward modularity and flexibility, which sometimes means more pieces to understand. Here's how they compare in practice.
The Tools at a Glance
| OpenBSD pf | FreeBSD ipfw | Linux nftables | |
|---|---|---|---|
| Integration | Tightly built into OpenBSD; also on FreeBSD | Native to FreeBSD | Linux kernel; works across all distros |
| Syntax | Clean, human-readable pf.conf | Command-line oriented, less intuitive | Modern and consistent; steeper initial curve |
| Stateful | With keep state — explicit and clear | With keep-state flag | Via conntrack; ct state matching |
| NAT | Built-in, clean nat syntax | Built-in via ipnat or divert | Via nat table hooks |
| Traffic shaping | ALTQ (built-in) | dummynet (built-in) | tc (separate tool entirely) |
| Default policy | set block-policy drop | Rule 65535 catch-all | Chain policy setting |
Syntax Comparison: The Same Rule Three Ways
Allow SSH in, block everything else:
OpenBSD pf
block all pass in on egress proto tcp to port 22 keep state
This reads almost like a sentence. block all is the default deny. pass in punches the specific hole. egress is a shorthand for the default route interface — you don't even have to name it.
FreeBSD ipfw
ipfw add 100 allow tcp from any to any 22 in keep-state ipfw add 65535 deny all from any to any
ipfw processes rules by number, lowest first. The 65535 deny-all is the catch-all at the end. Rule ordering is your responsibility to manage manually — easy to make gaps or conflicts as rulesets grow.
Linux nftables
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
tcp dport 22 ct state new accept
ct state established,related accept
}
}
nftables groups rules into tables and chains — more verbose upfront, but scales better for complex rulesets. Tables for different purposes (IPv4, IPv6, NAT) can coexist cleanly without interfering.
Stateful Filtering
# pf — keep state inline on the pass rule pass in proto tcp to port 80 keep state # ipfw — keep-state flag at the end ipfw add allow tcp from any to any 80 keep-state # nftables — conntrack matching is a separate step tcp dport 80 ct state new accept # allow new connections to port 80 ct state established,related accept # allow return traffic (applies to all)
The nftables approach of separating "allow established" from "allow new" is actually clearer once you're used to it. pf's inline keep state is more compact. Both correctly handle return traffic automatically.
NAT Comparison
# pf — masquerade the internal network behind the external interface
nat on egress from 192.168.1.0/24 to any -> (egress)
# nftables — hook-based approach
table ip nat {
chain postrouting {
type nat hook postrouting priority 100;
ip saddr 192.168.1.0/24 oifname "eth0" masquerade
}
}
pf's NAT syntax is simpler for standard use. nftables' hook-based approach is more explicit about where in the packet processing pipeline the rule applies — which matters for complex setups, not for masquerading a home network.
Rule First-Match vs. Last-Match
This is the biggest gotcha when switching between systems:
| System | Evaluation model | Override behaviour |
|---|---|---|
| pf | Last matching rule wins | Use quick to exit on first match |
| ipfw | First matching rule wins (by rule number) | Lower-numbered rules take precedence |
| nftables | First matching rule in chain wins | Use continue to keep evaluating |
| iptables | First matching rule wins | Use jump to other chains for exceptions |
Which to Use
This is mostly decided by the OS:
- OpenBSD: Use pf — it's deeply integrated and the rest of the system (authpf, relayd, ospfd) assumes it.
- FreeBSD: You have a choice. pf is available and well-supported; most people prefer it over ipfw for new setups.
- Linux: Use nftables, or ufw/firewalld on top of it if you want a simpler interface. iptables still works but new development is in nftables.
The philosophical difference is real but rarely matters in practice for a single-server setup. BSD firewalls feel more opinionated and integrated; Linux firewalls feel more like infrastructure you assemble. Both get the job done.
dispelled