Rootless Router(Part: 0): 用戶態DN42節點

RootlessRouter系列:
  1. Rootless Router(Part: 0): 用戶態DN42節點
  2. Rootless Router(Part: 1): wggo-vpp
  3. Rootless Router(Re: 0): VPP Host stack
  4. Rootless Router(Part: 2): BIRD-vpp
  5. Rootless Router(Part: 3): EtherGuard
  6. Rootless Router(Extra):蒐集的Userspace 網路棧
  7. Rootless Router(Part: 4): 被VPP Host Stack衝康
  8. Rootless Router(Part: 5): 完結
  9. Rootless Router(Fin): UML版本上線啦!
  10. Rootless Router(Afterword): Azure App Service真的很靈

起因是網路上有些免費的容器,例如heroku,或是Azure web service之類,給的權限有夠小
沒有tun/tap,沒有wireguard。這些功能都是kernel獨有,但都沒給
但是我想把它變成一台dn42節點!

理論上來說,一個dn42節點,說穿了不過就只是從A收udp wireguard封包,然後弄一弄轉給B而已嗎?,完全不需要root權限

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

於是我想,一般的路由功能,全部都大量依賴linux kernel。
frr/bird等常見的bgp daemon,也都是把收到的路由表寫入linux kernel。

難道就不能在userspace實現嗎?

要做到這個目的,必須要實現2點:

1: 用戶態的網路堆疊+路由轉發表
2: 在上面的堆疊裡面運作一個BGP daemon

我查了查,用戶態的網路堆疊還不少。但是第二點才是困難的

眾多現有的程式,有一個吸引了我的注意: fd.io/VPP。

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

不過以上都不是重點,我會看上fd.io/VPP是因為它有個神奇的操作,利用LD_PRELOAD,劫持掉普通應用程式的socket, bind, sendmsg, recvmsg, getsockname, if_nametoindex 等等syscall,讓他改用VPP裡面的網路堆疊

這樣就可以不用打任何patch,直接就能讓任意linux程式加入自己的網路堆疊,而不是用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方案,麻煩也告訴我,謝謝。
目前蒐集到的userspace網路堆疊: https://www.kskb.eu.org/2021/09/userspace.html
我實在是找不到別的了

要加入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裡面


總之目前進度就到這邊,有什麼想法歡迎留言/加群討論



留言