1. 引言: 为什么需要 NAT?
想象一下, 你住在一个大型公寓楼里, 整栋楼只有一个对外公开的地址 (如「中山路123号」) , 但楼内有数百个住户. 邮递员送信时, 只能送到大楼前台, 然后由管理员根据房间号将信件分发给具体住户. 这就是 NAT (网络地址转换) 的基本思想——在有限的公共 IP 地址背后, 支持大量使用私有地址的设备上网。
在 Linux 中, NAT 功能主要由 Netfilter 框架实现,这个强大的网络/系统框架允许内核模块干预网络通信。自 Linux 2.4 版本引入以来,Netfilter 已成为 Linux 网络栈的核心组件。
2. Linux NAT 的设计哲学
2.1 连接跟踪: NAT 的基石
连接跟踪 (Connection Tracking, 简称 conntrack) 是 NAT 能够正常工作的前提。它的作用就像邮局管理员记录每一封信件的收发记录。
// 连接跟踪的核心数据结构 (简化版)
struct nf_conn {
// 连接状态 (NEW, ESTABLISHED, RELATED 等)
enum ip_conntrack_info status;
// 元组 (Tuple) : 描述连接的关键信息
struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];
// 超时时间
unsigned long timeout;
// 引用计数
atomic_t use;
// NAT 转换信息
struct nf_conn_nat nat;
};
连接跟踪的工作流程:

2.2 Netfilter 的五个钩子点
Netfilter 在内核网络栈中设置了五个关键的拦截点(钩子),就像在快递分拣中心设置五个检查站。

五个钩子点的作用:
| 钩子点 |
触发时机 |
主要用途 |
| NF_INET_PRE_ROUTING |
数据包进入网络栈,路由决策之前 |
目的地址转换 (DNAT) |
| NF_INET_LOCAL_IN |
数据包目的地是本机,路由之后 |
过滤到本机的数据包 |
| NF_INET_FORWARD |
数据包目的地是其他主机 |
转发过滤 |
| NF_INET_LOCAL_OUT |
本机进程发出的数据包 |
本地发出数据包的过滤 |
| NF_INET_POST_ROUTING |
数据包离开网络栈之前 |
源地址转换 (SNAT) |
3. NAT 的核心类型与工作原理
3.1 SNAT (源地址转换)
生活比喻: 公司员工用公司总机向外打电话,对方看到的是公司总机号码,而不是员工的分机号。
工作流程:
- 内部主机 (192.168.1.100) 发送数据包到外部服务器。
- 数据包到达 POSTROUTING 链时,SNAT 规则将源 IP 改为公网 IP。
- 外部服务器回复时,目标地址是公网 IP。
- Linux 路由器根据连接跟踪记录,将目标 IP 改回内部 IP。
# 典型的 SNAT 规则
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 203.0.113.1
3.2 DNAT (目的地址转换)
生活比喻: 客户拨打公司总机号码,前台根据分机表将电话转接到具体部门。
工作流程:
- 外部客户端向公网 IP 的特定端口发送请求。
- 数据包到达 PREROUTING 链时,DNAT 规则将目标 IP 改为内部服务器 IP。
- 内部服务器回复时,源地址会自动改回公网 IP (通过连接跟踪的反向转换)。
# 典型的 DNAT 规则
iptables -t nat -A PREROUTING -d 203.0.113.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:80
3.3 MASQUERADE (伪装)
这是特殊类型的 SNAT,常用于动态获取 IP 的情况 (如 PPPoE 拨号),它会自动使用出口接口的当前 IP。
# MASQUERADE 会自动使用出口接口的当前 IP
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
4. Linux NAT 的核心数据结构
4.1 连接跟踪的关键结构
// 连接元组: 唯一标识一个连接
struct nf_conntrack_tuple {
struct {
// 源地址/目的地址
union nf_inet_addr u3;
// 协议号 (TCP/UDP等)
u_int8_t protonum;
// 源端口/目的端口 (TCP/UDP) 或ID (ICMP)
union {
u_int16_t all;
} u;
} src;
// 方向信息
struct {
union nf_inet_addr u3;
u_int8_t protonum;
union {
u_int16_t all;
} u;
} dst;
// 三层协议族 (IPv4/IPv6)
u_int16_t l3num;
};
4.2 NAT 映射信息结构
struct nf_nat_range {
// 地址映射标志
unsigned int flags;
// 最小地址 (用于地址范围)
union nf_inet_addr min_addr;
// 最大地址
union nf_inet_addr max_addr;
// 最小端口
union nf_conntrack_man_proto min_proto;
// 最大端口
union nf_conntrack_man_proto max_proto;
};
4.3 数据结构关系图

5. NAT 的完整工作流程
5.1 报文处理时序图

