This episode covers DragonFlyBSD network defense: building a ruleset in /etc/pf.conf, using tables and NAT, managing with pfctl, then understanding ipfw syntax and comparing pf vs ipfw to choose the right one for your needs.

In episode 11 you built a backup strategy to protect your data. But neatly stored data is meaningless if someone else can get into the system. This episode is about the network defense gate: the firewall. DragonFlyBSD provides two firewall engines — pf and ipfw — and you'll learn when to use which.
Imagine a firewall as a security guard at the building door. They have a list of rules (a ruleset): who may enter, who must be detained, and where visitors are directed (NAT). pf is the guard with a structured, readable rulebook; ipfw is the guard with a very flexible checklist form. Both are capable, but their working styles differ.
pf (Packet Filter) comes from OpenBSD and is DragonFlyBSD's default choice. Its configuration lives in /etc/pf.conf with a tidy structure: macros, tables, options, then the ruleset. Here's a realistic minimal example:
ext_if = "em0"
lan_if = "em1"
table <private> { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
set skip on lo0
block all
pass on $ext_if proto tcp to port { 22, 80, 443 }
pass on $lan_if from <private>Reading it: block everything; allow SSH, HTTP, HTTPS from the outside; allow everything from the internal network.
pf is stateful — it tracks already-authorized connections and automatically allows their reply packets. Tables allow dynamic address lists:
pfctl -t private -T add 10.0.0.0/8
pfctl -t private -T showTables are very useful for constantly changing lists of blocked or allowed IPs — they can be filled by scripts and reloaded without rewriting the ruleset.
Network Address Translation lets many hosts share a single public address. In pf, NAT is set up with the nat keyword:
nat on $ext_if from $lan_if to any -> ($ext_if)This line translates all LAN traffic to the external interface's address. Port redirection for internal services:
rdr on $ext_if proto tcp to port 8080 -> 192.168.1.10 port 80pfctl is pf's control tool:
pfctl -e
pfctl -f /etc/pf.conf
pfctl -s rules
pfctl -s state-e enables pf, -f loads the ruleset, -s rules shows the active rules, -s state shows the connection table. An important habit: always test the syntax before loading a production ruleset:
pfctl -nf /etc/pf.confipfw is the classic BSD firewall engine with a rule-number-based syntax. Rules are evaluated in order by number, so ordering is critical:
ipfw add 100 allow tcp from any to any 22
ipfw add 110 allow tcp from any to any 80
ipfw add 120 allow tcp from any to any 443
ipfw add 500 deny ip from any to anyEvery rule has a number; a packet is tested from the smallest number until one matches. count, allow, deny, and skipto are the most commonly used actions.
ipfw also supports tables for address lists:
ipfw table 1 add 192.168.1.0/24
ipfw add 200 allow ip from table(1) to anyTables keep the ruleset short even when the address list is long.
The firewall is enabled through rc.conf by choosing the engine and loading the rules:
firewall_enable="YES"
firewall_type="open"A custom ruleset can be stored in /etc/rc.firewall or /etc/ipfw.rules.
Danger
The two most expensive lessons in firewalling: (1) test syntax before loading — a single mistake in a production ruleset can lock you out of your own server; (2) always ensure a console or out-of-band access path exists before enabling the firewall. pfctl -nf and ipfw dry-runs are lifesavers.
| Aspect | pf | ipfw |
|---|---|---|
| Origin | OpenBSD | Classic BSD |
| Syntax | Declarative ruleset in a file | Ordered numbered rules |
| Stateful | Built-in | Can be enabled |
| Tables | Yes | Yes |
| NAT | Elegant (nat/rdr) | Needs more configuration |
| Fit | Default, modern | Legacy, flexible per rule |
The rule of thumb: start with pf — it's the default, well documented, and its NAT is clean. Use ipfw when you need explicit numbered per-rule control, or when migrating older ipfw-based configuration.
In this episode 12 you understood DragonFlyBSD's two firewall engines: building a pf ruleset in /etc/pf.conf with macros, tables, stateful filtering, and NAT, managing it with pfctl, then getting to know numbered ipfw syntax and its tables, plus comparing when to choose pf versus ipfw.
Key takeaways:
/etc/pf.conf, control with pfctl -f, test with pfctl -nf.block all, followed by specific pass rules — the deny-by-default principle.nat on $ext_if from $lan_if to any -> ($ext_if).In the next episode, episode 13, we secure the main entrance: SSH & remote access hardening. You'll configure sshd_config with ed25519 keys, disable root and password logins, generate keys with ssh-keygen, tunneling, and harden with UseDNS no, MaxAuthTries, AllowUsers, and fail2ban.