9.10 Quick Reference and Toolchain
Chapter Core Concepts Review
- Netfilter: Kernel network packet processing framework providing a Hook mechanism.
- Netfilter Hooks: 5 key points (PRE_ROUTING, LOCAL_IN, FORWARD, LOCAL_OUT, POST_ROUTING).
- Connection Tracking (conntrack): Tracks connection states; the foundation of NAT and stateful firewalls.
- NAT: Network Address Translation, including SNAT and DNAT.
- IPTables / nftables: Userspace rule management tools. ==================
Reaching the end of this chapter, your head is probably stuffed with macros, structure pointers, and callback functions. That's completely normal—Netfilter isn't a simple "plug-and-play" mechanism; it's a patch panel tangled with network cables.
In this final section, we won't introduce any new concepts. Instead, we'll lay out all the "tools" we've used in this chapter and inspect them. This comes in two parts: the core methods we call in kernel code, and the real-world (userspace) tools we use to debug it.
Kernel Core Methods Cheat Sheet
These are the symbols you'll actually type on your keyboard during kernel module development. Each one corresponds to a specific action we broke down earlier.
1. Hook Registration and Unregistration
This is the mandatory path to hooking your code into the network stack.
-
int nf_register_hook(struct nf_hook_ops *reg);- Purpose: Register a single
nf_hook_opsobject. This is the most basic usage—fill out the structure, call this, and your callback function takes effect. - Note: If it returns non-zero, registration failed (e.g., priority conflict or out of memory), and your module should handle this error.
- Purpose: Register a single
-
int nf_register_hooks(struct nf_hook_ops *reg, unsigned int n);- Purpose: Batch registration. The second parameter,
n, is the array length. - Scenario: For modules like
nf_conntrackthat need to plant "spies" across multiple protocol families and hook points simultaneously, this method pushes all hooks in the array in one go, saving you from looping through individual calls.
- Purpose: Batch registration. The second parameter,
-
void nf_unregister_hook(struct nf_hook_ops *reg);/void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n);- Purpose: Unregister hooks.
- Warning: Don't assume this step is unimportant. If your module unloads without unregistering its hooks, the kernel still holds function pointers to that freed memory—the result is a "beautiful" kernel panic.
2. IPTables Table Registration
This code belongs to the iptables kernel framework implementation. If you want to write a custom iptables module or extension, you'll use these.
-
struct xt_table *ipt_register_table(struct net *net, const struct xt_table *table, const struct ipt_replace *repl);- Purpose: Register a table in the kernel (e.g., the
filtertable ornattable). This typically happens during module initialization. - Parameter:
replcontains the initial rules and replacement information.
- Purpose: Register a table in the kernel (e.g., the
-
void ipt_unregister_table(struct net *net, struct xt_table *table);- Purpose: Unregister the table. This frees all kernel resources associated with that table.
3. Connection Tracking Lifecycle
This is Netfilter's core memory system.
-
static inline void nf_conntrack_get(struct nf_conntrack *nfct);- Purpose: Increment the reference count of a connection tracking object.
- Mechanism: Just like C++'s
shared_ptror Rust'sArc. Whenever you store this connection's pointer somewhere else (like in an SKB), you must call this function to prevent the object from being prematurely freed.
-
static inline void nf_conntrack_put(struct nf_conntrack *nfct);- Purpose: Decrement the reference count. When the count reaches zero,
nf_conntrack_destroy()is called, and the connection's file is permanently destroyed.
- Purpose: Decrement the reference count. When the count reaches zero,
-
static inline struct nf_conn *resolve_normal_ct(...)- Purpose: The entry point for connection tracking logic.
nf_conntrack_in()calls it to determine the packet's fate. - Logic: It first calls
__nf_conntrack_find_get()to search the hash table; if found, it binds the existingnf_connto the SKB; if not found, it callsinit_conntrack()to create a new one. Remember the "unconfirmed list" we talked about? Newly created entries start out right there.
- Purpose: The entry point for connection tracking logic.
-
struct nf_conntrack_tuple_hash *init_conntrack(...)- Purpose: Allocate and initialize a new
nf_connobject. - Details: Inside, it calls
nf_ct_find_expectation()to check if this is FTP traffic with "expected connections." If so, it directly associates the new connection with the main connection and setsIPS_EXPECTED_BIT.
- Purpose: Allocate and initialize a new
-
static struct nf_conn *__nf_conntrack_alloc(...)- Purpose: The underlying memory allocator. It grabs a chunk of memory from the Slab Allocator for
nf_connand conveniently attaches a timeout timer—when this timer fires, it callsdeath_by_timeout()to execute the connection.
- Purpose: The underlying memory allocator. It grabs a chunk of memory from the Slab Allocator for
4. Extension Registration
Whether it's a Target (action), a Match (condition), or a Conntrack extension, they all follow this set of interfaces.
-
Target (Action)
int xt_register_target(struct xt_target *target);void xt_unregister_target(struct xt_target *target);- Purpose: Register actions like
NF_DROP,NF_ACCEPT, orLOG. Use this when writing a custom "drop and alert" module.
-
Match (Condition)
int xt_register_match(struct xt_match *target);- **void xt_unregister_match(struct xt_match *target);`
- Purpose: Register matching conditions, such as "match by string content" or "match by time."
-
Conntrack Extensions (Connection Extensions)
int nf_ct_extend_register(struct nf_ct_ext_type *type);void nf_ct_extend_unregister(struct nf_ct_ext_type *type);- Purpose: We saw those extensions in the previous section (timestamp, acct, ecache). They use this interface to tell the kernel: "Hey, if you need to store custom data in connection tracking, please expand capacity according to this structure."
Core Macro Definitions
Besides functions, there are a few unavoidable macros in the code.
NF_CT_DIRECTION(hash)
- Definition:
include/net/netfilter/nf_conntrack_tuple.h - Input: A
nf_conntrack_tuple_hashobject (usually pulled from the hash table). - Output: A direction enum value.
IP_CT_DIR_ORIGINAL(0): Original direction (the direction of the request).IP_CT_DIR_REPLY(1): Reply direction.
- Why we need it: Because the
tuplehasharray innf_connonly has two elements ([0] and [1]), the kernel uses this macro to quickly determine which direction the current hash entry belongs to, thereby knowing which IP and port to parse.
Data Structure Reference Table
When writing code, APIs alone aren't enough; you also need to know what manages what.
Table 9-2: Tables under the IPv4 Namespace
These are the names that appear in /proc/net/ip_tables_names after you load the iptables-related kernel modules.
| Linux Symbol (netns_ipv4) | Corresponding Kernel Module Path | Purpose |
|---|---|---|
| iptable_filter | net/ipv4/netfilter/iptable_filter.c | The default filter table, used for firewall filtering. |
| iptable_mangle | net/ipv4/netfilter/iptable_mangle.c | The mangle table, used for modifying packet headers (TOS, etc.). |
| iptable_raw | net/ipv4/netfilter/iptable_raw.c | The raw table, used for processing packets before connection tracking (configuring NOTRACK). |
| arptable_filter | net/ipv4/netfilter/arp_tables.c | ARP packet filtering table. |
| nat_table | net/ipv4/netfilter/iptable_nat.c | The NAT table, responsible for address translation. |
| iptable_security | net/ipv4/netfilter/iptable_security.c | SELinux-related security table (requires CONFIG_SECURITY to be enabled). |
Table 9-3: Tables under the IPv6 Namespace
The structure is almost identical to IPv4, just with an extra 6 in the names.
| Linux Symbol (netns_ipv6) | Corresponding Kernel Module Path |
|---|---|
| ip6table_filter | net/ipv6/netfilter/ip6table_filter.c |
| ip6table_mangle | net/ipv6/netfilter/ip6table_mangle.c |
| ip6table_raw | net/ipv6/netfilter/ip6table_raw.c |
| ip6table_nat | net/ipv6/netfilter/ip6table_nat.c |
| ip6table_security | net/ipv6/netfilter/ip6table_security.c |
Table 9-4: Netfilter Hook Priorities
This is the key to whether your hook function runs before or after conntrack. The smaller the value, the higher the priority (the earlier it executes).
| Priority Macro | Value | Typical Purpose |
|---|---|---|
| NF_IP_PRI_FIRST | INT_MIN | Executes first, for certain operations that must preempt others. |
| NF_IP_PRI_CONNTRACK_DEFRAG | -400 | Defragmentation. Must reassemble packets before connection tracking. |
| NF_IP_PRI_RAW | -300 | raw table. Used for -j NOTRACK, to intercept packets before tracking. |
| NF_IP_PRI_CONNTRACK | -200 | Connection Tracking. This is the most critical step. |
| NF_IP_PRI_MANGLE | -150 | mangle table. Modifies packet headers. |
| NF_IP_PRI_NAT_DST | -100 | DNAT. Destination address translation (usually before routing). |
| NF_IP_PRI_FILTER | 0 | filter table. This is the default iptables filtering point. |
| NF_IP_PRI_SECURITY | 50 | security table. |
| NF_IP_PRI_NAT_SRC | 100 | SNAT. Source address translation (usually after routing). |
| NF_IP_PRI_CONNTRACK_HELPER | 300 | Helper. Auxiliary connection handling for FTP/SIP. |
| NF_IP_PRI_LAST | INT_MAX | Executes last. |
Defined at: include/uapi/linux/netfilter_ipv4.h
Tools and Libraries
Finally, let's step back from kernel code into the real world. Debugging Netfilter requires more than just printk; you need a capable set of tools.
conntrack-tools
This is the official userspace toolset provided by Netfilter, consisting of two parts:
conntrack: Command-line tool.- Purpose: Directly view the connection tracking table.
- Common commands:
conntrack -L: List all current connections (like looking at a police station's roster of registered suspects).conntrack -D <ip>: Delete all connections for a specific IP (sometimes connections get stuck and need to be manually severed).
conntrackd: Daemon.- Purpose: Used for connection synchronization (e.g., in active-passive load balancing scenarios, where the conntrack tables on two machines must stay consistent) and event logging.
libnetfilter_* Libraries
If you want to write C programs to manipulate Netfilter instead of writing kernel modules directly, you need these libraries:
- libnetfilter_conntrack: Manipulate the connection tracking table in userspace (the
conntrackcommand is written using this library). - libnetfilter_queue: Queue packets from the kernel to userspace for processing (commonly used in complex IDS/IPS).
- libnetfilter_log: Retrieve logged packets from the kernel.
Official resource: For details, see the Netfilter official website.
Chapter Echoes
In this chapter, we dismantled the "control hub" of the Linux network subsystem—Netfilter.
Remember the suspense we left at the beginning? How can iptables calmly decide whether a packet should stay, be disguised, or be dropped the moment it flies past the network card?
The answer isn't in the iptables commands, but in the kernel's five Hook points and that massive hash table. The essence of Netfilter is a collaborative framework: it defines a set of rules (Hooks) that allow various independent modules—firewalls, NAT, connection tracking, or even your own custom modules—to intercept the packet's path without modifying the main network stack code.
We saw how connection tracking acts like a "seasoned detective," not just looking at the packet in front of it, but remembering who is "talking" to whom (Tuple); we saw how NAT quietly modifies the addresses in the packet header based on this memory, enabling conversations between internal and external networks; and we saw how Helpers act like "special task forces" to handle complex protocols like FTP that not only talk but also "open another room."
Most importantly, we established a priority- and extension-based mindset. All modules queue up on the same chain; priority determines who acts first, and the extension mechanism determines who can sneak custom data into the file.
In the next chapter, we'll leave this complex network logic behind and enter a more foundational domain—Socket Buffers (SKB). If Netfilter is the brain's decision-making logic, then SKB is the data blood that carries the information. Only by understanding the structure of SKB can you truly understand the physical form of Linux network data.
Exercises
Exercise 1: Understanding
Question: Suppose you need to intercept and analyze all IPv4 packets on a Linux server that are "about to arrive at the local machine but have not yet undergone a routing lookup." Based on the Netfilter hook workflow, at which hook point should you register your callback function? Please specify which specific function in the kernel network stack this hook point is located in.
Answer and Analysis
Answer: NF_INET_PRE_ROUTING, located in the ip_rcv() function.
Analysis: According to the definition of Netfilter hooks, NF_INET_PRE_ROUTING is the first hook point reached by all inbound packets. At this point, the packet has just entered the kernel via the network card driver and is undergoing initial protocol stack processing, but has not yet undergone a routing lookup (which determines whether to deliver it locally or forward it to another host). Therefore, to intercept all inbound packets, this is the earliest opportunity. The text explicitly states that this hook is located in the IPv4 ip_rcv() method.
Exercise 2: Application
Question: If an administrator wants to write a Netfilter kernel module to modify the IP headers of all "locally generated and about to be sent" packets (e.g., to implement a transparent proxy or IP masquerading), at which Netfilter hook point should the module register? What is the corresponding kernel function?
Answer and Analysis
Answer: NF_INET_LOCAL_OUT, located in the __ip_local_out() function.
Analysis: Locally generated outbound packets, after a routing lookup determines the output interface but before finally leaving the kernel for the network card driver, pass through the NF_INET_LOCAL_OUT hook. This is the optimal location to modify the contents of locally sent packets (such as performing SNAT or modifying ports). The text indicates that this hook is located in the __ip_local_out() method, processing before NF_INET_POST_ROUTING (ip_output), at which point the packet has already passed the local routing lookup.
Exercise 3: Application
Question: In the Linux kernel's connection tracking mechanism, when a packet arrives for the first time, the system creates a new nf_conn entry. Please describe the intermediate state of this new entry from creation to正式生效 (insertion into the hash table), and which function is responsible for transitioning it from the "intermediate state" to the "officially effective" state.
Answer and Analysis
Answer: The intermediate state is called the "unconfirmed" state, where the entry resides on the network namespace's unconfirmed list. The __nf_conntrack_confirm() function is responsible for moving it from the unconfirmed list into the hash table.
Analysis: When resolve_normal_ct() creates a new connection, it calls init_conntrack(). At this point, the entry is not immediately placed into the main hash table, but rather into the unconfirmed list of netns_ct. This is done to prevent the hash table from being polluted by invalid entries if the packet is subsequently dropped (e.g., due to a checksum error). Only when the packet successfully passes subsequent validation (typically in the NF_INET_POST_ROUTING or NF_INET_LOCAL_IN hook), the ipv4_confirm callback calls __nf_conntrack_confirm() to officially insert the entry into the hash table and remove it from the unconfirmed list.
Exercise 4: Thinking
Question: Before actual data transfer, the FTP protocol negotiates a dynamic port through the control channel for the data connection. Simple connection tracking (matching only IP and port) cannot correctly associate such dynamic data flows. Combining your knowledge from this chapter, analyze how the Netfilter subsystem uses the "connection tracking helper module" and "expected connection" mechanisms to solve the problem of FTP data packets being incorrectly dropped?
Answer and Analysis
Answer: Netfilter uses a dedicated Helper (such as nf_conntrack_ftp) to parse the control channel's payload, extract the port about to be negotiated, and create an "Expectation" for it. When subsequent packets matching this characteristic arrive, the kernel marks them with the RELATED state instead of treating them as invalid new connections.
Analysis: For complex protocols like FTP, the packet payload in the control connection (e.g., port 21) contains the address and port information for the subsequent data connection. The nf_conntrack_ftp helper loaded in the kernel performs deep inspection of these packets, reading the PORT or PASV commands in the payload. Based on this information, the kernel creates an nf_conntrack_expect structure, telling the kernel: "A connection to port X is coming soon, and it belongs to the current control connection." When that data packet actually arrives, the connection tracking system finds that it matches the Expectation, thereby establishing the association and marking the connection state as IPS_EXPECTED (expected connection). This way, firewall rules (like iptables -m conntrack --ctstate RELATED) can recognize and allow this dynamic packet, rather than treating it as an unauthorized inbound connection and dropping it.
Key Takeaways
The Netfilter framework allows kernel modules to intercept packets at these checkpoints by pre-setting five "hooks" (such as PRE_ROUTING and LOCAL_IN) on the critical paths of the protocol stack. Whether implementing firewall filtering, NAT translation, or more advanced IPVS load balancing, it essentially boils down to calling the NF_HOOK macro to register callback functions at these stubs and returning verdicts like NF_ACCEPT or NF_DROP, thereby intervening in the fate of traffic without modifying the mainline code.
Connection tracking is the cornerstone of NAT and stateful firewalls. It records tuple information for bidirectional communication through a hash table, transforming the stateless network stack into stateful session management. This mechanism has a very high priority, intervening before packets enter routing decisions; it creates struct nf_conn entries when it sees new traffic, and quickly matches subsequent arriving packets to update state or timeouts. Only when a packet finally passes all checks is the entry officially confirmed and written to the global hash table.
Netfilter is not a single firewall, but an extensible foundation supporting upper-layer functionalities like IPVS (L4 load balancing), IP sets (efficiently managing massive IP sets), and iptables. IPVS leverages hooks to modify forwarding destination addresses for traffic distribution, while IP sets optimizes matching performance through kernel hash tables, solving the efficiency bottleneck of traditional firewalls when handling large numbers of addresses.
To address the multi-channel problems of complex protocols like FTP, Netfilter introduces the connection tracking helper mechanism. These helpers act as "protocol interpreters," listening for specific patterns in the control channel (like FTP's PORT command) and dynamically creating "expected connection" records. This allows the subsequently initiated data connections to be associated with the main connection via the master pointer, and recognized by the firewall as being in the RELATED state for automatic allowance, solving the challenge of dynamic ports traversing the firewall.
As a userspace frontend tool, the core role of iptables is to push rules down to the kernel via setsockopt. The tables in the kernel (like the filter table) then register to the corresponding Netfilter hooks based on protocol family and priority. Although iptables is currently mainstream, the kernel has evolved toward nftables. The latter solves the code duplication and rule update performance issues of the old architecture through a unified codebase and a virtual machine execution engine.