本文基于《Linux 策略路由与 NAT 转发指南》的通用技术,聚焦于 GRE 隧道场景。
一、背景
1.1 基本参数
1.2 前置条件
✅ GRE 隧道已建立(参考《GRE 隧道配置》)
✅ A 侧路由已配置(指定哪些流量走隧道)
1.3 本文目标
A 的流量到达 B 后,B 需要根据目标地址做出不同处理:
二、B 侧 NAT 转发配置(CentOS / 通用 Linux)
以下命令均已在通用指南中详细解释,此处只给具体参数。
放行 FORWARD 回包:
# 查看 FORWARD 当前默认策略
iptables -L FORWARD | head -1
# 若为 Chain FORWARD (policy DROP), 需显式放行回包
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT2.1 A 访问 B 所在 VPC 内网
目标:A 访问 192.168.0.0/20 的流量到达 B 后,B 将其转发到内网。
# 放行 FORWARD(放行 A 的请求包(二选一))
# 仅内网转发
iptables -A FORWARD -d 192.168.0.0/20 -j ACCEPT
# 内网+公网转发(推荐)
iptables -A FORWARD -s 172.18.0.0/20 -j ACCEPT验证:
# 在 A 上执行
ping 192.168.0.1
curl http://192.168.0.100:802.2 A 通过 B 访问公网
目标:A 访问公网的流量到达 B 后,B 做 MASQUERADE,将源 IP 改为 B 的公网 IP,访问互联网。
# 放行 FORWARD(放行 A 的请求包)
iptables -A FORWARD -s 172.18.0.0/20 -j ACCEPT
# 做 MASQUERADE
iptables -t nat -A POSTROUTING -s 172.18.0.0/20 -j MASQUERADE验证:
# 在 A 上执行(数据包从 tun1 出去,并且协议是 IPv4)
curl -4 --interface tun1 http://www.baidu.com2.3 端口转发
将来自 A 访问 B 特定端口的流量,转发到 B 本机的其他端口或内网其他服务器。
详细配置方法请参考《Linux 策略路由与 NAT 转发完全指南》DNAT / REDIRECT 章节。
三、抓包验证
3.1 内网转发抓包
隧道入口(B 侧):
tcpdump -i tun1 -nn src 172.18.0.0/20 and dst 192.168.0.0/20预期输出:
172.18.0.6 > 192.168.0.1: ICMP echo request
172.18.0.6 > 192.168.0.100.80: Flags [S]3.2 公网转发抓包
隧道入口(B 侧):
tcpdump -i tun1 -nn src 172.18.0.0/20公网出口(B 侧):
# 183.2.172.177 是不指定 host 抓包看到的百度 IP
tcpdump -i eth0 -nn host 183.2.172.177
# GRE 封装抓包
tcpdump -i eth0 -nn 'ip proto 47'
tcpdump -i eth0 -nn proto gre预取输出:tun1 能看到 A 的请求(源 IP 172.18.0.6),eth0 能看到 MASQUERADE 后的包(源 IP 变为 B 的公网 IP)。
3.3 连接跟踪验证(验证 NAT)
# 在 B 上执行
conntrack -L | grep 172.18.0.6输出解读:
[ASSURED]:双向通信正常 -> ✅ NAT 生效
[UNREPLIED]:只有请求无回包 -> ❌ 检查 FORWARD 或路由
无记录:包没到 B 或没出 B -> ❌ 检查 FORWARD 是否放行
四、B 侧 NAT 转发配置(OpenWrt 22.03)
与 CentOS 7 只有单一的 FORWARD 链不同,OpenWrt 引入了 Zone(区域)的概念来隔离不同网络接口的流量。FORWARD 链默认只放行 lan 区域到 wan 区域的流量,其他接口(如新创建的 GRE 隧道)不属于任何区域,其转发流量会被默认策略拦截,因此需要自定义区域(如 gre_zone)并显式配置转发规则放行。
4.1 清理旧配置
方式一:手动清理
用 vi 手动删除 /etc/config/firewall 中的 gre_zone 相关配置段:
vi /etc/config/firewall在 vi 中操作:
按 / 输入 gre_zone 回车,搜索定位
按 dd 删除整行
继续按 n 搜索下一个,重复 dd 删除
直到所有 gre_zone、gre_masq、GRE_to_LAN、LAN_to_GRE、gre_include 相关内容全部删除
按 :wq 保存退出
# 重启防火墙
/etc/init.d/firewall restart方式二:sed 脚本清理(推荐)
cat > /usr/local/bin/clean-gre-forward << 'EOF'
#!/bin/sh
# 清理 GRE 转发配置
echo "=== 清理 GRE 转发配置 ==="
# 1. 删除 firewall.user 中的引用
sed -i '/gavin_gre_forward.sh/d' /etc/firewall.user
# 2. 删除主脚本
rm -f /usr/local/bin/gavin_gre_forward.sh
# 3. 通过 uci 删除所有 gre 相关配置
# 删除 gre_zone
for idx in $(uci show firewall | grep "\.name='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除引用 gre_zone 的 rule
for idx in $(uci show firewall | grep "\.src='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除引用 gre_zone 的 nat
for idx in $(uci show firewall | grep "\.src='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除 gre_include
for idx in $(uci show firewall | grep "\.name='gre_include'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 4. 删除日志
rm -f /tmp/gavin_gre_forward.log
# 5. 重启防火墙
/etc/init.d/firewall restart
echo "清理完成"
EOF
chmod +x /usr/local/bin/clean-gre-forward4.2 隧道转发配置
以下为分步配置,如需一键安装请直接跳至 4.4。
UCI 配置 - 自定义 GRE zone、转发、NAT:
# 1 确认清理干净
grep gre_zone /etc/config/firewall
# 应该没有输出
# 2. 重新创建 gre_zone
uci add firewall zone
uci set firewall.@zone[-1].name='gre_zone'
uci set firewall.@zone[-1].input='ACCEPT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].forward='ACCEPT'
uci add_list firewall.@zone[-1].network='gre-4g-main'
uci add_list firewall.@zone[-1].network='gre-4g-backup'
uci add_list firewall.@zone[-1].network='gre-5g-main'
uci add_list firewall.@zone[-1].network='gre-5g-backup'
# 3. 允许 GRE 到 LAN 转发(用 rule,保留扩展性)
uci add firewall rule
uci set firewall.@rule[-1].name='GRE_to_LAN'
uci set firewall.@rule[-1].src='gre_zone'
uci set firewall.@rule[-1].dest='lan'
uci set firewall.@rule[-1].target='ACCEPT'
# 4. 允许 LAN 到 GRE 转发(回包方向)
uci add firewall rule
uci set firewall.@rule[-1].name='LAN_to_GRE'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='gre_zone'
uci set firewall.@rule[-1].target='ACCEPT'
# 5. NAT 规则(MASQUERADE)
uci add firewall nat
uci set firewall.@nat[-1].name='gre_masq'
uci set firewall.@nat[-1].src='gre_zone'
uci set firewall.@nat[-1].target='MASQUERADE'
# 6. include 自定义规则(让 fw4 在 reload 时执行 /etc/firewall.user)
uci add firewall include
uci set firewall.@include[-1].name='gre_include'
uci set firewall.@include[-1].path='/etc/firewall.user'
uci set firewall.@include[-1].enabled='1'
uci set firewall.@include[-1].fw4_compatible='1'
# 7. 提交并重启
uci commit firewall
/etc/init.d/firewall restartnftables 放行规则 - 放行自定义 zone:
cat > /usr/local/bin/gavin_gre_forward.sh << 'EOF'
#!/bin/sh
# GRE 隧道转发放行规则(带重复检测)
# 检测规则是否已存在
if nft list chain inet fw4 forward 2>/dev/null | grep -q "gre-4g-main"; then
# 规则已存在,跳过
exit 0
fi
# 放行回包
# ip saddr 规则负责放行来程数据包,ct state related 规则负责确保 conntrack 表项完整,两者共同保证回程包能被全局 established 规则正确放行
nft insert rule inet fw4 forward iifname "gre-4g-main" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-4g-backup" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-main" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-backup" ct state established,related accept 2>/dev/null
# 放行请求包
nft insert rule inet fw4 forward iifname "gre-4g-main" ip saddr 10.60.32.0/19 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-4g-backup" ip saddr 10.60.0.0/19 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-main" ip saddr 172.16.2.0/24 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-backup" ip saddr 172.16.2.0/24 accept 2>/dev/null
echo "GRE rules loaded at $(date)" >> /tmp/gavin_gre_forward.log
EOF
chmod +x /usr/local/bin/gavin_gre_forward.sh
在 firewall.user 中引用:
# 在 firewall.user 中引用
echo ". /usr/local/bin/gavin_gre_forward.sh" >> /etc/firewall.user
chmod +x /etc/firewall.user
# 手动执行一次
/etc/firewall.user4.3 验证配置
# 查看防火墙配置
uci show firewall | grep gre
# 查看转发规则
nft list chain inet fw4 forward | grep gre
# 查看日志
cat /tmp/gavin_gre_forward.log4.4 完整脚本配置
cat > /usr/local/bin/gre-forward << 'EOF'
#!/bin/sh
# GRE 隧道转发管理脚本
# 用法: gre-forward install | remove
usage() {
echo "用法: $0 install | remove"
echo " install - 安装 GRE 转发配置"
echo " remove - 清理 GRE 转发配置"
exit 1
}
# 清理函数:删除所有 gre 相关 UCI 配置
clean_uci() {
# 删除 gre_zone
for idx in $(uci show firewall 2>/dev/null | grep "\.name='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除 src='gre_zone' 的 rule
for idx in $(uci show firewall 2>/dev/null | grep "\.src='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除 dest='gre_zone' 的 rule
for idx in $(uci show firewall 2>/dev/null | grep "\.dest='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除 src='gre_zone' 的 nat
for idx in $(uci show firewall 2>/dev/null | grep "\.src='gre_zone'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
# 删除 gre_include
for idx in $(uci show firewall 2>/dev/null | grep "\.name='gre_include'" | cut -d. -f2 | cut -d= -f1); do
uci delete firewall.$idx
done
uci commit firewall
}
case "$1" in
install)
echo "=== 安装 GRE 转发配置 ==="
# 1. 清理旧配置
clean_uci
# 2. 删除 firewall.user 中的引用
sed -i '/gavin_gre_forward.sh/d' /etc/firewall.user 2>/dev/null
# 3. 创建 GRE 规则脚本(带重复检测)
cat > /usr/local/bin/gavin_gre_forward.sh << 'INNER_EOF'
#!/bin/sh
# GRE 隧道转发放行规则(带重复检测)
if nft list chain inet fw4 forward 2>/dev/null | grep -q "gre-4g-main"; then
exit 0
fi
nft insert rule inet fw4 forward iifname "gre-4g-main" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-4g-backup" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-main" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-backup" ct state established,related accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-4g-main" ip saddr 10.60.32.0/19 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-4g-backup" ip saddr 10.60.0.0/19 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-main" ip saddr 172.16.2.0/24 accept 2>/dev/null
nft insert rule inet fw4 forward iifname "gre-5g-backup" ip saddr 172.16.2.0/24 accept 2>/dev/null
echo "GRE rules loaded at $(date)" >> /tmp/gavin_gre_forward.log
INNER_EOF
chmod +x /usr/local/bin/gavin_gre_forward.sh
# 4. 在 firewall.user 中引用
echo ". /usr/local/bin/gavin_gre_forward.sh" >> /etc/firewall.user
# 5. 创建 UCI 配置
uci add firewall zone
uci set firewall.@zone[-1].name='gre_zone'
uci set firewall.@zone[-1].input='ACCEPT'
uci set firewall.@zone[-1].output='ACCEPT'
uci set firewall.@zone[-1].forward='ACCEPT'
uci add_list firewall.@zone[-1].network='gre-4g-main'
uci add_list firewall.@zone[-1].network='gre-4g-backup'
uci add_list firewall.@zone[-1].network='gre-5g-main'
uci add_list firewall.@zone[-1].network='gre-5g-backup'
uci add firewall rule
uci set firewall.@rule[-1].name='GRE_to_LAN'
uci set firewall.@rule[-1].src='gre_zone'
uci set firewall.@rule[-1].dest='lan'
uci set firewall.@rule[-1].target='ACCEPT'
uci add firewall rule
uci set firewall.@rule[-1].name='LAN_to_GRE'
uci set firewall.@rule[-1].src='lan'
uci set firewall.@rule[-1].dest='gre_zone'
uci set firewall.@rule[-1].target='ACCEPT'
uci add firewall nat
uci set firewall.@nat[-1].name='gre_masq'
uci set firewall.@nat[-1].src='gre_zone'
uci set firewall.@nat[-1].target='MASQUERADE'
uci add firewall include
uci set firewall.@include[-1].name='gre_include'
uci set firewall.@include[-1].path='/etc/firewall.user'
uci set firewall.@include[-1].enabled='1'
uci set firewall.@include[-1].fw4_compatible='1'
uci commit firewall
# 6. 重启防火墙
echo "重启防火墙..."
/etc/init.d/firewall restart
# 7. 验证
echo ""
echo "=== 安装完成 ==="
sleep 2
nft list chain inet fw4 forward 2>/dev/null | grep -E "gre-4g|gre-5g" || echo "未找到 GRE 规则,请检查"
;;
remove)
echo "=== 清理 GRE 转发配置 ==="
# 1. 清理 UCI 配置
clean_uci
# 2. 删除 firewall.user 中的引用
sed -i '/gavin_gre_forward.sh/d' /etc/firewall.user
# 3. 删除主脚本
rm -f /usr/local/bin/gavin_gre_forward.sh
# 4. 删除日志
rm -f /tmp/gavin_gre_forward.log
# 5. 重启防火墙
/etc/init.d/firewall restart
echo "=== 清理完成 ==="
echo "管理脚本 /usr/local/bin/gre-forward 已保留,可手动删除"
;;
*)
usage
;;
esac
EOF
chmod +x /usr/local/bin/gre-forward五、转发白名单(定向,OpenWrt)
5.1 限制规则配置(未验证)
使用 nft 命令(临时生效,重启防火墙失效):
# 一、只允许 10.250.0.10 访问 221.226.20.74
nft insert rule inet fw4 forward iifname "gre-*" oifname "eth1" ip saddr 10.250.0.10 ip daddr 221.226.20.74 accept
# 拒绝 10.250.0.10 访问其他任何 IP
nft add rule inet fw4 forward iifname "gre-*" oifname "eth1" ip saddr 10.250.0.10 drop
# 二、允许所有源IP 访问 221.226.20.74
nft insert rule inet fw4 forward iifname "gre-*" oifname "eth1" ip daddr 221.226.20.74 accept
# 拒绝所有源IP 访问其他任何IP
nft add rule inet fw4 forward iifname "gre-*" oifname "eth1" drop
# 简化:
nft insert rule inet fw4 forward ip daddr 221.226.20.74 accept
nft add rule inet fw4 forward drop
5.2 解除限制配置(未验证)
删除 nft 规则(临时):
# 一、查看所有规则及其句柄
nft -a list chain inet fw4 forward
# 输出示例:
# chain forward {
# type filter hook forward priority filter; policy drop;
# ct state established,related accept # handle 10
# iifname "gre-*" oifname "eth1" ip daddr 221.226.20.74 accept # handle 11
# iifname "gre-*" oifname "eth1" drop # handle 12
# }
# 二、根据句柄删除
nft delete rule inet fw4 forward handle 10
nft delete rule inet fw4 forward handle 11
nft delete rule inet fw4 forward handle 12六、排障专题
通用排障方法(FORWARD 放行、rp_filter、云环境特殊配置等)请参考《Linux 策略路由与 NAT 转发指南》排障章节。