Netfilter is merely a series of hooks in various points in a protocol stack (at this stage, IPv4). The (idealized) IPv4 traversal diagram looks like the following:
A Packet Traversing the Netfilter System:
--->[1]--->[ROUTE]--->[3]--->[4]--->
| ^
| |
| [ROUTE]
v |
[2] [5]
| ^
| |
v |
On the left is where packets come in: having passed the simple sanity checks (ie. not truncated, IP checksum OK, not a promiscuous receive), and defragmentation (if CONFIG_IP_ALWAYS_DEFRAG is set) they are passed to the netfilter framework's NF_IP_PRE_ROUTING hook.
Next they enter the routing code, which decides whether the packet is destined for another interface, or a local process. The routing code may drop packets which are unroutable.
If it's destined for a local process, the netfilter framework is called again for the NF_IP_LOCAL_IN hook, before being passed to the process.
If it's destined to pass to another interface, the netfilter framework is called for the NF_IP_FORWARD hook.
The packet then passes a final netfilter hook, the NF_IP_POST_ROUTING hook, before being put on the wire again.
The final hook is called for packets which are created by local processes. Here you can see that routing occurs after this hook is called: in fact, the routing code is called first (to figure out the source IP address and some IP options), and called again if the packet is altered.
Now we have an example of netfilter for IPv4, you can see when each hook is activated. This is the essence of netfilter.
Kernel modules can register to listen at any of these hooks. Then when that netfilter hook is called from the core networking code, each module registered at that point is free to manipulate the packet. The module can then tell netfilter to do one of three things:
The other parts of netfilter (handling queued packets, and registering for dropped packets) will be covered in the kernel section later.
Upon this foundation, we can build fairly complex packet manipulations, as shown in the next two sections.
A packet filtering system called IP Tables has been built over the netfilter framework. It is a direct descendent of ipchains (which came from ipfwadm, which came from BSD's ipfw IIRC), except that all the references to redirect and masquerade have been removed.
One of the advantages of iptables is that it is small and fast, and it hooks into netfilter at the NF_IP_LOCAL_IN, NF_IP_FORWARD and NF_IP_LOCAL_OUT points. This means that for any given packet, there is one (and only one) possible place to filter it. This makes things much simpler. Also, the fact that the netfilter framework provides both the input and output interfaces for the NF_IP_FORWARD hook means that many kinds of filtering are far simpler.
I am expecting to port the kernel portions of both ipchains and ipfwadm as modules on top of netfilter, enabling the use of the old ipfwadm and ipchains userspace tools without requiring an upgrade (except for transparent proxying, which may never be supported for ipfwadm and ipchains).
The netfilter framework provides four significant places to do Network Address Translation. For non-local packets, the NF_IP_PRE_ROUTING and NF_IP_POST_ROUTING hooks are perfect for destination and source alterations respectively. For local packets, the NF_IP_LOCAL_OUT and NF_IP_LOCAL_IN hooks fulfill the same role.
Not surprisingly, I have implemented a NAT framework on top of netfilter as well, which uses these hooks. It provides both full dynamic NAT and the (conceptually simpler) masquerade facility.
These are now all done using the NAT framework, rather than being independent entities. This is useful because they are all closely related.
The new flexibility provides both the opportunity to do really funky things, but for people to write enhancements or complete replacements which can be mixed and matched.