如何使用 WireGuard 组建非对称路由

系统 Linux
在实际的互联网中“非对称路由”非常常见,即 A 到 B 和 B 到 A 走了不同的路径,而我们要想实现这个效果则需要先建立一个虚拟的网络,然后再在这个网络中配置路由,我这里使用了 wireguard 作为建立虚拟内网的工具。

   ❝

   方案二个人感觉是一个更好的配置方案,并且其中的 wireguard 配置方式也在方案一中使用,步骤更简洁明确一些。所以推荐没有耐心看完的同学直接看方案二。

奇怪的环境产生奇怪的需求——现在有一台机器 去程只有移动能够直连,电信和联通都会绕日走 ntt(tnt) ,一到了晚上就会产生剧烈的抖动以及严重丢包,那么是是否有办法去优化一下呢?使用一台移动网络的机器作为中转是一个方法,但是这样的话,所有的流量都会经过这台中转的机器,这台机器的速度成为了这个网络中的瓶颈,而且流量也会加倍消耗。既然我们只是去程绕路,那么是否有办法只优化去程的路由而保留原有的回程路由呢?在实际的互联网中“非对称路由”非常常见,即 A 到 B 和 B 到 A 走了不同的路径,而我们要想实现这个效果则需要先建立一个虚拟的网络,然后再在这个网络中配置路由,我这里使用了 wireguard 作为建立虚拟内网的工具。

三台机器上的非对称路由

环境准备

在这个实验中使用了三台机器 :

  1.  本地机器 A wireguard 网内 ip 为 192.168.51.5 169.254.1.5
  2.  去程不错但是带宽较小的机器 B 192.168.51.1 169.254.1.1
  3.  去程绕路但是回程不绕且带宽较大的机器 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。

命名如下 :

  1.  a1 本地对应的通过流量转发建立的 wireguard 连接的接口 169.254.2.1 192.168.51.3
  2.  a2 本地对应的通过直接连接建立的 wireguard 连接的接口 169.254.2.2 192.168.51.3
  3.  c1 远程对应的通过流量转发建立的 wireguard 连接接口 169.254.2.3 192.168.51.2
  4.  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 的配置方式,我觉得可以很好的实现这个需求,并且效果还挺不错,并且只需要在本地与远程两台服务器上进行配置即可。

责任编辑:庞桂玉 来源: 奇妙的Linux世界
相关推荐
点赞
收藏

51CTO技术栈公众号