适用场景:
你需要一台 Linux 服务器做流量调度或地址转换:
按 IP、端口选择路由出口
端口映射(DNAT / REDIRECT)
内网设备上网(MASQUERADE / SNAT)
本文独立于任何隧道协议(GRE、IPIP、WireGuard、VXLAN 等均可参考),提供策略路由和 NAT 转发的通用配置方法与排障手段。
一、策略路由
1.1 核心原理
策略路由的本质是:让特定的流量走指定的路由表。
Linux 提供了两种实现方式:
选择建议:能用 ip rule 原生条件解决的,优先使用,避免引入 iptables 的额外复杂度。
1.2 按目标 IP 选路(最常用)
适用场景:将去往特定目标 IP 或网段的流量导入指定出口(如 VPN 隧道、特定网卡)。
方法一:直接添加路由(最简单)
ip route add 183.2.172.177 via 192.168.1.1 dev eth1方法二:独立路由表 + ip rule(推荐,最清晰)
# 1. 创建路由表
echo "100 custom_table" >> /etc/iproute2/rt_tables
# 2. 添加路由
ip route add 183.2.172.177 via 192.168.1.1 dev eth1 table custom_table
# 3. 策略路由:匹配该目标 IP 的流量使用 custom_table
ip rule add to 183.2.172.177 table custom_table priority 100CIDR 网段示例:
ip route add 10.0.0.0/8 via 192.168.1.1 dev eth1 table custom_table
ip rule add to 10.0.0.0/8 table custom_table priority 1001.3 按源 IP 选路
适用场景:将特定客户端的所有流量导入指定出口。
ip route add default via 192.168.1.1 dev eth1 table custom_table
ip rule add from 192.168.0.100 table custom_table priority 1000⚠️ 注意:此配置会将客户端的所有流量(含访问内网的流量)导入指定出口,需确保对端有回程路由。
1.4 按端口选路(iptables + fwmark)
适用场景:只将特定应用(如 Web 流量)导入指定出口,Linux 路由表不支持按端口匹配,必须借助 iptables。
# 1. 创建路由表
echo "100 web_table" >> /etc/iproute2/rt_tables
ip route add default via 192.168.1.1 dev eth1 table web_table
# 2. iptables 打标
iptables -t mangle -A OUTPUT -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -p tcp --dport 443 -j MARK --set-mark 1
# 3. 策略规则
ip rule add fwmark 1 table web_table priority 1001.5 多条件匹配
适用场景:需要同时匹配多个条件(如源 IP + 目标端口)。
# 来自 192.168.0.100 且访问 80 端口的流量走指定路由表
iptables -t mangle -A OUTPUT -s 192.168.0.100 -p tcp --dport 80 -j MARK --set-mark 1
ip rule add fwmark 1 table web_table priority 1001.6 查看与验证
# 查看策略规则
ip rule show
# 查看路由决策
ip route get 183.2.172.177
ip route get 183.2.172.177 from 192.168.0.100
# 查看路由表中的路由
ip route show table custom_table
# 查看所有路由表
ip route show table all | grep -E "dev eth1|via"1.7 清理与恢复
# 删除策略规则(按条件)
ip rule del to 183.2.172.177 table custom_table priority 100
ip rule del from 192.168.0.100 table custom_table priority 1000
ip rule del fwmark 1 table web_table priority 100
# 或按优先级删除
ip rule del priority 100
# 删除路由表路由
ip route flush table custom_table
# 删除路由表定义(可选)
sed -i '/custom_table/d' /etc/iproute2/rt_tables
# 删除 iptables 规则
iptables -t mangle -D OUTPUT -p tcp --dport 80 -j MARK --set-mark 1二、NAT 转发
2.1 前置条件
所有 NAT 转发配置前,需确保 IP 转发已开启:
# 临时生效
echo 1 > /proc/sys/net/ipv4/ip_forward
# 永久生效
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p2.2 NAT 类型总览
2.3 FORWARD 转发规则
转发的流量必须经过 FORWARD 链。标准写法如下:
# 1. 放行回包(建议保留,确保兼容性)
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
# 2. 放行请求包(按需配置)
# - 按源 IP 放行(推荐,覆盖所有目标)
iptables -A FORWARD -s 172.18.0.0/20 -j ACCEPT
# - 按目标 IP 放行
iptables -A FORWARD -d 192.168.0.0/20 -j ACCEPT配置前检查:
# 查看 FORWARD 当前默认策略
iptables -L FORWARD | head -1
# 若为 Chain FORWARD (policy DROP),必须放行回包,否则通信失败2.4 端口转发(REDIRECT / DNAT)
场景一:转发到本机其他端口(REDIRECT)
将访问本机 80 端口的流量转发到本机 8080 端口。
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080场景二:转发到远程主机(DNAT + SNAT)
将访问本机 80 端口的流量转发到远程服务器 192.168.1.100:8080。
# DNAT(修改目标 IP)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:8080
# SNAT(修改源 IP,确保回包回到本机)
iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 8080 -j SNAT --to-source 192.168.0.8
# 放行 FORWARD
iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 8080 -j ACCEPT
iptables -A FORWARD -p tcp -s 192.168.1.100 --sport 8080 -j ACCEPT⚠️ 为什么必须加 SNAT?
不加 SNAT 时,后端服务器回包直接发给客户端,绕过了本机,导致连接不对称、被客户端丢弃。SNAT 将源 IP 改为本机 IP,确保回包回到本机。
2.5 转发到公网(MASQUERADE / SNAT)
使内网设备通过本机访问互联网。
# MASQUERADE(推荐,自动适配出口 IP)
iptables -t nat -A POSTROUTING -s 172.18.0.0/20 -j MASQUERADE
# SNAT(固定源 IP,性能更好)
# 云环境(阿里云/腾讯云/AWS)注意:to-source 应填内网 IP,回包由云平台 DNAT 处理
iptables -t nat -A POSTROUTING -s 172.18.0.0/20 -j SNAT --to-source 5.6.7.8
# 放行 FORWARD
iptables -A FORWARD -s 172.18.0.0/20 -j ACCEPTMASQUERADE vs SNAT:
2.6 保存与持久化
# CentOS/RHEL
iptables-save > /etc/iptables.rules
# Debian/Ubuntu
apt install iptables-persistent -y
netfilter-persistent save三、OpenWrt 配置转发
本文前两章基于 iptables。若在 OpenWrt 22.03+ 上配置,底层已切换为 fw4/nftables,配置方式有所不同。
完整的 OpenWrt 配置示例(含 GRE 隧道场景)请参考《GRE 隧道转发与抓包分析》—— 四、B 侧 NAT 转发(OpenWrt 22.03)。
四、排障专题
4.1 FORWARD 未放行
症状:流量不通,conntrack 无记录或显示 [UNREPLIED]。
# 查看 FORWARD 链规则
iptables -L FORWARD -n -v
# 查看 FORWARD 默认策略
iptables -L FORWARD | head -1
# 临时放行所有转发(测试用)
iptables -A FORWARD -j ACCEPT4.2 rp_filter 反向路径过滤
症状:从隧道/特定接口进来的包被丢弃,dmesg 可能看到 martian source。
原理:rp_filter 检查数据包的回程路由是否与接收接口一致,若不一致则丢弃。
0:不检查
1:严格模式:回程路由必须与接收接口一致
2:松散模式(默认):只要源 IP 路由可达即放行
# 检查当前值
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.tun1.rp_filter
# 临时修复
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/tun1.rp_filter
# 永久生效
cat >> /etc/sysctl.conf << EOF
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.tun1.rp_filter = 0
EOF
sysctl -p4.3 conntrack 连接跟踪
# 查看连接跟踪
conntrack -L
# 按 IP 过滤
conntrack -L | grep 172.18.0.6
# 实时跟踪连接事件
conntrack -E
# 清空连接跟踪表(排障时常用)
conntrack -D输出解读:
[ASSURED]:双向通信正常 -> ✅ NAT 生效
[UNREPLIED]:只有请求无回包 -> ❌ 检查 FORWARD 或路由
无记录:包没到 B 或没出 B -> ❌ 检查 FORWARD 是否放行
4.4 抓包定位
# 通用抓包命令
tcpdump -i any -nn host 172.18.0.6
# 只看特定接口
tcpdump -i eth0 -nn host 183.2.172.177
tcpdump -i eth0 -nn host www.baidu.com
tcpdump -i tun1 -nn src 172.18.0.6
# 只看特定协议(如 GRE)
tcpdump -i eth0 proto gre
# 组合条件
tcpdump -i tun1 -n "icmp and (host 172.18.0.10 or host 172.18.0.6)"4.5 排障决策树
流量不通?
│
▼
ip route get <目标IP> # 路由决策是否正确?
│
├── 路由错误 → 检查路由表和策略规则
│
└── 路由正确 ↓
conntrack -L | grep <源IP> # NAT 是否生效?
│
├── 无记录 → 检查 FORWARD 链是否放行
│
├── [UNREPLIED] → 检查回包路由或 rp_filter
│
└── [ASSURED] → 检查目标服务是否正常五、综合案例:跨 VPC 转发(C -> B -> A)
本章是一个综合应用案例,展示策略路由、VPC 路由表和云平台配置的协同工作。
场景说明:阿里云 B 与阿里云 A 已建立 GRE 隧道。C 是 B 所在 VPC(192.168.0.0/20)内的一台独立服务器,需要通过 B 访问 A 所在 VPC(172.18.0.0/20)内的资源(如服务器 A 或内网其他设备)。由于 VPC 路由表默认不含跨 VPC 路由,需额外配置才可连通。
5.1 场景拓扑
服务器 C (192.168.0.9)
│
▼
服务器 B (192.168.0.8) ← 作为网关
│
▼
GRE 隧道 (tun1) ← 172.16.0.1 ↔ 172.16.0.2
│
▼
服务器 A (172.18.0.6) → 设备 (172.18.0.10)5.2 配置 VPC 路由
在 C 上添加本地路由:
# 将发往 A 所在网段的流量指向 B
# 172.18.0.0/20 是服务器 A 的内网网段
ip route add 172.18.0.0/20 via 192.168.0.8 dev eth0云平台侧需添加自定义路由(阿里云/腾讯云控制台):
目标网段: 172.18.0.0/20
下一跳: 192.168.0.8(服务器 B 的内网 IP)⚠️ 关键理解:为什么 C 上配了路由还不够?
在物理网络中,C 上配置
ip route add 172.18.0.0/20 via 192.168.0.8后,包会直接发给 B。但在云 VPC 网络中,C 发出的包到达 VPC 虚拟交换机时,交换机会检查 VPC 路由表。
VPC 路由表中没有
172.18.0.0/20的路由,交换机不知道如何转发,直接丢弃。
5.3 安全组放行
5.4 数据包路径
C (192.168.0.9) → 发送到 172.18.0.0/20
│
▼
云虚拟交换机 → 查 VPC 路由表
│
▼(有自定义路由:172.18.0.0/20 → 192.168.0.8)
│
▼
B (192.168.0.8) → 查本地路由
│
▼(有路由:172.18.0.0/20 via 172.16.0.1 dev tun1)
│
▼
GRE 隧道 → A (172.18.0.6) → 设备 (172.18.0.10)附录:排障命令速查
路由走哪条路:ip route get <目标IP>
查看所有路由表:ip route show table all | grep tun1
策略规则是否生效:ip rule show
查看 FORWARD 规则:iptables -L FORWARD -n -v
查看 NAT 规则:iptables -t nat -L -n -v
抓包-隧道内流量:tcpdump -i tun1 -nn
抓包-GRE 包是否发出:tcpdump -i eth0 proto gre -nn
连接跟踪-NAT 是否生效:conntrack -L | grep <IP>
端口转发是否生效:nc -l -p 8080 + curl