跳转到主要内容

GRE 隧道介绍与在 Linux 上搭建

GRE (Generic Routing Encapsulation) 通用路由封装协议,用于将使用一个路由协议的数据包封装在另一协议的数据包中。“封装”是指将一个数据包包装在另一个数据包中,就像将一个盒子放在另一个盒子中一样。GRE 是在网络上建立直接点对点连接的一种方法,目的是简化单独网络之间的连接。它适用于各种网络层协议。由 RFC 2784 定义并在 RFC 2890 中更新。

由于 GRE 是一个网络层协议,它不需要端口(传输层协议),但需要两端有直接的 IP 访问,如公网或同一个局域网。


介绍

GRE 的使用如何影响 MTU 和 MSS ?

MTU 和 MSS 是用来限制通过网络传输的数据包最大长度的度量单位,就像对过桥车辆限重一样。
MTU 测量数据包的总大小,包括标头;MSS仅测量有效负载。
超过 MTU 值的数据包将被分成几段或分解成较小的数据包,使之适合在网络上传输。

像任何协议一样,使用 GRE 会在数据包原有大小基础上增加几个字节。在数据包的 MSS 和 MTU 设置中必须考虑这个因素。如果 MTU 设为 1500 字节,MSS 设为 1460 字节(考虑到必要的 IP 和 TCP 标头大小),则增加 GRE 24字节标头将导致数据包超过 MTU 限值:

  • 新 IP 标头[20 字节] + GRE 标头[4-16字节] + 20 字节 [IP 标头] + 20 字节 [TCP 标头] + 1460 字节 [有效负载] = 1524字节

因此,数据包将被分段。分段会减慢数据包传递,并增加算力开销,因为超出 MTU 的数据包必须分解然后重新组合。

通过减少 MSS 长度以包含 GRE 标头,可以避免这种情况。如果将 MSS 设置为 1436 而不是 1460,那么,GRE 标头的问题就得到了解决,并且数据包不会超过 MTU 值 1500:

  • 新 IP 标头[20 字节] + GRE 标头[4-16字节] + 20 字节 [IP 标头] + 20 字节 [TCP 标头] + 1436 字节 [有效负载] = 1500 字节

尽管避免了分段,但结果却是有效载荷变小,这意味着需要额外的数据包来传递数据。例如,如果目标是传递 150000 字节的内容(或大约 150 kB),假设 MTU 设置为 1500,且未使用其他 3 层协议,那么,比较一下使用 GRE 和不使用 GRE 时分别需要多少个数据包:

  • 不使用 GRE,MSS 为 1460: 103 个数据包
  • 使用 GRE,MSS 为 1436: 105 个数据包

额外的两个数据包会增加毫秒级的数据传输延迟。但是,在配置良好的情况下使用 GRE 比不使用 GRE 可以使这些数据包选择更快的网络路径,进而可以弥补损失的时间。

封装过程
  • IP 标头[20 字节] + 有效负载                                         网络层
  • GRE 标头[4-16字节] + IP 标头[20 字节] + 有效负载                         GRE 处理
  • 新 IP 标头[20 字节] + GRE 标头[4-16字节] + IP 标头[20 字节] + 有效负载         网络层

注:也可能不是 IP 而是其他网络层协议。

Keepalive

Keepalive 用于检测隧道对方是否可达。

GRE 隧道本方会定期(默认值为 5 秒,deadtime 为 15 秒)向对方发送 Keepalive 探测报文。
若对端可达,则本端会收到对端的回应报文;若对端不可达,则收不到对端的回应报文。

如果本方在计数器值达到预先设置的值之前收到回应报文,则表明对方可达。
如果计数器值达到预先设置的重试次数,本方还是没有收到回应报文,则认为对方不可达。此时,本方将关闭隧道连接。

加密

GRE 传输中未经过加密,也不支持加密。

考虑使用 GRE 传输加密过的数据,如 HTTPS。

亦或是配和 IPSec 使用。

TUN 和 TAP 设备

TUN 和 TAP 设备允许用户空间程序模拟网络设备。
两者的区别在于 TAP 设备使用以太网帧(2 层设备),而 TUN 使用 IP 数据包(3 层设备)。

有两种类型的 TUN/TAP 设备:持久性和瞬态性。
瞬态 TUN/TAP 设备由用户空间程序在打开特殊设备时创建,并在相关文件描述符关闭时自动销毁。
持久性设备由管理员创建,例如使用 ip 命令。

GRE 和 GRETAP

静态 GRE 隧道传统上用于封装 IPv4 或 IPv6 数据包,但 RFC2784 并未将 GRE 负载限制为 3 层协议数据包。所以可以封装任何东西,包括以太网帧(2 层)。

因此在 Linux 中,gre 专门指 3 层设备,而能够传输 2 层数据的 GRE 称为 gretap。

在 Linux 中创建 GRE 隧道

Linux 目前直接支持 IPIP(IPv4 中的 IPv4)、SIT(IPv4 中的 IPv6)、IP6IP6(IPv6 中的 IPv6)、IPIP6(IPv6 中的 IPv4)、GRE(几乎任何东西中的任何东西)、GRETAP(可以传输 2 层协议的 GRE)、VTI(IPsec 中的 IPv4) 这几种隧道。

创建隧道一般使用 iproute2,但也可以使用 nmcli (NetworkManager)。

下面使用 iproute2 进行演示

以下示例按照 CC BY-SA 4.0 协议,参考、翻译自 https://baturin.org/docs/iproute2/ 有修改 ,作者 Daniil Baturin 和其他贡献者。

注: gre0 为系统保留名称,自定义接口名不可设置为 gre0。

检测内核模块
lsmod | grep gre

如果没有,则使用下面的命令加载模块:

