人人都会AI编程

7.4 netfilter/iptables 内核框架:数据包过滤、转发、地址转换原理

更新时间:2026-07-12

哪怕你不曾直接配置过防火墙,只要你使用过 Linux 服务器或路由器,netfilter/iptables 就在后台保护着你的系统。它是 Linux 内核中实现网络数据包处理、过滤、网络地址转换(NAT)的核心框架,同时提供了一个强大但稍显抽象的用户界面——iptables 命令。

1. netfilter:内核中的数据包“安检通道”
netfilter 是内核里的一系列钩子(hook)点,分布在网络协议栈处理数据包的关键路径上。当数据包流经这些钩子点时,内核会调用你预先注册的规则函数,检查、修改或丢弃数据包。iptables 就是用来管理这些规则的最经典工具(较新的替代前端有 nftables,但原理相通)。

可以形象地理解为,内核在网络栈的不同关卡设立了五个检查站:

| 钩子点 | 触发时机 |
|------------------|----------------------------------------------------|
| PREROUTING | 数据包进入网络接口后,在进行路由决策之前 |
| INPUT | 经过路由决策,确定包是发给本机进程之后 |
| FORWARD | 经过路由决策,确定包需要转发到其他主机之后 |
| OUTPUT | 本机进程生成数据包,准备发出之前 |
| POSTROUTING | 数据包即将离开网络接口,在路由决策之后 |

数据包的流向是固定的(简化版):

  • 外部进入本机:PREROUTING → INPUT → 本地进程
  • 经过本机转发:PREROUTING → FORWARD → POSTROUTING
  • 本机发往外网:本地进程 → OUTPUT → POSTROUTING

2. 表与链:规则的容器
iptables 用“表(table)”将规则按功能分类,再用“链(chain)”将规则挂载到对应的钩子点上。常用的表有:

  • filter:默认表,负责数据包的过滤(允许/拒绝),包含 INPUT、FORWARD、OUTPUT 链。
  • nat:用于网络地址转换(修改源或目的地址),包含 PREROUTING、OUTPUT、POSTROUTING 链。
  • mangle:用于修改数据包的 TOS、TTL 等字段,较少日常使用。
  • raw:用于配置数据包不进入连接跟踪,一般用于防止某些攻击或性能调优。

每个链实质上是一系列规则的顺序列表,每条规则包含“匹配条件”和“目标动作(target)”。数据包到达某个钩子点时,会按规则顺序逐一匹配,一旦匹配到某条规则,就执行相应的 target,不再继续匹配链中后续规则(除非 target 明确要求返回链继续执行)。

3. 数据包过滤:谁可以穿过网络边界
最常用的功能,对应 filter 表。设想一台服务器只需要对外提供 SSH 服务(端口 22),那么你可以这样思考内核的行为:

在 INPUT 链上(因为包的目的地是本机),首先要允许用于维持连接状态的包(已建立连接和关联连接的包)通过,然后明确允许目标端口为 22 的新连接包,最后将其他所有进入包丢弃。规则大致如下(用 iptables 语法示例):

iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -j DROP

这里涉及连接跟踪(conntrack),内核会记录每个网络连接(五元组:源IP、目的IP、源端口、目的端口、协议)的状态。许多规则的匹配条件会引用状态,使得状态化防火墙成为默认行为,不需要为返回的应答包单独开放端口。

4. 数据包转发:让 Linux 成为路由器
当一台 Linux 主机有多个网络接口(比如一个接内网,一个接外网),并希望在内网和外网之间转发包时,就涉及 FORWARD 链和 IP 转发功能。首先要开启内核的 IP 转发:

echo 1 > /proc/sys/net/ipv4/ip_forward

此后,内核在 PREROUTING 中决定路由,发现目的 IP 不是本机,便将包送入 FORWARD 链。你可以在 FORWARD 链定义哪些方向的转发是允许的,比如只允许内网访问外网,不允许外网主动访问内网。

数据包经过 FORWARD 链后,来到 POSTROUTING,此时通常需要做地址转换才能让包正确返回到内网主机(见下节)。

5. 地址转换(NAT):改写地址,让私有网络连通世界
网络地址转换的原理很简单:在特定钩子点修改数据包的源 IP 地址或目的 IP 地址,并让连接跟踪模块记住这次修改,以便反向应答包能自动还原。

  • 源地址转换(SNAT / MASQUERADE)

用于内网主机共享单个公网 IP 访问互联网。包从内网主机发出时,源地址是私有 IP(如 192.168.1.100),无法在公网上直接路由回来。内核可以在 POSTROUTING 链上做 SNAT:将源 IP 改为出口接口的公网 IP(比如 203.0.113.5),同时修改源端口(如果必要),并记录映射关系。当应答包从公网回来时,连接跟踪自动识别并将目的 IP 和端口还原为原来内网主机的地址端口。

当公网 IP 是动态分配(如家庭宽带拨号)时,用 MASQUERADE 代替 SNAT,它会自动使用出口接口当前获取到的 IP,而不需要写死地址。

  • 目的地址转换(DNAT)

用于将发往某个公网 IP 特定端口的访问,重定向到内网某台服务器上。比如将 203.0.113.5 的 80 端口映射到内网 192.168.1.10 的 8080 端口。DNAT 发生在 PREROUTING 链,因为需要先修改目的地址,再做出路由决策。修改后,内核根据新的目的 IP 判断应将该包路由到内网接口并转发给目标主机。回包时同样依赖连接跟踪自动反向转换。

所有这些转换都依赖内核的连接跟踪子系统(nf_conntrack),它维护着每一对“原始流”和“转换后流”之间的状态。你可以用 conntrack -L 查看当前所有活跃的跟踪条目。如果跟踪表满了,新的连接可能被丢弃,这也是为什么高并发 NAT 网关需要调高 nf_conntrack_max 的原因。

6. 实战小节
用几个实际场景帮助理解:

  • 屏蔽某个 IPiptables -A INPUT -s 1.2.3.4 -j DROP(在 filter 表的 INPUT 链上丢弃来自该 IP 的包)
  • 端口转发iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:8080
  • 共享上网iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE(eth0 是公网接口)
  • 允许转发iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT 以及对应的回包规则(通常配合连接跟踪状态规则)

理解这套框架后,你就能明白为何 docker run -p 8080:80 可以在主机上开放一个端口——Docker 底层也是利用 iptables 在 PREROUTING 和 FORWARD 链上创建了相应的 DNAT 和过滤规则。

netfilter/iptables 是 Linux 网络安全的基石,清晰的“钩子-表-链-规则”四层结构赋予了它极强的灵活性和性能。一旦掌握它的数据流和转换时机,日常的防火墙配置、容器网络排错、自建 VPN 网关等任务都会变得有条不紊。