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)。
- 使用 nmcli 创建 GRE 隧道可以参考此文档:https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/8/html/configuring_and_managing_networking/configuring-a-gre-tunnel-using-nmcli-to-encapsulate-layer-3-traffic-in-ipv4-packets_configuring-ip-tunnels
下面使用 iproute2 进行演示。
以下示例按照 CC BY-SA 4.0 协议,参考、翻译自 https://baturin.org/docs/iproute2/ 有修改 ,作者 Daniil Baturin 和其他贡献者。
注: gre0 为系统保留名称,自定义接口名不可设置为 gre0。
特别注意:使用 iproute2 添加的接口会在重启后消失,如果你想要持久化设置请使用 nmcli,或直接编写 NetworkManager 配置文件,不过你也可以把命令写到 rc.local 中让其开机自动执行。
检测内核模块
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
ip link add <自定义隧道设备名> type gretap remote <远程地址> local <本地地址>
本地地址可以稍后指定,远程地址即隧道对方地址。
即:添加一个名为<自定义隧道设备名>类型为 gretap 的接口,设置远程地址为<远程地址>本地地址为<本地地址>。
如果要传输 IPv6,则设备类型为 ip6gretap。
普通 GRE 点对点隧道
ip tunnel add <自定义隧道设备名> mode gre remote <远程地址> local <本地地址>
即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,设置远程地址为<远程地址>本地地址为<本地地址>。
GRE 可以同时封装 IPv4 和 IPv6。但是,默认情况下,它使用 IPv4 进行传输。
如果要使用 IPv6 传输,则设备类型为 ip6gre。
ip link set dev <自定义隧道设备名> up
即:启动刚刚设置的<自定义隧道设备名>设备。
ip address add <自定义设备 IP 地址> pree <对端自定义设备 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 点对点隧道示例
A B 机器内网互通,下面设置完后隧道即可互通,可以通过 [root@B ~]# ping 10.10.10.2
验证。
[root@A ~]# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:1b:77:8e brd ff:ff:ff:ff:ff:ff
inet 172.31.34.149/23 brd 172.31.35.255 scope global dynamic eth0
valid_lft 315109016sec preferred_lft 315109016sec
inet6 fe80::216:3eff:fe1b:778e/64 scope link
valid_lft forever preferred_lft forever
[root@A ~]# ip tunnel add gre_hk mode gre remote 172.31.37.44 local 172.31.34.149
[root@A ~]# ip link set dev gre_hk up
[root@A ~]# ip address add 10.10.10.1/30 peer 10.10.10.2/30 dev gre_hk
[root@B ~]# ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:16:3e:00:ef:2f brd ff:ff:ff:ff:ff:ff
inet 172.31.37.44/23 brd 172.31.37.255 scope global dynamic eth0
valid_lft 315112679sec preferred_lft 315112679sec
inet6 fe80::216:3eff:fe00:ef2f/64 scope link
valid_lft forever preferred_lft forever
[root@B ~]# ip tunnel add gre_hk mode gre remote 172.31.34.149 local 172.31.37.44
[root@B ~]# ip link set dev gre_hk up
[root@B ~]# ip address add 10.10.10.2/30 peer 10.10.10.1/30 dev gre_hk
但如果想在 A 设备上通过 gre 隧道访问内容,例如: [root@A~]# ping -I gre_hk 8.8.4.4
还需要在 B 设备上启用 IP 转发,并为来自 10.10.10.0/30 并发往 eth0(默认出口)的流量启用 MASQUERADE (SNAT) 。
iptables 示例:
iptables -t nat -A POSTROUTING -s 10.10.10.0/30 -o eth0 -j MASQUERADE
firewalld 示例(无法指定来源地址和出口):
firewall-cmd --zone=public --add-masquerade --permanent
通过 firewalld 操作 iptables 示例:
firewall-cmd --direct --permanent --add-rule ipv4 nat POSTROUTING 0 -s 10.10.10.0/30 -o eth0 -j MASQUERADE
值得注意的是,使用 iproute2 添加的接口会在重启后消失。
如果你想持久化,可以将命令写到 rc.local 中,或是编写 network-scripts。
例如:/etc/sysconfig/network-scripts/ifcfg-gre_hk
DEVICE=gre_hk
TYPE=GRE
BOOTPROTO=none
ONBOOT=yes
PEER_OUTER_IPADDR=172.31.37.44
PEER_INNER_IPADDR=10.10.10.2/30
MY_OUTER_IPADDR=172.31.34.149
MY_INNER_IPADDR=10.10.10.1/30
别忘了需要开启内存参数允许转发 IP
net.ipv4.ip_forward = 1
比如写在 /etc/sysctl.conf
内,然后执行 sysctl -p
。
一对多的 GRE 隧道
ip tunnel add <自定义隧道设备名> mode gre local <本地地址> remote <远程地址> key <标识符>
即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,本地地址为<本地地址>远程地址为<远程地址>,标识符为<标识符>。
使用不同的隧道设备名和标识符创建多个隧道,可以让多个对方连接。
多对多的 GRE 隧道
ip tunnel add <自定义隧道设备名> mode gre local <本地地址> key <标识符>
即:添加一个名为<自定义隧道设备名>类型为 gre 的接口,本地地址为<本地地址>,标识符为<标识符>。
不指定远程地址。这里的 key 与加密无关,它只是一个标识符,用来区分不同的隧道。
ip link set dev <自定义隧道接口名> up
即:启动刚刚设置的<自定义隧道设备名>设备。
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 route
和 ip neighbor
和 netstat -rt
查看路由信息。
多对多的 GRE 隧道例子
机器 | 公网 IP | GRE 设备 IP |
A | 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 address add 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 address add 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 address add 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 [自定义隧道名]
其他
子网计算器
- https://www.vultr.com/resources/subnet-calculator/
- https://www.davidc.net/sites/default/subnets/subnets.html
No Comments