Rootless Router (Part2): BIRD-vpp

篇: Rootless Router (Re:0): VPP Host stack
前情提要: 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狀態機的維護之類

而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裡面

留言