5.2 NAT 内核处理流程
// 简化的 NAT 处理逻辑 (伪代码)
unsigned int nf_nat_in_fn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
// 获取连接跟踪信息
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return NF_ACCEPT;
// 判断是否需要 NAT 处理
if (!nf_ct_is_confirmed(ct)) {
// 新连接: 尝试 NAT 转换
int ret = nf_nat_manip_pkt(skb, ct, nf_nat_manip_type, state->hook);
if (ret != NF_ACCEPT)
return ret;
} else {
// 已确认连接: 恢复 NAT 信息
nf_nat_packet(ct, ctinfo, state->hook, skb);
}
return NF_ACCEPT;
}
6. 实际应用: 搭建简单的 NAT 网关
6.1 网络拓扑
[互联网]
|
| eth0: 203.0.113.1
[Linux NAT路由器]
| eth1: 192.168.1.1
|
[内部网络 192.168.1.0/24]
6.2 核心配置脚本
#!/bin/bash
# 启用 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 刷新现有规则
iptables -F
iptables -t nat -F
iptables -t mangle -F
# 设置默认策略
iptables -P INPUT ACCEPT
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 允许已建立的连接和相关的连接通过
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 允许内部网络向外访问
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
# 配置 SNAT (源地址转换)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# 配置 DNAT 示例: 将公网 80 端口映射到内部 Web 服务器
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
-j DNAT --to-destination 192.168.1.10:80
# 允许从外部访问 DNAT 映射的服务
iptables -A FORWARD -i eth0 -o eth1 -d 192.168.1.10 -p tcp --dport 80 -j ACCEPT
6.3 验证 NAT 工作状态
# 查看连接跟踪表
cat /proc/net/nf_conntrack
# 查看 NAT 表规则
iptables -t nat -L -n -v
# 实时监控 NAT 转换
conntrack -E
# 查看内核 NAT 统计信息
cat /proc/net/stat/nf_conntrack
7. 调试与故障排除
7.1 常用诊断命令
# 1. 检查连接跟踪表
conntrack -L
# 2. 跟踪特定连接的 NAT 过程
tcpdump -i eth0 host 203.0.113.1 and port 80 -n -v
# 3. 检查内核日志中的 NAT 相关消息
dmesg | grep -i nat
# 4. 查看 Netfilter 钩子点的数据包计数
iptables -t nat -L -n -v
iptables -L -n -v
# 5. 使用 conntrack 工具管理连接
conntrack -D -s 192.168.1.100 # 删除特定连接
conntrack -F # 清空整个连接表
7.2 常见问题及解决方案
| 问题现象 |
可能原因 |
解决方案 |
| 内部主机无法上网 |
1. IP 转发未启用<br>2. FORWARD 链策略限制<br>3. SNAT 规则错误 |
echo 1 > /proc/sys/net/ipv4/ip_forward<br>检查 iptables 规则 |
| 外部无法访问 NAT 后的服务 |
1. DNAT 规则错误<br>2. FORWARD 链阻止<br>3. 内部主机防火墙 |
确认 DNAT 规则正确<br>检查 FORWARD 链规则 |
| NAT 性能差 |
1. 连接跟踪表满<br>2. NAT 类型不匹配 |
增大 net.netfilter.nf_conntrack_max<br>优化 NAT 规则 |
| 特定协议失效 (如 FTP) |
需要特殊的 NAT 助手模块 |
加载 nf_conntrack_ftp 模块 |
8. 高级主题与优化
8.1 NAT 性能优化
# 调整连接跟踪表大小
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max
# 优化连接跟踪超时时间
echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
# 启用连接跟踪的哈希表自动调整
echo 1 > /proc/sys/net/netfilter/nf_conntrack_hashsize
# 使用多个 CPU 处理连接跟踪 (如果有多个 CPU)
echo 8192 > /sys/module/nf_conntrack/parameters/hashsize
8.2 NAT 与 Docker/Kubernetes
现代容器化与云原生网络大量使用 NAT 技术来实现容器与外部网络的通信以及服务暴露。

9. Linux NAT 的演进与替代方案
9.1 nftables: 下一代 Netfilter
nftables 旨在取代 iptables,提供更统一的语法和更好的性能。
# nftables 的 NAT 配置示例
nft add table ip nat
nft add chain ip nat prerouting { type nat hook prerouting priority 0 \; }
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
# SNAT 配置
nft add rule ip nat postrouting oif eth0 masquerade
# DNAT 配置
nft add rule ip nat prerouting iif eth0 tcp dport 80 dnat to 192.168.1.10:80
9.2 eBPF 与 NAT
最新的 Linux 内核开始支持使用 eBPF 实现高性能、可编程的网络数据路径,包括 NAT 功能。
// eBPF NAT 示例 (概念代码)
SEC("xdp_nat")
int xdp_nat_func(struct xdp_md *ctx)
{
struct ethhdr *eth = bpf_hdr_pointer(ctx);
struct iphdr *iph = (struct iphdr *)(eth + 1);
// 执行 NAT 转换
if (iph->saddr == internal_ip) {
iph->saddr = public_ip;
recalc_checksum(iph);
}
return XDP_TX;
}
10. 总结
10.1 核心要点回顾
通过本文的深入分析,我们可以看到 Linux NAT 是一个多层次、模块化的复杂系统。
- 连接跟踪是基础:没有 conntrack,就没有状态化 NAT。
- 五钩子模型是框架:PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING 构成了完整的处理流水线。
- 多种 NAT 类型适应不同场景:SNAT、DNAT、MASQUERADE 各有其用武之地。
- 内核数据结构高效协同:
nf_conn、nf_conntrack_tuple、sk_buff 等结构共同完成 NAT 功能。
10.2 Linux NAT 架构全景图

10.3 关键决策表
| 场景 |
推荐 NAT 类型 |
配置要点 |
注意事项 |
| 家庭/小型办公室共享上网 |
MASQUERADE |
-o eth0 -j MASQUERADE |
适用于动态 IP |
| 服务器负载均衡 |
DNAT |
PREROUTING 链配置 |
需要会话保持时使用 --probability |
| 云环境多租户 |
SNAT + DNAT |
结合网络命名空间 |
注意连接跟踪表大小 |
| 游戏/VoIP 应用 |
完全锥形 NAT |
调整 conntrack 超时 |
可能需要 UPnP 支持 |
| 高吞吐量网关 |
nftables + eBPF |
使用硬件卸载 |
考虑连接跟踪性能 |