如何使用 WireGuard 組建非對稱路由
?
方案二個人感覺是一個更好的配置方案,并且其中的 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 的配置方式,我覺得可以很好的實現這個需求,并且效果還挺不錯,并且只需要在本地與遠程兩臺服務器上進行配置即可。