在云上做多可用区(Availability Zone)架构时,经常会遇到这样的需求:两个不同可用区的 VPC 内网之间需要互通,但又不想把内网直接暴露到公网。GRE 隧道就是解决这类问题最轻量的方案之一。
GRE(Generic Routing Encapsulation,通用路由封装)本质上是一种 IP-over-IP 隧道协议:它把一种网络层协议的数据报整个封装进另一种网络层协议里,让原本无法直接路由的数据包可以在 IPv4/IPv6 网络中穿行。隧道的两端各自负责「加封装」和「解封装」,对中间的承载网络来说,看到的只是一对普通的点对点连接。
一、GRE 解决了什么问题
不同可用区、不同地域之间的内网,在逻辑上像是彼此隔离的孤岛。常规做法要么走公网(慢、不安全、容易被劫持),要么拉专线(贵)。
用 GRE 隧道可以在两端的公网地址之间打一条虚拟的点对点通路,让两侧的私网网段像在同一个局域网里一样互相访问,而且全程只走一条封装后的链路。
典型适用场景
- 跨可用区 / 跨地域的 VPC 内网互通
- 把分散在不同机房的私网段连成一张大内网
- 作为 IPsec、BGP 等更复杂方案的底层承载(GRE over IPsec)
- 网络实验与拓扑教学
实验目标
本次实验的拓扑如下:A 区有一台 A1 作为隧道端点,其内网有一台 A2;B 区有一台 B1 作为隧道端点,其内网有一台 B2。
要求实现:A2 与 B2、A2 与 B1 之间的通信全部经由 GRE 隧道,而不是走各自的内网直连。
完成后可以在 B1 的网卡上用 tcpdump 抓到隧道流量,验证流量确实走了隧道。
二、开始搭建
2.1 确认 ip_gre 内核模块
Linux 上创建 GRE 隧道依赖 ip_gre 内核模块,它是 GRE over IPv4 的驱动。先加载并确认:
modprobe ip_gre
lsmod | grep ip_gre
2.2 两端都开启路由转发
隧道要转发「不属于本机」的流量,因此端点必须开启内核转发:
vim /etc/sysctl.conf
# 加入或确认这一行
net.ipv4.ip_forward = 1
# 生效
sysctl -p
# 确认
cat /etc/sysctl.conf | grep ip_forward
A1 和 B1 两端都要做这一步。
2.3 建立隧道
约定 A1 内网地址为 192.168.1.10,B1 内网地址为 192.168.2.10。先在 A1 上执行:
# tun1 为隧道名;local 是本机地址,remote 是对端地址
# 执行前请先确认两端可以互通
ip tunnel add tun1 mode gre remote 192.168.2.10 local 192.168.1.10 ttl 255
# 启用隧道接口
ip link set tun1 up
# 给隧道两端分配一个私有网段内的地址(本端 .1,对端 .2)
ip addr add 10.10.10.1 peer 10.10.10.2 dev tun1
# 查看结果
ip addr show
然后在 B1 上做对称配置:
ip tunnel add tun1 mode gre remote 192.168.1.10 local 192.168.2.10 ttl 255
ip link set tun1 up
ip addr add 10.10.10.2 peer 10.10.10.1 dev tun1
2.4 验证隧道连通性
- A1 侧隧道地址:
10.10.10.1 - B1 侧隧道地址:
10.10.10.2
# 在 B1 上抓隧道接口的包
tcpdump -i tun1
# 另一端 ping 隧道对端地址验证
ping 10.10.10.2 # 在 A1 上执行
三、配置路由与 NAT 转发
3.1 让两个内网段互相可达
隧道通了不等于内网通。还需要告诉各自的机器「去对端网段要走隧道」,并把跨网段的流量做 NAT。
在 A1 上:
# 去往 B 区内网段的路由走隧道
ip route add 192.168.2.0/24 dev tun1 via 10.10.10.2
# 把从隧道过来的 10.10.10.2 的源地址 NAT 成本机出口地址
iptables -t nat -A POSTROUTING -o eth0 -s 10.10.10.2 -j MASQUERADE
iptables -A FORWARD -s 10.10.10.2 -o eth0 -j ACCEPT
# 把 B 区内网段过来的包 NAT 成隧道地址
iptables -t nat -A POSTROUTING -o tun1 -s 192.168.2.0/24 -j MASQUERADE
iptables -A FORWARD -s 192.168.2.0/24 -o tun1 -j ACCEPT
# 查看规则
iptables -t nat -L -n
iptables -L -n
在 B1 上做对称配置:
ip route add 192.168.1.0/24 dev tun1 via 10.10.10.1
iptables -t nat -A POSTROUTING -o eth0 -s 10.10.10.1 -j MASQUERADE
iptables -A FORWARD -s 10.10.10.1 -o eth0 -j ACCEPT
iptables -t nat -A POSTROUTING -o tun1 -s 192.168.1.0/24 -j MASQUERADE
iptables -A FORWARD -s 192.168.1.0/24 -o tun1 -j ACCEPT
保存规则并设置开机自启:
# 持久化规则
netfilter-persistent save # Debian/Ubuntu
# 或
service iptables save # CentOS 系
# 开机自启
systemctl enable netfilter-persistent # Debian/Ubuntu
systemctl enable iptables # CentOS 系
3.2 让内网主机找到网关
最后一步:告诉 A2「去 B 区内网要经过 A1」,告诉 B2「去 A 区内网要经过 B1」。
# 在 A2 上
ip route add 192.168.2.0/24 via 192.168.1.10
# 在 B2 上(方向相反)
ip route add 192.168.1.0/24 via 192.168.2.10
做完这一步,A2 就能 ping 通 B2,而且中间确实经过隧道。
3.3 拆除隧道
ip link set tun1 down
ip tunnel del tun1
四、两个常见的坑
4.1 MTU / MSS 问题
GRE 会把一个数据包再封装一层,封装后的大小会超过原接口的 MTU。典型表现是:小包能通,ping 得通,但一传大文件、一开网页就卡死。
解决办法是在隧道接口上调整 MSS:
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN
-j TCPMSS --clamp-mss-to-pmtu
# 或手动指定
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN
-j TCPMSS --set-mss 1436
4.2 隧道没有状态检测
GRE 本身是无状态的:一端已经挂了,另一端可能还傻傻地以为隧道是通的,流量发出去就石沉大海。
解决方式是启用 keepalive,让两端定时互发探测包,超时未收到就把隧道接口置为 down:
ip tunnel change tun1 mode gre remote 192.168.2.10 local 192.168.1.10
ttl 255 keepalive 10 3
五、关于安全性
GRE 本身不做任何加密,封装后的流量在公网上是明文的,抓包就能看到内容。
如果涉及敏感数据,有两个选择:
- 在隧道上用
ip tunnel ... key设一个密钥(本质是明文口令,只防误配不防抓包) - 更靠谱的是 GRE over IPsec,用 IPsec 给 GRE 流量加密,但配置复杂度和运维成本都会上去

Comments NOTHING