日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

内核调试技巧-逆向寻踪,揭开 LACP 协议流程的神秘面纱

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内核调试技巧-逆向寻踪,揭开 LACP 协议流程的神秘面纱 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:wqiangwang,騰訊 TEG 后臺開發工程師

本文通過“Kni 映射到內核的接口未能發送 LACP 報文導致 bond 不能聚合”這個問題,來探索內核調試中,對于正在運行的內核,通過 systemtap 獲取關鍵數據結構的值的通用方法。

背景

DPDK 支持物理端口 通過 kni 映射到內核的虛擬接口作為內核的標準 net device,借助內核完善的生態處理相對復雜的網絡協議,如 tcp 等,這樣以后,無需在用戶態實現這些協議。在 NGW 網關產品中,同樣的,從物理端口接收的 LACP 報文則通過 kni 注入給內核;內核向外發送的 LACP 報文則通過 kni 處理并從物理口出。借助內核成熟的 LACP 協議和生態 而無需用戶態自己實現 LACP,即可完成 bond 聚合。

但在升級 DPDK-20.11 版本時,出現 bond 未能聚合。通過 tcpdump 抓包發現,原來對端交換機已經發送了 LACP 報文,而本端一直未發送 LACP 報文,所以未能聚合:

分析

1、 既然是本端一直未發送 LACP 報文, 則內核協議棧沒有調用 dev_queue_xmit 函數向外發送 skb->protocol 為 0x8809 的 skb。剛好手上有一臺相同內核版本且 bond 已經聚合的設備,為了縮短 bond 內核模塊的代碼學習時間,因此看下這臺設備當調用 dev_queue_xmit 函數發送 skb->protocol 為 0x8809 的 skb 的 backtrace。編寫 systemtap 腳本如下:

2、 對于本設備,systemtap probe 函數 bond_3ad_state_machine_handler 發現其調用時 ad_lacpdu_send 未調用??匆幌麓a,發現 ad_lacpdu_send 若執行,需滿足的條件離不開 port 的訪問,為此必須知道 port 的地址:

這里出現了難點,從上述的 backtrace 調用??芍?#xff0c;ad_tx_machine 已經被編譯器優化,不能通過 systemtap 直接獲取$port 入參或者 pointer_arg(1)提取入參,因此從匯編入手獲取標識 port 的寄存器的值。先用 systemtap 看下行號和匯編指令的對應關系:

匯編的幾個[test 和 je/jne]指令剛好和上面的 ad_tx_machine 源碼中 if 的邏輯是一致的。同時上述的 if 條件都滿足的話,則進入 bond_3ad_state_machine_handler+3079 開始的邏輯:

而 ad_lacpdu_send 作為 callee 是沒有被優化的,仍以函數調用,按照 x64 傳參規則,%rdi 作為第一個參數即為 port 的標識。從 mov %r12,%rdi 可看出,獲取%r12 也就獲取了 port。那么問題來了,應該 probe 什么位置,調用 print_regs 函數獲取%r12 呢。

3、 從上面的匯編[890-929],[3079,3303]這兩個連續區間來看,沒有任何寫指令操作%r12。而匯編[890-929]區間為 ad_lacpdu_send 能否執行的判斷邏輯,所以選擇匯編 890 位置為 probe 位置調用 print_regs 函數來獲取寄存器。同時匯編 890 位置對應的源碼位置為 bond_3ad.c:1261:

因此編寫如下 systemtap 腳本:

運行結果如下:

至此找到 port 的地址了,為了驗證 port 的地址的正確性,此時可以通過 crash 或者 systemtap 繼續校驗。這里通過 systemtap 提取 port 的 adctor_system 成員的 mac 地址來校驗。

因此編寫如下 systemtap 腳本,驗證 ffff888995109838 是 port:

運行結果如下,其 mac 地址和系統中記錄的 bond 信息一致:

4、 既然已經知道了 port,那看下為什么 port 的成員在上述 if 條件中失敗了,再回顧下這個代碼,顯然需要訪問下 port 的 sm_tx_timer_counter, ntt, sm_vars 三個成員:

因此編寫如下 systemtap 腳本:

運行輸出如下,一目了然,原來 sm_vars & AD_PORT_LACP_ENABLED (0x2)為假。

Tips:從藍色方框,結合下方參數可以再次驗證了 port 的地址獲取正確。

5、 為什么 sm_vars & AD_PORT_LACP_ENABLED 為假呢,要知道初始化的時候是置位了 AD_PORT_LACP_ENABLED 標志的

顯然是存在邏輯,復位了 sm_vars 的 AD_PORT_LACP_ENABLED 標志位,搜索下代碼:

原來獲取 port 的 speed 和 duplex 影響了 actor_oper_port_key 的值,而 sm_vars 跟 actor_oper_port_key 直接相關,即源頭是 speed 和 duplex 的問題。從系統記錄的 bond 信息也可以看到 speed 和 duplex 是 unknown 的:

5、 為什么 speed 和 duplex 是 unknown 呢。搜下代碼,推斷 bond 的 slave 口獲取其 speed 和 duplex 失敗了:

編寫腳本驗證查看__ethtool_get_link_ksettings 的返回值,由于編譯器優化,systemtap 提示源碼 385 行 不能輸出變量$res 的值:

同樣的方法使用 print_regs,獲取%eax 即__ethtool_get_link_ksettings 返回值:

%eax 是%rax 的低 32 位,因此%eax = 0xffffffa1,這個值是-95,查看 errno 得知,標識的意思是 operation not supported

6、 而 slave 口是前面說的 kni 映射到內核的接口,通過__ethtool_get_link_ksettings 的源碼可知,原來 kni 映射到內核的接口沒有注冊 ethtool_ops 的 get_link_ksettings/get_settings 方法。

至此該問題的根因分析非常明確了。

解決

1、 kni 映射到內核的接口注冊并實現 ethtool_ops 的 get_settings 方法

2、 get_settings 設置 speed 和 duplex

如下:

方法

1、 根據當前問題點,通過 backtrace 梳理流程,然后找到問題點的核心數據結構。

2、 當核心數據結構不能直接訪問時,則反匯編,查看可通過哪些寄存器間接獲取。

3、 對標識核心數據結構的寄存器,確定正確探測位置,確保探測位置和問題點之間寄存器不會改寫,輸出寄存器的值,systemtap 可調用 print_regs 方法。

4、 遞歸迭代步驟 1-3。

備注:

內核版本:4.14.105-1-tlinux3-0007

Dpdk 版本:20.11

最后

所謂工欲善其事必先利其器,systemtap 是我們分析內核,學習內核,非常好的工具,這玩意需多寫多練,才熟能生巧。歡迎各位一起切磋,一起玩 systemtap。

總結

以上是生活随笔為你收集整理的内核调试技巧-逆向寻踪,揭开 LACP 协议流程的神秘面纱的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。