Rootless Router(Extra):Userspace 網路棧

之前在這篇提到我蒐集到的Userspace network stack,想說獨立一篇出來好了。之後有查到新東西直接加進來

用戶態的網路堆疊方案

這些是目前我找到的用戶態的網路堆疊方案,以及他們kernel bypass所用的技術

  1. 基於LD_PRELOAD
    1. VPP (我用起來相容性不好。後來發現BIRD能用)
    2. NUSE (聽說bug很多)
    3. The shadow simulator (很新的專案,資料蠻少的)
  2. 基於ptrace
    1. gVisor
    2. UML(User Mode Linux) (就是linux的網路棧)
    3. The shadow simulator (好像兩個一起用,增加穩定度)
  3. 重新編譯原始碼
    1. LKL(Linux Kernel Library) (C/C++版本,也是linux的網路棧)
    2. LKL.js (javascript版本的LKL,可以在瀏覽器上執行!)
    3. F-Stack
  4. 基於network namespace (只是用了linux的ns隔離,本質上還是跑在kernel)
    1. mininet
  5. 沒有kernel bypass功能,依賴tun/tap
    1. RARE/freertr
    2. ns3

什麼是kernel bypass? 和網路隔離有什麼關係?

首先我們要知道,unix底下,程式如何連上網路的
首先,linux可執行檔使用 socket(domain,type,protocol) 系統呼叫
OS就會在背景創建一個socket,同時返回一個file descriptor,是一個整數。以下簡稱fd
如果我們想監聽port,我們使用 bind(fd, &addr, addrlen) 這個系統呼叫
如果我們想連線,就會使用 connect(fd, &addr, addrlen) 這個系統呼叫
我們想接收/發送資料,只要呼叫 read(fd, &buflen) 或是 write(fd, &buflen)即可
剩下的一切,linux都會在背後幫我們處理

linux如何處裡的呢? 預設情況下,linux會維護一個網路堆疊
據我們的需求,這些API都會和那個堆疊互動。修改堆疊內tcp狀態機的狀態
或是從網路堆疊複製/寫入資料到我們提供的buffer內
或是呼叫網卡驅動,驅動網卡的電路晶片,發送網路封包

當我們想要隔離網路的時候,有一個要點: 不要和主系統的網路堆疊互動。我們跑去和別的網路堆疊作互動

常見的網路隔離的技術:

  1. network namespace: linux kernel 維護多個網路堆疊
    1. 當應用程式呼叫socket()時,kernel會根據你所處的namespace,決定要和哪個堆疊作互動
  2. LD_PRELOAD: 劫持現有程式內部的系統呼叫。
    1. 以VPP為例,首先
    2. DPDK架構下,封包不經過kernel
    3. 所以VPP要自己維護一個網路堆疊,獨立於kernel的網路堆疊
    4. VCL負責劫持程式的 socket()/bind()/connect()/read()/write() 等等系統呼叫
    5. 並且把呼叫參數轉交給VPP,和VPP維護的網路堆疊作互動
    6. 被VPP劫持了的系統呼叫列表
    7. 基於環境變數,環境變數被改變,劫持就失效了
  3. ptrace: 和LD_PRELOAD一樣,但是改成透過ptrace()這個系呼叫
    1. 流程類似,劫持網路相關系統呼叫
    2. hook由kernel管理,更難逃逸
    3. 為一些容器技術利用,在用戶態實現kernel API
    4. 做出比docker更安全的容器,不受kernel漏洞影響
  4. 重新編譯原始碼: 這個很容易理解
    1. 編譯的時候直接把 bind() socket()之類的改成 custom_bind() custom_socket()
    2. 裡面則是和自己維護的堆疊互動

這個特性對我來說是至關重要的。因為我並沒有系統管理員權限
改動系統路由表/創建network space/veth/tun tap等等操作都是Operation Not Permitted

所以只能仰賴這種方式,不再依賴kernel提供的網路堆疊,使用自己實作的網路堆疊。再用kernel bypass的方式把第三方程式拉入使用自己的堆疊

能用的kernel bypass技術

Docker unprivileged container 裡面,只有方案1/3是可以用的,ptrace/ns都沒權限

更新: 有個好消息,kernel 4.8以後,ptrace就可以用了。因為linux kernel的ptrace security issues修好了!
所以docker 19.03以及kernel 4.8以後,方案2也可用了! 

現在方案2也納入我的考慮之中了!
不過我目前VPP已經搞一堆東西了,現在要我換跑道,改走gvisor路線,之前的努力就又白費了
還不想現在切換跑道,遇到問題能patch先考慮patch看看。這就是沉沒成本效應嗎?

留言