ipfw hooks into pfil when the module is loaded
pf hooks into pfil when pf gets enabled via pfctl -e, not when the module is loaded
both are hooking into ip_output and ip_input. the pfil hooks are maintained in tailq lists and the order used with insertion is dependent on the direction where the hook is added.
both ipfw and pf have hooks for both input and output.
the input hooks are inserted in the head of tailq. the output hooks are inserted at the tail of tailq. this to maintain the order of hooks-calling in sync with the packet flow.
let's talk about hooking in ip_input:
when ipfw module gets kldload-ed, it hooks into pfil's tailq head with its input hook and into pfil's tailq tail with its output hook
when pf gets loaded, it doesn't hook yet
when pf gets enabled via pfctl -e, it hooks into pfil's tailq head with its input hook and into pfil's tailq tail with its output hook through an ioctl call.
at this moment, the order is:
ip_input: pfil -> ipfw
ip_output: ipfw -> pfil
if you kldunload the ipfw module and kldload it back the order will change:
ip_input: ipfw -> pfil
ip_output: pfil -> ipfw
by default, on a freshly installed freebsd system, the firewall startup sequence is the one mentioned just above:
ipfw gets enabled second so its input hook will be called first on input and last on output chain.
godel# rcorder /etc/rc.d/* | grep -nE '/i?pfw?$'
freebsd default packet filtering order:
- ip_input: ipfw -> pfil ->
- ip_output: pfil -> ipfw ->
|FreeBSD default filtering sequence|
pf gets enabled first, with its input hook inserted into head and its output hook inserted into tail
if you run this sequence of commands:
things will change backwards.