(圖片來源)
在這之前,如果需要看一下 eBPF 的簡介,那可以去 YouTube 上找。比如說 "eBPF 101" - Muhammad Falak R Wani (LCA 2022 Online)。2016 年開始每年各大研討會都會有,會放這個是因為年份看起來最新。
這個教學的原始版本是 2019 年 netdev 中的 XDP Hands On Tutorial。不過這個工作坊當中比較像是參與者自己看文件做一遍,有問題自己發問,幾乎沒有講解。
上一個影片是使用 network namespace 搭配 veth
做實驗。更多 XDP 在 container 中的應用可以參考這個影片。
是 AF_XDP
的介紹,
這裡使用的是 xdp-project/xdp-tutorial
中的例子。除了該文件上提到的套件外,如果是在 Raspberry Pi 4B 上使用 Ubuntu 21.10,那麼要多安裝以下套件:
否則會出現下面的警告:
另外,如果是使用 xdp-project/xdp-tutorial
中的 Makefile,而且要在 Raspberry Pi 4B 上編譯,那要在 common/common.mk
中的 BPF_CFLAGS
後面多加上 BPF_CFLAGS += -I/usr/include/aarch64-linux-gnu
選項 (參考 Update setup_dependencies.org #201 這個 PR):
而如果是在命令列直接用 clang
編譯,那要加上 -I/usr/include/aarch64-linux-gnu
選項。
如果是在 Raspberry Pi 4B 上編譯,但是不想使用它的 Makefile,那麼可以使用以下命令:
然後使用以下命令來載入:
這裡的 xdpgeneric
選項有一些細節,不過用下一個例子說明會比較清楚。
例子裡面提到會藉由兩個 network namespace 之間的溝通作為例子,使用當中的 testenv.sh
會建立具有以下的 namespace:
如果不想用那個 testenv.sh
來建立 namespace,也可以用以下方法建立類似的實驗環境。首先,新增一個名稱為 test01
的 network namespace:
然後新增一對 veth-pair。其中,令這個 veth-pair 中的兩個 interface 的名稱各自為 veth-basic02
與 veth0
:
把 veth0
那個 interface 加入 veth-basic02
這個 network namespace 中:
接著幫這兩個 interface 各自設定 IPv6 位址:
最後,啟動這兩個 interface:
就可以在 veth-basic02
這個 namespace 中載入剛剛的 XDP 程式。比如說如果載入 xdp_drop
這個 section:
這時候,如果在 Root namespace 中:
會發現 veth-basic02
這個 interface:
而如果是在 veth-basic02
裡面:
則會看到以下的:
如果之後要在 veth-basic02
這個 network namespace 中執行命令,可以使用:
比如說如果要從 veth-basic02
這個 namespace ping
到 Root namespace,那麼就:
另外,原先的文章中提到使用 XDP_ABORTED
會觸發 xdp:xdp_exception
這個 tracepoint,然後有使用 perf
去 hook 這個 tracepoint。事實上也可以用 trace-cmd
(ftrace) 去 hook。比如說在背景 ping
之後,使用以下命令:
一段時間後終止它,再生成報告:
就會看到被觸發的事件:
順帶一提,如果使用 -T
選項:
那麼在 trace-cmd report
的時候,可以產生 stack trace。比如說:
或是像這個:
上面這兩個狀況都是在 softirq 中觸發到注入的 XDP 程式:前者是給 ksoftirqd
做,後者則是在重新啟動 softirq 時被執行。但不管是哪一個,都可以發現發生在處理封包的 softirq 中觸發了這個 XDP 程式。
另外,這個 XDP 程式被執行到的時候,已經是 sk_buff
處理到一半的時候。這跟上面某些演講提到「若驅動程式中有支援 XDP,則可以在建立 sk_buuff
之前就過濾封包」似乎不同。這個理由是:前面的例子中使用的是 generic XDP,也就是剛剛使用 ip
命令載入時使用的 xdpgeneric
選項,它觸發的時機是出現在 sk_buff
建立之後,所以會發現過濾並非在驅動程式中發生,而是在 network stack 中。ip-link(8)
的文件中的 ip link set - change device attributes 中則有提到:
xdp object | pinned | off
…If the driver does not have native XDP support, the kernel will fall back to a slower, driver-independent "generic" XDP variant. The ip link output will in that case indicate xdpgeneric instead of xdp only.
除此之外,可以參考 LWN 中的 Generic XDP 的說明。
如果希望在驅動程式中就能使用 XDP,則需要該驅動程式有支援,並且在載入時使用 xdpdrv
選項。在有些上面的演講裡面,這會叫作 driver XDP 或 native XDP。而 veth
對此也有支援。想做到這件事,首先把原來的 XDP 程式卸載:
並且將改為:
然後重複剛剛的 ping
與 trace-cmd
。可以用 tmux
開多個分割,或是把 hook 住 tracepoint 的程式先用 &
放到背景執行,ping
完之後再把它用 fg
恢復到前景並終止:
結束後產生報告 (使用 trace-cmd report -l
) 會發現:這個 XDP 程式被觸發的地方並不是在處理 sk_buff
的 __netif_receive_skb_core
中,而是在那之前,在 __napi_poll
裡面就觸發了:
另外一部分對應的 stack trace 如下,可以觀察到相同的結論。只是為了完整而附上。