技术 · 2021年3月29日 0 203  

GRE隧道的套娃尝试

关于Olink GRE隧道的套娃尝试

之前的一篇文章提到了自建GRE隧道修改服务器的回程的操作,看上去这个技术很不错,但是实际操作起来很有局限性,因为99%以上的服务器都是无法伪造ip的,线路过得去的就更加没有了(gia对于源ip还有白名单,即使机房允许你伪造ip,但是回程依旧走不了gia),所以真想用来优化线路还是建议直接购买现有的产品,不过Olink的隧道有一点局限性就是只能绑定一个ip,而我帮朋友买了好几台欧洲的服务器,回程线路太差需要优化,而他们网站基本也没啥流量,如果每个人都要单独买一个GRE就有点浪费了,所以我想着能否套娃一下,在我的服务器和Olink的转发服务器之间建立隧道,再用我的服务器通过隧道转发来自我朋友的服务器的数据包。

启用GRE隧道

为什么我会想到这一点呢,因为在之前自建隧道的时候我就注意到了一点,即用来转发的服务器即使忘了在POSTROUTING中添加SNAT规则,我们ping被转发的机器的ip依旧可以得到回应,但是从被转发的机器上主动ping国内的ip则得不到回应,因为很重要的一点 NAT表里的规则仅针对一条连接的第一个数据包有效。也就是说,被转发的服务器如果是为响应连接而发出的包,源地址一直就是被转发的机器的ip,并不会受到NAT的影响,而主动连接的时候,我们传进隧道的包的源ip是被转发的服务器上的隧道的ip,如10.0.0.2,必须要做SNAT改成被转发的服务器的公网ip,连接才能建立。

Olink的脚本中创建GRE隧道时没有使用ip addr为隧道分配ip,也就是说隧道另一边收到的数据包的源ip就是我们往里面传的数据包的源ip,所以我们只需要修改传进去的包的ip就能让多台服务器用上隧道了。

那么我们先在Contabo的机器上测试一下GRE的效果吧,图一是启用隧道前的ping,图二是启用隧道后的,不过是在下午进行的测试,所以原始线路丢包也不严重。

启用隧道前

套上GRE后,从我家ping过去的延迟降低了20ms~30ms(到了晚高峰的时候这个差异应该会更加明显)

使用GRE后

我们先尝试一下是否可以通过这个隧道发送其他源ip的数据包吧,为了便于抓包测试,我另外添加了一个国外的服务器的ip。

ip route add [目的服务器ip] dev tun_olink
iptables -t nat -A POSTROUTING --destination [目的服务器ip] -j SNAT --to-source 1.1.1.1
ping [目的服务器ip]
#同时在目的服务器上 tcpdump icmp
ip route del [目的服务器ip] dev tun_olink
iptables -t nat -vnL POSTROUTING
iptables -t nat -D POSTROUTING 1

看来我的方案确实可行。

开始套娃

接着我们便开始套娃了,我们需要再在Contabo和多台待优化的服务器间再建立一个一对多的GRE隧道,参考上一篇文章中一对多GRE隧道的建立方案。(另外我发现olink的gre隧道没有分配ip,直接用的链路层地址)

下列操作暂时只在两台机器上进行,之后想要加入新的机器,重复操作即可。

#两台服务器上都执行
ip tunnel add mgre0 mode gre key 0xfffffffe ttl 255
#机器1 用作中转的Contabo
sudo echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sudo sysctl -p
ip addr add 10.0.1.1/24 dev mgre0
#机器2 待优化的机器
ip addr add 10.0.1.2/24 dev mgre0
# 互相添加邻居
#机器1上
ip neigh add 10.0.1.2 lladdr [待优化的机器的ip] dev mgre0
#机器2上
ip neigh add 10.0.1.1 lladdr [用作中转的机器的ip] dev mgre0
#最后启用gre隧道
ip link set mgre0 up
#如果被转发的机器开了firewalld 得先关掉
systemctl stop firewalld

配置完成之后互相ping一下看看有没有问题。

之后就可以开始套娃了,套娃之前先截图一张便于对比。

套娃之前

我的思路是,发往大陆的数据包,先发送到mgre0隧道中到达我的Contabo机器,然后Contabo的机器对于来自于mgre0隧道的数据包,转发到olink-tun的隧道中。

如果要让主动连接也能通过隧道。。那可要采取类似于透明代理的方案了

#先在待优化的机器上操作
# 创建一张单独的表,看上去干净一点
echo '100 mgre' >> /etc/iproute2/rt_tables
ip rule add from all table mgre
# 先拿个ip测试一下
ip route add [国内机器的ip] via 10.0.1.1 table mgre
# 然后要在用于转发的机器上设置一下转发规则,将数据包修改源ip后转发到隧道中(主动连接大概不行)
#将mgre0隧道进来的数据转发到 tun_olink上
iptables -t filter -I FORWARD -i mgre0 -o tun_olink -j ACCEPT

(我不知道为什么server.it的机器上抓不到icmp的包,大概是受到了防火墙的影响,所以换了一台机器测试)

