如何使用 WireGuard 組建非對稱路由
?
方案二個人感覺是一個更好的配置方案,并且其中的 wireguard 配置方式也在方案一中使用,步驟更簡潔明確一些。所以推薦沒有耐心看完的同學(xué)直接看方案二。
奇怪的環(huán)境產(chǎn)生奇怪的需求——現(xiàn)在有一臺機器 去程只有移動能夠直連,電信和聯(lián)通都會繞日走 ntt(tnt) ,一到了晚上就會產(chǎn)生劇烈的抖動以及嚴(yán)重丟包,那么是是否有辦法去優(yōu)化一下呢?使用一臺移動網(wǎng)絡(luò)的機器作為中轉(zhuǎn)是一個方法,但是這樣的話,所有的流量都會經(jīng)過這臺中轉(zhuǎn)的機器,這臺機器的速度成為了這個網(wǎng)絡(luò)中的瓶頸,而且流量也會加倍消耗。既然我們只是去程繞路,那么是否有辦法只優(yōu)化去程的路由而保留原有的回程路由呢?在實際的互聯(lián)網(wǎng)中“非對稱路由”非常常見,即 A 到 B 和 B 到 A 走了不同的路徑,而我們要想實現(xiàn)這個效果則需要先建立一個虛擬的網(wǎng)絡(luò),然后再在這個網(wǎng)絡(luò)中配置路由,我這里使用了 wireguard 作為建立虛擬內(nèi)網(wǎng)的工具。
三臺機器上的非對稱路由
環(huán)境準(zhǔn)備
在這個實驗中使用了三臺機器 :
- 本地機器 A wireguard 網(wǎng)內(nèi) ip 為 192.168.51.5 169.254.1.5
- 去程不錯但是帶寬較小的機器 B 192.168.51.1 169.254.1.1
- 去程繞路但是回程不繞且?guī)捿^大的機器 C 192.168.51.2 169.254.1.2
需要實現(xiàn)的效果是 A 訪問 C 路徑為 A->B->C->A
在安裝好 wireguard 后需要生成密鑰且開啟包轉(zhuǎn)發(fā) :
$ 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 配置,注意一點 需要設(shè)置 Table=off,即禁止 wireguard 直接修改路由表 , 且這里使用的是鏈路本地地址建立的連接。另外,三臺機器彼此之間要兩兩連接 (對應(yīng)單獨的一個配置文件), 也就是說需要自己寫 6 個配置文件
節(jié)點 A 與節(jié)點 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
節(jié)點 A 與節(jié)點 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
節(jié)點 B 與節(jié)點 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
節(jié)點 B 與節(jié)點 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 =
節(jié)點 C 與節(jié)點 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
節(jié)點 C 與節(jié)點 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 分配與靜態(tài)路由
在建立了兩兩之間的連接之后,我們還需要在每臺機器上創(chuàng)建一個 dummy 網(wǎng)卡 (也可以寫到 postup 里面),用來獲取到發(fā)給自己的包。
以機器 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) 是還沒有路由的,不過我們希望去程經(jīng)過節(jié)點 B,那么我們在 A 上設(shè)置靜態(tài)路由 :
$ ip route add 192.168.51.2/32 dev [AB之間連接對應(yīng)的wireguard接口名]
# 或者 ip route add 192.168.51.2/32 via 169.254.1.1
然后還需要在節(jié)點 B 上設(shè)置到 C 的靜態(tài)路由 :
$ ip route add 192.168.51.2/32 dev [BC之間連接對應(yīng)的wireguard接口]
# 或者 ip route add 192.168.51.2/32 via 169.254.1.2
此時,在 A 上 pingC,在 B 上抓包,我們便能得到如下結(jié)果 :
PING C
在 B 上只能看見單向的數(shù)據(jù)流,而 A 是可以得到回應(yīng)的,再在 C 上抓包看看 :
C 上分別查看兩個接口 B-C 與 C-A 的接口
B-C
C-A
可以發(fā)現(xiàn)一點,實際上 B 作為路由進行轉(zhuǎn)發(fā)時,源 ip 是什么是無所謂的,只需要有目的 ip 即可,而目的 ip 的路由方式通過靜態(tài)路由指定了,那么在 C 上則會收到來自于 A 的鏈路本地地址 (169.254.1.5) 的包,而 C 與 A 直接連接,且 wireguard 創(chuàng)建了到 169.254.1.5 的連接,所以 C 的響應(yīng)是可以不經(jīng)過 B 直接走到 A 的。這樣就實現(xiàn)了一個非對稱的路由?,F(xiàn)在的設(shè)置下,A 主動 訪問 C 時會經(jīng)過 B,C 還沒辦法主動的連接 A,因為還沒有設(shè)置 C 到 A 的靜態(tài)路由,我們可以重復(fù)一遍前面的步驟,在 C 上與 B 上指定到 A 的靜態(tài)路由,也可以直接讓 C 訪問 A 而不經(jīng)過 A。不過由于該需求中我只需要 A 能夠主動訪問到 C 即可,所以不再進行這些設(shè)置。
為什么使用了鏈路本地地址
為什么在配置文件中使用了 169 開頭的地址,而不是直接用 192 開頭的地址建立連接并參與后續(xù)的路由設(shè)置呢?因為(至少是在我這種配置方式下),ip addr add xxx peer xxx 建立點對點連接時,會自動添加一條路由規(guī)則,會和我們后續(xù)添加的手動路由沖突。所以我只能退一步,使用另一個地址來建立點對點連接,然后用新的地址來設(shè)置靜態(tài)路由。wireguard 工作在網(wǎng)絡(luò)層,大概不支持不設(shè)置 ip 直接通過 mac 來連接 ?
只用兩臺機器實現(xiàn)非對稱路由
上一節(jié)實現(xiàn)了三臺機器組建的網(wǎng)絡(luò)的非對稱路由,那么能否更進一步,借助現(xiàn)有的“流量轉(zhuǎn)發(fā)”服務(wù),實現(xiàn)兩臺機器建立非對稱路由呢?這樣我們便不再需要第三臺服務(wù)器轉(zhuǎn)發(fā)去程,且優(yōu)質(zhì)線路在流量轉(zhuǎn)發(fā)服務(wù)中也常見一些,且由于只有去程走了轉(zhuǎn)發(fā),實際上是花不了多少錢的。
個人測試下來,這個方案也很容易實現(xiàn)。以本地機器 A 與遠程機器 C 為例,每臺機器上都要設(shè)置兩個 wireguard 接口,分別對應(yīng)通過端口轉(zhuǎn)發(fā)建立的 wireguard 以及直接連接建立的 wireguard。
命名如下 :
- a1 本地對應(yīng)的通過流量轉(zhuǎn)發(fā)建立的 wireguard 連接的接口 169.254.2.1 192.168.51.3
- a2 本地對應(yīng)的通過直接連接建立的 wireguard 連接的接口 169.254.2.2 192.168.51.3
- c1 遠程對應(yīng)的通過流量轉(zhuǎn)發(fā)建立的 wireguard 連接接口 169.254.2.3 192.168.51.2
- c2 遠程對應(yīng)的通過直接連接建立的 wireguard 連接接口 169.254.2.4 192.168.51.2
配置文件如下,注意這里沒有用 ip addr add peer 的命令,而完全采用靜態(tài)路由來實現(xiàn),也不再需要 dummy 設(shè)備,而是直接使用了后面需要用的 ip。對,沒寫錯,其實并沒有規(guī)定一個 ip 只能分配給一個接口,只需要我們后面靜態(tài)路由別寫錯就好,上一節(jié)采用三臺服務(wù)器的方案也可以這樣的配置形式,而不是用 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 = [填寫端口轉(zhuǎn)發(fā)服務(wù)的地址]: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配置文件進行該設(shè)置,指定直連
#PostDown =
Table = off
# local
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
然后配置靜態(tài)路由
本地機器上,去程通過 a1(經(jīng)過流量轉(zhuǎn)發(fā)) 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,并且抖動劇烈,可以看出效果還是很明顯的。
總結(jié)
目前的方案感覺依舊不是最優(yōu)解,用鏈路本地地址建立連接再添加 dummy 設(shè)備多少有些繁瑣,計算機網(wǎng)絡(luò)上還有很多我沒搞清楚的,且這個網(wǎng)絡(luò)節(jié)點較少,在較多 (>3) 路由節(jié)點的情況下,是否能直接這樣簡單的配置還是一個問題。不過關(guān)于這方面的資料實在是太少了,而當(dāng)前的配置方法雖然比較麻煩,但是也不是不能用。
上面那段總結(jié)是針對方案 1 的,而針對方案 2 的配置方式,我覺得可以很好的實現(xiàn)這個需求,并且效果還挺不錯,并且只需要在本地與遠程兩臺服務(wù)器上進行配置即可。