modprobe ip_gre
 
防火墙 

如果你使用 Firewalld,请使用如下命令:

firewall-cmd [--zone=] [--permanent] --add-rich-rule="rule protocol value="gre" accept"

或者

firewall-cmd [--zone=] [--permanent] --add-rich-rule='rule protocol value="47" accept'

GRE 在 Firewalld 中的协议号为 47。

GRETAP

即:添加一个名为<自定义隧道设备名>类型为 gretap 的接口,设置远程地址为<远程地址>本地地址为<本地地址>。

如果要传输 IPv6,则设备类型为 ip6gretap。

 
普通 GRE 点对点隧道
ip tunnel add <自定义隧道设备名> mode gre remote <远程地址> local <本地地址>

即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,设置远程地址为<远程地址>本地地址为<本地地址>。

GRE 可以同时封装IPv4 和IPv6。但是,默认情况下,它使用 IPv4 进行传输。
如果要使用 IPv6 传输,则设备类型为 ip6gre。

即:启动刚刚设置的<自定义隧道设备名>设备。

ip address add <自定义设备 IP 地址> dev <自定义接口名>

即:给刚刚设置的<自定义隧道设备名>设备,添加 IP 地址,可以使用 CIDR 表示法。
由于一般都是点对点隧道,所以使用有 2 个 IP 地址的子网就够了,如 10.10.10.0/30  可用 IP 为 10.10.10.1 和 10.10.10.2。

特别注意:在上面的例子中不能填写 10.10.10.0/30 而是 10.10.10.1/30 和 10.10.10.2/30。


然后你需要在对方设备上也进行同样的操作,但是交换远程地址和本地地址。

 
一对多的 GRE 隧道
ip tunnel add <自定义隧道设备名> mode gre local <本地地址> remote <远程地址> key <标识符>

即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,本地地址为<本地地址>远程地址为<远程地址>,标识符为<标识符>。

使用不同的隧道设备名和标识符创建多个隧道,可以让多个对方连接。

多对多的 GRE 隧道
ip tunnel add <自定义隧道设备名> mode gre local <本地地址> key <标识符>

即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,本地地址为<本地地址>,标识符为<标识符>。

不指定远程地址。这里的 key 与加密无关,它只是一个标识符,用来区分不同的隧道。

即:启动刚刚设置的<自定义隧道设备名>设备。

ip address add <自定义设备 IP 地址> dev <自定义隧道设备名>

即:给刚刚设置的<自定义隧道接口名>设备,添加 IP 地址,可以使用 CIDR 表示法。
由于是一对多隧道,所以需要有多个 IP 地址的子网,如 10.10.10.0/29  可用 IP 为 10.10.10.1 - 10.10.10.6。

特别注意:在上面的例子中不能填写 10.10.10.0/30 而是 10.10.10.1/30 到 10.10.10.6/30。

由于没有明确的远程地址,所以仅仅创建隧道是不够的。系统需要知道其他端点的位置。
一般使用 NHRP (Next Hop Resolution Protocol)  下一跳解析协议来获取路由。

但也可以手动添加对等点:

假设远程端点的 GRE 接口地址 为 10.10.10.2,公共网络接口地址为 9.9.9.9。

ip neighbor add 10.10.10.2 lladdr 9.9.9.9 dev <自定义隧道设备名>

然后你需要在对方设备上也进行同样的操作,但是更换地址。
若执行此命令提示 RTNETLINK answers: File exists,则可以将 add 命令更换为 replace,如:

ip neighbor replace 10.10.10.2 lladdr 9.9.9.9 dev <自定义隧道设备名>

使用 ip routeip neighbornetstat -rt 查看路由信息。

多对多的 GRE 隧道例子
机器 公网 IP GRE 设备 IP
159.1.1.1
10.10.10.1/29
B 134.1.1.1 10.10.10.2/29
C 178.1.1.1 10.10.10.3/29

这是一个实际的例子,但实际公网 IP 仅保留开头。

机器 A 执行:

ip tunnel add gre1 mode gre  local 159.1.1.1 key 111
ip link set gre1 up
ip a a 10.10.10.1/29 dev gre1
ip neighbor add 10.10.10.3 lladdr 178.1.1.1 dev gre1
ip neighbor replace 10.10.10.2 lladdr 134.1.1.1 dev gre1

机器 B 执行:

ip tunnel add gre1 mode gre  local 134.1.1.1 key 111
ip link set gre1 up
ip a a 10.10.10.2/29 dev gre1
ip neighbor add 10.10.10.1 lladdr 159.1.1.1 dev gre1
ip neighbor replace 10.10.10.3 lladdr 178.1.1.1 dev gre1

机器 C 执行:

ip tunnel add gre1 mode gre  local 178.1.1.1 key 111
ip link set gre1 up
ip a a 10.10.10.3/29 dev gre1
ip neighbor add 10.10.10.1 lladdr 159.1.1.1 dev gre1
ip neighbor replace 10.10.10.2 lladdr 134.1.1.1 dev gre1

结果:任意两台机器均可通过 gre1 接口 IP 通信。

删除隧道
ip tunnel del <自定义隧道设备名>

请注意,在较旧的 iproute2 版本中,此命令不支持完整 delete 语法(ip tunnel delete),仅支持 del。
最近的版本允许完整形式和缩写形式(在 iproute2-ss131122 中测试)。


修改隧道
ip tunnel change <自定义隧道名> <选项>

例子:ip tunnel change gre55 remote 9.9.9.9

注意:显然,您不能将 key 添加到以前没有设置 key 的隧道中。不确定这是错误还是功能。
此外,由于显而易见的原因,您不能即时更改隧道模式。

查看隧道信息
ip tunnel show [自定义隧道名]

其他

子网计算器

主要参考