方案二个人感觉是一个更好的配置方案,并且其中的 wireguard 配置方式也在方案一中使用,步骤更简洁明确一些。所以推荐没有耐心看完的同学直接看方案二。
奇怪的环境产生奇怪的需求——现在有一台机器 去程只有移动能够直连,电信和联通都会绕日走 ntt(tnt) ,一到了晚上就会产生剧烈的抖动以及严重丢包,那么是是否有办法去优化一下呢?使用一台移动网络的机器作为中转是一个方法,但是这样的话,所有的流量都会经过这台中转的机器,这台机器的速度成为了这个网络中的瓶颈,而且流量也会加倍消耗。既然我们只是去程绕路,那么是否有办法只优化去程的路由而保留原有的回程路由呢?在实际的互联网中“非对称路由”非常常见,即 A 到 B 和 B 到 A 走了不同的路径,而我们要想实现这个效果则需要先建立一个虚拟的网络,然后再在这个网络中配置路由,我这里使用了 wireguard 作为建立虚拟内网的工具。
三台机器上的非对称路由
环境准备
在这个实验中使用了三台机器 :
- 本地机器 A wireguard 网内 ip 为 192.168.51.5 169.254.1.5
- 去程不错但是带宽较小的机器 B 192.168.51.1 169.254.1.1
- 去程绕路但是回程不绕且带宽较大的机器 C 192.168.51.2 169.254.1.2
需要实现的效果是 A 访问 C 路径为 A->B->C->A
在安装好 wireguard 后需要生成密钥且开启包转发 :
$ apt install wireguard wireguard-tools
$ wg genkey | tee privatekey | wg pubkey > publickey
$ echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
$ echo "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.conf
$ echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
$ sysctl -p
两两之间建立连接
下面直接贴一下三台机器的 wireguard 配置,注意一点 需要设置 Table=off,即禁止 wireguard 直接修改路由表 , 且这里使用的是链路本地地址建立的连接。另外,三台机器彼此之间要两两连接 (对应单独的一个配置文件), 也就是说需要自己写 6 个配置文件
节点 A 与节点 B 配置文件 :
[Interface]
PrivateKey =
ListenPort = 27000
PostUp = ip addr add 169.254.1.5/32 peer 169.254.1.1/32 dev %i
PostDown = ip addr del 169.254.1.5/32 peer 169.254.1.1/32 dev %i
Table = off
# B
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
PersistentKeepalive = 10
节点 A 与节点 C 配置文件 :
[Interface]
PrivateKey =
ListenPort = 27001
PostUp = ip addr add 169.254.1.5/32 peer 169.254.1.2/32 dev %i
PostDown = ip addr del 169.254.1.5/32 peer 169.254.1.2/32 dev %i
Table = off
# C
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
PersistentKeepalive = 10
节点 B 与节点 A 配置文件 :
[Interface]
PrivateKey =
ListenPort = 27000
PostUp = ip addr add 169.254.1.1/32 peer 169.254.1.5/32 dev %i
PostDown = ip addr del 169.254.1.1/32 peer 169.254.1.5/32 dev %i
Table = off
#A
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
节点 B 与节点 C 配置文件 :
[Interface]
PrivateKey =
ListenPort = 26002
PostUp = ip addr add 169.254.1.1/32 peer 169.254.1.2/32 dev %i
PostDown = ip addr del 169.254.1.1/32 peer 169.254.1.2/32 dev %i
Table = off
# C
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
节点 C 与节点 A 配置文件 :
[Interface]
PrivateKey =
ListenPort = 27001
PostUp = ip addr add 169.254.1.2/32 peer 169.254.1.5/32 dev %i
PostDown = ip addr del 169.254.1.2/32 peer 169.254.1.5/32 dev %i
Table = off
#A
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
节点 C 与节点 B 配置文件 :
[Interface]
PrivateKey =
ListenPort = 26002
PostUp = ip addr add 169.254.1.2/32 peer 169.254.1.1/32 dev %i
PostDown = ip addr del 169.254.1.2/32 peer 169.254.1.1/32 dev %i
Table = off
# thk
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
ip 分配与静态路由
在建立了两两之间的连接之后,我们还需要在每台机器上创建一个 dummy 网卡 (也可以写到 postup 里面),用来获取到发给自己的包。
以机器 A 为例,BC 上也要进行同样的操作 (记得改 ip)
# ip link del dummy
$ ip link add dummy type dummy
$ ip addr add 192.168.50.5/32 dev dummy
$ ip link set dummy up
此时 192.168.50.5(A) 到 192.168.50.2(C) 是还没有路由的,不过我们希望去程经过节点 B,那么我们在 A 上设置静态路由 :
$ ip route add 192.168.51.2/32 dev [AB之间连接对应的wireguard接口名]
# 或者 ip route add 192.168.51.2/32 via 169.254.1.1
然后还需要在节点 B 上设置到 C 的静态路由 :
$ ip route add 192.168.51.2/32 dev [BC之间连接对应的wireguard接口]
# 或者 ip route add 192.168.51.2/32 via 169.254.1.2
此时,在 A 上 pingC,在 B 上抓包,我们便能得到如下结果 :
PING C
在 B 上只能看见单向的数据流,而 A 是可以得到回应的,再在 C 上抓包看看 :
C 上分别查看两个接口 B-C 与 C-A 的接口
B-C
C-A
可以发现一点,实际上 B 作为路由进行转发时,源 ip 是什么是无所谓的,只需要有目的 ip 即可,而目的 ip 的路由方式通过静态路由指定了,那么在 C 上则会收到来自于 A 的链路本地地址 (169.254.1.5) 的包,而 C 与 A 直接连接,且 wireguard 创建了到 169.254.1.5 的连接,所以 C 的响应是可以不经过 B 直接走到 A 的。这样就实现了一个非对称的路由。现在的设置下,A 主动 访问 C 时会经过 B,C 还没办法主动的连接 A,因为还没有设置 C 到 A 的静态路由,我们可以重复一遍前面的步骤,在 C 上与 B 上指定到 A 的静态路由,也可以直接让 C 访问 A 而不经过 A。不过由于该需求中我只需要 A 能够主动访问到 C 即可,所以不再进行这些设置。
为什么使用了链路本地地址
为什么在配置文件中使用了 169 开头的地址,而不是直接用 192 开头的地址建立连接并参与后续的路由设置呢?因为(至少是在我这种配置方式下),ip addr add xxx peer xxx 建立点对点连接时,会自动添加一条路由规则,会和我们后续添加的手动路由冲突。所以我只能退一步,使用另一个地址来建立点对点连接,然后用新的地址来设置静态路由。wireguard 工作在网络层,大概不支持不设置 ip 直接通过 mac 来连接 ?
只用两台机器实现非对称路由
上一节实现了三台机器组建的网络的非对称路由,那么能否更进一步,借助现有的“流量转发”服务,实现两台机器建立非对称路由呢?这样我们便不再需要第三台服务器转发去程,且优质线路在流量转发服务中也常见一些,且由于只有去程走了转发,实际上是花不了多少钱的。
个人测试下来,这个方案也很容易实现。以本地机器 A 与远程机器 C 为例,每台机器上都要设置两个 wireguard 接口,分别对应通过端口转发建立的 wireguard 以及直接连接建立的 wireguard。
命名如下 :
- a1 本地对应的通过流量转发建立的 wireguard 连接的接口 169.254.2.1 192.168.51.3
- a2 本地对应的通过直接连接建立的 wireguard 连接的接口 169.254.2.2 192.168.51.3
- c1 远程对应的通过流量转发建立的 wireguard 连接接口 169.254.2.3 192.168.51.2
- c2 远程对应的通过直接连接建立的 wireguard 连接接口 169.254.2.4 192.168.51.2
配置文件如下,注意这里没有用 ip addr add peer 的命令,而完全采用静态路由来实现,也不再需要 dummy 设备,而是直接使用了后面需要用的 ip。对,没写错,其实并没有规定一个 ip 只能分配给一个接口,只需要我们后面静态路由别写错就好,上一节采用三台服务器的方案也可以这样的配置形式,而不是用 dummy 接口。
a1 配置:
[Interface]
PrivateKey =
Address = 192.168.51.3/32
PostUp = ip route add 192.168.51.2/32 dev %i
#PostDown =
Table = off
#hkg
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint = [填写端口转发服务的地址]:14967
PersistentKeepalive = 10
a2 配置 a1 和 a2 只有 endpoint ip、端口不一样:
[Interface]
PrivateKey =
Address = 192.168.51.3/32
#PostUp =
#PostDown =
Table = off
#hkg
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint = [C的地址]:14967
PersistentKeepalive = 10
c1、c2 配置,注意 c1 和 c2 只有端口不一样:
[Interface]
PrivateKey =
ListenPort = 27002
Address = 192.168.51.2/32
PostUp = ip route add 192.168.51.3/32 dev %i # c2配置文件进行该设置,指定直连
#PostDown =
Table = off
# local
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
然后配置静态路由
本地机器上,去程通过 a1(经过流量转发) ip route add 192.168.51.2/32 dev a1
远程机器上,回程直连 ip route add 192.168.51.2/32 dev c2
之后我们尝试在本地 ping 192.168.51.2 看看效果
延迟降低到了 30ms,而之前去程绕日 ntt 延迟有 70ms,并且抖动剧烈,可以看出效果还是很明显的。
总结
目前的方案感觉依旧不是最优解,用链路本地地址建立连接再添加 dummy 设备多少有些繁琐,计算机网络上还有很多我没搞清楚的,且这个网络节点较少,在较多 (>3) 路由节点的情况下,是否能直接这样简单的配置还是一个问题。不过关于这方面的资料实在是太少了,而当前的配置方法虽然比较麻烦,但是也不是不能用。
上面那段总结是针对方案 1 的,而针对方案 2 的配置方式,我觉得可以很好的实现这个需求,并且效果还挺不错,并且只需要在本地与远程两台服务器上进行配置即可。