Rootless Router(Part: 2): BIRD-vpp
RootlessRouter系列:
- Rootless Router(Part: 0): 用戶態DN42節點
- Rootless Router(Part: 1): wggo-vpp
- Rootless Router(Re: 0): VPP Host stack
- Rootless Router(Part: 2): BIRD-vpp
- Rootless Router(Part: 3): EtherGuard
- Rootless Router(Extra):蒐集的Userspace 網路棧
- Rootless Router(Part: 4): 被VPP Host Stack衝康
- Rootless Router(Part: 5): 完結
- Rootless Router(Fin): UML版本上線啦!
- Rootless Router(Afterword): Azure App Service真的很靈
前情提要: exabgp / gobgp / frr / python3-http-server 沒有一個能正常運作,我正心灰意冷的時候,發現有一個居然能夠正常運作,就是BIRD
BIRD-vpp
https://zstas.github.io/jekyll/update/2020/03/07/vcl.html
這篇文章作者讓BIRD在VPP host stack底下成功運作起來了
我一開始一直沒辦法成功運作,出現
Socket error: IP_TTL: Operation not supported
詢問了群友們沒有結果,畢竟這個玩法不是這麼的常見,那麼只能自己看源碼了
發現... 沒辦法,只能改原碼,BIRD不提供任何設定檔關掉這個行為
經過一番patch,BIRD 終於運作起來了。
其中讓我覺得最雷的是這一段
因為VPP是按照POSIX規範進行實作,但BIRD居然沒有遵照POSIX規範呼叫,導致無法正常運作!!
linux給你方便,其他人好歹也遵守一下POSIX標準吧。能遵守POSIX盡量遵守,害我在這邊花超多時間QQ
好的開始
但是我發現BIRD雖然起來了,但是peer一直都是idle狀態,就是不肯連線。
lsof查看,也沒有監聽任何port。
網路上查詢過以後發現別人也遇過這個問題,只要加上multihop就可以了。
此時我也不知道multihop是什麼,但是ipv4 / ipv6都能Eastablish,路由也有正常過來
大部分都運作正常,但是就只有ipv6 link-local沒辦法正常運作
這裡可以看到,link-local一定要綁定interface才能運作 |
我以為只要解決ipv6 link local問題,就萬事大吉。沒想到這才是惡夢的開始...
IPV6 Link Local 問題
首先我們要知道BIRD運作的環境。也就是VPP Host Stack
一般情況我們程式想要監聽port,多半會呼叫bind()這個系統呼叫。
想要連線,就會呼叫socket()這個系統呼叫。如果呼叫成功,就會拿到一個file descriptor,是一個整數
之後呼叫Read/Write function時,傳入fd,OS就會把資料弄去buffer了
這一切,OS會在背後幫我們處理。從網路堆疊複製東西/tcp狀態機的維護之類
想要連線,就會呼叫socket()這個系統呼叫。如果呼叫成功,就會拿到一個file descriptor,是一個整數
之後呼叫Read/Write function時,傳入fd,OS就會把資料弄去buffer了
這一切,OS會在背後幫我們處理。從網路堆疊複製東西/tcp狀態機的維護之類
而VCL會劫持BIRD的bind之類的系統呼叫,然後在用戶態維護他自己的網路堆疊/tcp狀態機之類,複製資料給BIRD
這個列表是被VPP劫持了的系統呼叫
https://github.com/KusakabeSi/RootlessRouter/blob/main/misc/readelf_libvcl.txt
Socket可以指定參數綁定到特定硬體,但是BIRD指定的硬體id在VPP裡面肯定是不存在的,就算存在也會是錯的
所以綁定硬體這個行為不能被用到。使用multihop參數,BIRD就不會綁定硬體。但是link-local強制綁定硬體,怎麼辦呢...
首先被我盯上的就是這個function,直接拿掉link-local的判斷就好了嘛
ip6_is_link_local |
首先遇到的問題,就是ipv6 link local,BGP session能夠正常建立,但是路由一個都過不來
用ipv6 LL,v4都能正常過來,v6不行!? |
birdc則是一直輸出log: Invalid nexthop attrubute
找到問題
群友們隔空抓藥,也得不出結果。只能說我這個use case太奇怪了,正常人哪會這麼搞?
只能再度改code,把所有出現這個message(有8處)的地方,全部加上編號,看看是哪邊有問題
這一查,看到是2號BAD_NEXT_HOP出問題。到這邊花了我一個下午+晚上,才被我找到問題
跑去看原始碼:
還有抓封包:
普通ipv6 |
ipv6 link-local |
發現這個問題同時和發送方以及接收方的配置有關。可以看到本地對這個session是direct模式還是multihop模式做了判斷,兩邊行為是不一樣的
當我們配置ipv6的時候(上圖),nexthop會同時帶上ipv6以及ipv6 link-local
但是當我們配置ipv6 link-local的時候,就只會帶上LL欄位了,gw欄位是空的[::]
所以對面發過來的欄位就只有ipv6 link-local欄位,而gw欄位就是[::]了
而我們這邊也是,我們這邊檢查,如果是direct模式,ipv6欄位是空的沒關係(952行)
如果LL欄位不是空的(954行),就使用LL欄位作為鄰居ip(955行)
但是如果是multihop模式就不同了。才不管你的LL欄位,你gw欄位是空的就直接WITHDRAW (967行)
所以是我邏輯寫錯了。
因為VPP不能綁定網卡,所以我用multihop讓他不要綁網卡
導致這個的檢查,讓我收不到路由
解決方案
當然我也可以改code,把multihop部分code改掉,讓它明明是multihop模式,但是當成direct來處理
但是在這之餘,我瀏覽了BIRD其他多處的程式碼,發現還是有很多類似的判斷,要改動的話改不完的。像是ospf protocol就是強制要求direct,還有其他形形色色各式各樣的模式
現在行為是BIRD會掃描系統的網卡,如果ip對不上就甚麼都不會做,也不會嘗試建立連接綁定端口之類,卡在idle狀態
理想:
我覺得比較好的code改法,應該是config還是可以設定direct模式,但是BIRD會綁到一張假的網卡。listen()那邊,把iface設定成null,讓這個socket實際上不綁定任何網卡。
讓BIRD內部邏輯一樣是direct模式
更好的解決方法,就是netlink.c全部砍掉。改成串接vpp-api,讀取VPP裡面的網卡資訊。這樣就能夠更加發揮BIRD的設計,而不是他以為有做安全限制,實際上限制全沒了
而且用這個解決方案的話,vpp-api都串了,protocol kernel也可以順便弄一弄。讓他把收到的路由直接寫入VPP,還有從VPP裡面讀取路由表。發揮protocol kernel完整的功能
現實:
但是我覺得工程量還是有點太大了。而且這個計畫還缺不少東西,像是把所有節點串在一起的mesh vpn。沒有一款支援VPP,要開始考慮自己刻一個,或是改造別人的,弄成VPP版了。至少要有NAT打洞功能。
所以最後我的Rootless Router計畫還是決定使用膠水方案了。
首先設定檔直接砍掉protocol kernel
用python3每10秒解析birdc show route的輸出,再用python3-vpp-api寫入VPP裡面
首先設定檔直接砍掉protocol kernel
用python3每10秒解析birdc show route的輸出,再用python3-vpp-api寫入VPP裡面
留言
張貼留言