純用戶態的DN42節點(尚未完成,心路歷程)

起因是有一台免費docker小雞,給的權限有夠小。沒有tun/tap,沒有wireguard。但是我想讓它運作成一台dn42節點。

理想中的這隻程式,對外維持許多wireguard session,沒別的東西了。
對內則是維護一個network stack,wg解包出來丟進stack,經過BGP daemon+routing以後跑去另一個wireguard。在這之中完全沒有kernel的參與


我在想,路由這些東西都在kernel實現,就沒有在userspace實現的版本嗎?


我查了查,許多用戶態的network stack還不少。但是有一個吸引了我的注意: fd.io/VPP。

因為linux kernel網卡驅動模型的特性,每個packet都是一個CPU中斷,注定快不起來。所以有人搞了個DPDK,讓網卡封包直接跑去userspace,就不會有那麼多中斷。不過這樣就要有用戶態的封包處理程式。於是就有了fd.io/VPP

不過以上都不是重點,我會看上fd.io/VPP是因為它有個神奇的操作,利用LD_PRELOAD,劫持掉普通應用程式的socket, bind, getsockname, if_nametoindex, sendmsg, recvmsg等等syscall,redirect去VPP裡面的network stack。這樣就可以不用patch,直接運作就能讓任意程式加入自己的網路,而不是用kernel提供的網路的。聽說這個技巧叫做kernel bypass。這正是我需要的,bypass掉那該死的kernel


VPP是為了性能而誕生,但我這裡完全不是為了這個。

VPP的正常用法,是搭配下層搭配DPDK,上層劫持socket api,讓普通讀程式可以連入DPDK網路

但是我只是想要他的用戶態網路堆疊的處理能力,再用memif把流量送到wireguard-go。讓這台沒權限的docker裡面的任意app加入dn42網路

後來有群友推薦,可以嘗試gvisor。他是用ptrace來達到kernel bypass的效果。雖然我這台docker VPS有開放ptrace權限,不過普通的docker預設是不開放的。

在弄的時候果然還是希望成品能盡量支援多一點的地方,所以有ld_preload實現地會被我優先考慮。目前我看到有ld_preload實現的只有2個,VPP和NUSE。但是NUSE已經停止開發5年了,而且有些bug也沒修復。所以VPP目前算是唯一的選擇。而且聽說VPP速度也快一點

群友還有推薦另一個freertr,也是userspace的network stack。
dataplane/controlplane都有。不過缺點就是沒有ld_preload讓其他elf可以不經修改直接加入網路

還有ns3也是我考慮過的,不過缺點一樣是沒有ld_preload可以用。

如果有其他的,比較成熟的ld_preload方案,麻煩也告訴我,謝謝。
我實在是找不到別的了

要加入DN42,還有一個東西要搞就是BGP daemon,也就是現在卡住的地方。 VPP裡面有FIB路由表。看了一下幾個BGP daemon,BIRD會直接透過netlink把路由表發給kernel,這完全不行。docker根本沒權限改路由表

看到FRRouting/Quagga,他們不是直接寫表到kernel,而是透過zebra api寫到kernel。這讓我燃起了一絲希望。如果有人有寫zebra-VPP的plugin,抽換掉正常的zebra,問題就解決了。
而且我還看到VPP有router plugin,一度讓我覺得終於要搞定了。但很可惜,問題沒解決

我在網路上找到的所有VPP+Dynamic routing的方案都類似這樣
先讓zebra把路由寫入kernel,VPP的router-plugin再把路由資訊讀取出來,同步到VPP的路由表。就是這一步經過了kernel,又不行了。

原本堅持想用VPP+FRRouting而不是全程手刻,其中一個原因是覺得FRRouting是十分強大的routing daemon,不重複造輪子。未來想搞babel/ospf都能直接用。

要解決這個,只能寫一個解析zebra protocol的VPP-plugin了。但是這個複雜度實在有點高。

導致我想尋求其他解決方案了。BGP daemon還是跑去改造exabgp吧,收到的路由用vpp-python3-api寫入vpp裡面
總之目前進度就到這邊,有什麼想法歡迎留言/加群討論

留言