3. iptables与Netfilter关系:用户态工具、内核态匹配与目标、表与链映射
iptables 是 Linux 系统中最经典的防火墙配置工具,但它本身并不执行数据包过滤。真正完成过滤、修改、跟踪等工作的,是内核中的 Netfilter 框架。iptables 只是 Netfilter 的用户态配置前端,通过 setsockopt/getsockopt 系统调用与内核态 xtables 模块通信。
核心认知:iptables 是命令解析器 + 规则下发通道;Netfilter 是规则执行引擎。两者通过
struct xt_table 和 struct xt_entry 在内核中完成映射。
3.1 整体架构:用户态 ↔ 内核态
iptables 命令在用户态解析规则,通过 getsockopt / setsockopt 系统调用(协议族 AF_INET,选项 IPT_SO_SET_REPLACE / IPT_SO_GET_INFO)将规则集传递给内核。内核中的 xtables 核心层 负责将规则挂载到对应 Netfilter 钩子点。
3.2 表(table)与链(chain)的映射关系
iptables 中的表对应内核中的 struct xt_table,链对应内核中挂载到特定 Netfilter 钩子点的规则列表。每个表可以注册到多个钩子点,每个钩子点可以被多个表共享。
| 表名 | 内核结构 | 注册的钩子点 | 功能 |
|---|---|---|---|
| filter | xt_filter_table |
LOCAL_IN, FORWARD, LOCAL_OUT | 数据包过滤(默认表) |
| nat | xt_nat_table |
PREROUTING, POSTROUTING, LOCAL_OUT | 网络地址转换 |
| mangle | xt_mangle_table |
PREROUTING, LOCAL_IN, FORWARD, LOCAL_OUT, POSTROUTING | 数据包修改(TOS、TTL 等) |
| raw | xt_raw_table |
PREROUTING, LOCAL_OUT | 连接跟踪豁免(NOTRACK) |
| security | xt_security_table |
LOCAL_IN, FORWARD, LOCAL_OUT | SELinux 安全策略标记 |
优先级顺序:在同一钩子点,多个表的执行顺序固定为:
raw → mangle → nat (PREROUTING) → filter → security。这是由内核中 xt_table 的 priority 字段决定的。
3.3 内核态 xtables 匹配(match)与目标(target)
每条 iptables 规则由匹配条件和目标动作组成。在内核中,它们分别对应 xt_match 和 xt_target 结构体,以内核模块形式动态加载。
3.3.1 匹配模块(match)
匹配模块负责检查数据包是否满足特定条件。内核通过 struct xt_match 注册,关键字段包括:
- name:匹配名称,如 "tcp"、"conntrack"、"state"
- family:协议族(NFPROTO_IPV4 / NFPROTO_IPV6)
- match:核心回调函数,返回 true/false 决定是否匹配
- checkentry:规则插入时校验参数合法性
/* 内核 xt_match 结构体核心定义 (include/linux/netfilter/x_tables.h) */
struct xt_match {
const char *name;
u8 revision;
bool (*match)(const struct sk_buff *skb,
struct xt_action_param *par);
int (*checkentry)(const struct xt_mtchk_param *par);
void (*destroy)(const struct xt_mtdtor_param *par);
struct module *me;
const char *table;
unsigned int matchsize;
unsigned int hooks;
unsigned short proto;
unsigned short family;
/* ... */
};
3.3.2 目标模块(target)
目标模块定义匹配成功后执行的动作。常见目标包括 ACCEPT、DROP、LOG、DNAT、SNAT、MASQUERADE 等。
| 目标名称 | 内核模块 | 动作说明 | 返回值 |
|---|---|---|---|
| ACCEPT | 内置于核心 | 允许数据包通过,终止当前表遍历 | NF_ACCEPT |
| DROP | 内置于核心 | 丢弃数据包,不回复 | NF_DROP |
| LOG | xt_LOG.ko |
记录日志到内核 ring buffer,继续匹配 | XT_CONTINUE |
| DNAT | xt_DNAT.ko |
修改目的 IP/端口(PREROUTING 链) | NF_ACCEPT |
| SNAT | xt_SNAT.ko |
修改源 IP/端口(POSTROUTING 链) | NF_ACCEPT |
| MASQUERADE | xt_MASQUERADE.ko |
动态 SNAT(用于 PPPoE 等动态 IP) | NF_ACCEPT |
| CT | xt_CT.ko |
手动指定连接跟踪行为 | NF_ACCEPT |
关键区别:ACCEPT/DROP 是判决目标,直接返回 Netfilter 判决值(NF_ACCEPT/NF_DROP);LOG/ULOG 是非判决目标,返回 XT_CONTINUE 让规则继续遍历。自定义目标模块必须明确返回语义。
3.4 表与链的内核注册机制
每个表在内核中通过 xt_register_table() 注册,指定其所属的钩子点和优先级。链是表内部的规则列表,通过 struct xt_table_info 管理。
/* 内核 filter 表注册示例 (net/ipv4/netfilter/iptable_filter.c) */
static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS, /* 位图:LOCAL_IN | FORWARD | LOCAL_OUT */
.me = THIS_MODULE,
.af = NFPROTO_IPV4,
.priority = NF_IP_PRI_FILTER, /* 优先级值,决定表在钩子点的执行顺序 */
};
static int __init iptable_filter_init(void)
{
return xt_register_table(net, &packet_filter, &filter_ops);
}
3.5 规则遍历流程:从用户态到内核态
- 用户态解析:iptables 解析命令行,构建
struct ipt_replace结构体,包含所有规则条目。 - 系统调用:通过
setsockopt(IPT_SO_SET_REPLACE)将规则集传入内核。 - 内核校验:
do_replace()函数校验规则合法性,调用各匹配/目标的checkentry。 - 规则挂载:将规则链挂入对应表的
xt_table_info中,替换旧规则集。 - 钩子触发:数据包到达 Netfilter 钩子点时,
nf_hook_slow()遍历该钩子点上的所有表,按优先级执行规则。 - 规则匹配:对每条规则,依次调用所有匹配模块的
match(),全部通过后执行目标模块的target()。
3.6 关键数据结构映射总结
| iptables 概念 | 内核数据结构 | 说明 |
|---|---|---|
| 表 (table) | struct xt_table |
包含表名、有效钩子点位图、优先级 |
| 链 (chain) | struct xt_table_info 中的规则数组 |
每个钩子点对应一个规则链,链是规则数组的切片 |
| 规则 (rule) | struct xt_entry / struct ipt_entry |
包含匹配段和目标段,通过偏移量链接 |
| 匹配 (match) | struct xt_entry_match + struct xt_match |
匹配条件,可叠加多个 |
| 目标 (target) | struct xt_entry_target + struct xt_target |
动作定义,每条规则只有一个目标 |
| 钩子点 | nf_hook_ops |
表通过 xt_table 注册到钩子点 |
开发启示:编写自定义 Netfilter 模块时,你实际上是在注册
xt_match 或 xt_target,而不是直接操作钩子点。iptables 用户态工具会自动将你的模块与表/链关联。理解这套映射关系,是开发高效内核模块的基础。