之后先尝试被动的建立连接,用一台国内的机器去(即上面添加的测试ip)ping这台待优化的机器,发现可以正常得到回应,再在contabo上面抓包,发现icmp确实是经过了contabo的中转,现在回程数据包的路径是。

被ping的justhost->用于中转的contabo->国内的机器。

另外拿iperf3测试了一下,速度要比原来直连justhost快很多,并且Contabo上面也看到了流量,说明套娃成功了。 第一张图是没套娃的时候测的,第二张图则是套娃后测的。

没套娃测试

下面这张图是套娃后测的。

套娃后测速

不过注意**到目前为止,套娃操作后,我们的服务器是没办法主动向 [国内机器的ip] 发起连接的,因为Olink gre隧道的另外一端POSTROUTING SNAT时用的是我们Contabo的ip。 **所以我们只需要让连接的第一个包不要经过隧道即可,在待优化的服务器上配置,使用iptables的state扩展,不要转发报文状态为NEW的报文。

上面这段文字是我一开始把问题复杂化了,我一开始以为在Olink的隧道上操作和我之前在自己建的隧道上操作一样,需要在隧道另外一端POSTROUTING时进行SNAT,将源ip(gre隧道的ip,如10.0.1.2)修改成对应服务器的公网ip,但是Olink的隧道是一对一的,并且没有分配ip地址,相当于数据包从隧道另一端出来时源ip就是我们待优化的服务器的公网ip,所以我们只需要在中间的Contabo服务器POSTROUTING时进行一次SNAT,将mgre隧道传过来的数据包的源ip由10.0.1.2这样的形式改成对应的服务器的公网ip即可。

iptables -t nat -I POSTROUTING -s 10.0.1.3 -j SNAT --to [待优化的服务器的公网ip]

之后我们便可以从我们的待优化服务器上向国内主动发起连接了,套娃成功。

汇总

最后汇总一下上面的操作吧,先用一台服务器作为中间层,创建隧道。 记得在POSTROUTING中设置SNAT

echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
ip tunnel add mgre0 mode gre key 0xfffffffe ttl 255
ip addr add 10.0.1.1/24 dev mgre0
#注意 10.0.1.2 对应的就是待优化的服务器
ip neigh add 10.0.1.2 lladdr [待优化的机器的ip] dev mgre0
ip link set mgre0 up
iptables -t filter -I FORWARD -i mgre0 -o tun_olink -j ACCEPT
iptables -t nat -I POSTROUTING -s 10.0.1.2 -j SNAT --to [待优化的服务器的公网ip]

在待优化的服务器下进行下面的操作:

#反向路由校验改为宽松模式
sysctl -w net.ipv4.conf.all.rp_filter=2
ip tunnel add mgre0 mode gre key 0xfffffffe ttl 255
ip addr add 10.0.1.2/24 dev mgre0
ip neigh add 10.0.1.1 lladdr [中间机器的ip] dev mgre0
ip link set mgre0 up
echo '100 mgre' >> /etc/iproute2/rt_tables
ip rule add from all table mgre

然后使用上一篇文章提到的py脚本,生成批量命令脚本

# -*- coding: utf-8 -*-
import requests
url = 'https://raw.githubusercontent.com/metowolf/iplist/master/data/special/china.txt'
r = requests.get(url, allow_redirects=True)
open('ips.txt', 'wb').write(r.content)
# 之后生成添加路由表命令与卸载路由表命令
start = open('startCMD.sh', 'w')
end = open('endCMD.sh', 'w')
with open('ips.txt','r') as f:
  ips = f.read().splitlines()
  for ip in ips:
    start.write('ip route add ' + ip + ' via 10.0.1.1 table mgre\n') 
    end.write('ip route del ' + ip + ' via 10.0.1.1 table mgre\n')     
    # 生成启动脚本与卸载脚本 (暂时没分运营商)
    # print(ip)
start.close()
end.close()

然后执行 basg sttartCMD.sh 来批量修改路由。

如果要持久化,让客户服务器重启后依旧能维持修改,可以把下面的命令写到脚本中去,然后开机的时候执行。

ip tunnel add mgre0 mode gre key 0xfffffffe ttl 255
ip addr add 10.0.1.2/24 dev mgre0
ip neigh add 10.0.1.1 lladdr 161.97.1.1 dev mgre0
ip link set mgre0 up
ip rule add from all table mgre
bash /root/startCMD.sh
#卸载连接只需要 ip rule del from all table mgre

优化前:

优化后:

可以说效果显著了(腾讯云过滤10099的流量,所以丢包比较严重)Olink的gre隧道还是很好用的,感兴趣的朋友可以试试。

Olink的德国9929 GRE 2美元/月起 自然月计费
Olink的美国9929 GRE 3美元/月起 自然月计费

参考

https://serverfault.com/questions/431593/iptables-forwarding-between-two-interface

Netfilter和iproute联合使用

内核参数 反向路由校验

深入理解 iptables 和 netfilter 架构