Administrator
发布于 2026-07-20 / 35 阅读
0
0

GRE 隧道转发与抓包分析

本文基于《Linux 策略路由与 NAT 转发指南》的通用技术,聚焦于 GRE 隧道场景。

一、背景

1.1 基本参数

参数

服务器 A(隧道起点)

服务器 B(隧道终点,本文操作对象)

内网 IP

172.18.0.6(172.18.0.0/20)

192.168.0.8(192.168.0.0/20)

对等 IP(隧道)

172.16.0.1

172.16.0.2

公网 IP

1.2.3.4

5.6.7.8

GRE 隧道名称

tun1

tun1

1.2 前置条件

  • ✅ GRE 隧道已建立(参考《GRE 隧道配置》)

  • ✅ A 侧路由已配置(指定哪些流量走隧道)

1.3 本文目标

A 的流量到达 B 后,B 需要根据目标地址做出不同处理:

目标

实现方式

操作节点

A 访问 B 所在 VPC 内网

FORWARD 放行

内网机器直接响应

A 通过 B 访问公网

FORWARD 放行 + MASQUERADE

需要做 SNAT 才能上网

二、B 侧 NAT 转发配置(CentOS / 通用 Linux)

以下命令均已在通用指南中详细解释,此处只给具体参数。

放行 FORWARD 回包:

# 查看 FORWARD 当前默认策略
iptables -L FORWARD | head -1

# 若为 Chain FORWARD (policy DROP), 需显式放行回包
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

2.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:80

2.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.com

2.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-forward

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

nftables 放行规则 - 放行自定义 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.user

4.3 验证配置

# 查看防火墙配置
uci show firewall | grep gre

# 查看转发规则
nft list chain inet fw4 forward | grep gre

# 查看日志
cat /tmp/gavin_gre_forward.log

4.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 转发指南》排障章节。


评论