Administrator
发布于 2025-10-26 / 77 阅读
0
0

Linux 策略路由与 NAT 转发指南

适用场景:

你需要一台 Linux 服务器做流量调度或地址转换:

  • 按 IP、端口选择路由出口

  • 端口映射(DNAT / REDIRECT)

  • 内网设备上网(MASQUERADE / SNAT)

本文独立于任何隧道协议(GRE、IPIP、WireGuard、VXLAN 等均可参考),提供策略路由和 NAT 转发的通用配置方法与排障手段。

一、策略路由

1.1 核心原理

策略路由的本质是:让特定的流量走指定的路由表。

Linux 提供了两种实现方式:

方式

适用场景

灵活性

原生规则(ip rule)

按目标 IP、源 IP、接口匹配

有限

iptables 打标 + ip rule fwmark

按端口、协议、连接状态、应用层特征等匹配

灵活

选择建议:能用 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 100

CIDR 网段示例:

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 100

1.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 100

1.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 100

1.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 -p

2.2 NAT 类型总览

NAT 类型

修改对象

适用场景

DNAT

PREROUTING

目标 IP/端口

端口映射、负载均衡

SNAT

POSTROUTING

源 IP(固定)

固定源 IP 访问外部

MASQUERADE

POSTROUTING

源 IP(自动)

动态 IP 上网

REDIRECT

PREROUTING

目标端口(重定向到本机)

透明代理、本地端口映射

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 ACCEPT

MASQUERADE vs SNAT:

特性

MASQUERADE

SNAT

出口 IP

自动适配

固定指定

适用性能

出口 IP 动态(DHCP、拨号)

出口 IP 固定

性能

略差(需查表)

更好

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 ACCEPT

4.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 -p

4.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 安全组放行

协议

来源

目的

说明

TCP

本 VPC 网段(192.168.0.0/20)

全部端口

业务流量

ICMP

全部

全部

排障测试

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


评论