校驗和解除安裝¶
簡介¶
本文件介紹了 Linux 網路棧中的一組技術,用於利用各種網絡卡的校驗和解除安裝功能。
描述了以下技術
TX 校驗和解除安裝
LCO:本地校驗和解除安裝
RCO:遠端校驗和解除安裝
應該在此處記錄但尚未記錄的內容
RX 校驗和解除安裝
CHECKSUM_UNNECESSARY 轉換
TX 校驗和解除安裝¶
include/linux/skbuff.h 頂部附近的註釋詳細解釋了將傳輸校驗和解除安裝到裝置的介面。
簡而言之,它允許請求裝置填寫由 sk_buff 欄位 skb->csum_start 和 skb->csum_offset 定義的單個 ones-complement 校驗和。 裝置應計算從 csum_start 到資料包末尾的 16 位 ones-complement 校驗和(即“IP 樣式”校驗和),並將結果填寫到(csum_start + csum_offset)中。
由於 csum_offset 不能為負數,這確保了校驗和欄位的先前值包含在校驗和計算中,因此它可以用於提供對校驗和所需的任何更正(例如 UDP 或 TCP 的偽報頭之和)。
此介面僅允許解除安裝單個校驗和。 在使用封裝的情況下,資料包在不同的報頭層中可能具有多個校驗和欄位,其餘的必須由另一種機制(如 LCO 或 RCO)處理。
也可以使用此介面解除安裝 CRC32c,方法是按照上述描述填寫 skb->csum_start 和 skb->csum_offset,並設定 skb->csum_not_inet:有關更多詳細資訊,請參見 skbuff.h 註釋(“D”部分)。
不執行 IP 報頭校驗和的解除安裝;它始終在軟體中完成。 這沒問題,因為當我們構建 IP 報頭時,我們顯然在快取中擁有它,因此對其求和並不昂貴。 而且它也很短。
GSO 的要求更加複雜,因為當分割封裝的資料包時,可能需要為每個生成的分段編輯或重新計算內部和外部校驗和。 有關更多詳細資訊,請參見 skbuff.h 註釋(“E”部分)。
驅動程式在 netdev->hw_features 中宣告其解除安裝功能;有關更多資訊,請參見Netdev 特性混亂以及如何擺脫困境。 請注意,僅通告 NETIF_F_IP[V6]_CSUM 的裝置仍必須遵守 SKB 中給出的 csum_start 和 csum_offset; 如果它嘗試在硬體中自行推斷這些值(某些網絡卡會這樣做),則驅動程式應檢查 SKB 中的值是否與硬體將推斷的值匹配,如果不是,則退回到軟體中的校驗和計算(使用 skb_csum_hwoffload_help() 或 skb_checksum_help() / skb_crc32c_csum_help 函式之一,如 include/linux/skbuff.h 中所述)。
在大多數情況下,堆疊應假定底層裝置支援校驗和解除安裝。 唯一應該檢查的地方是 validate_xmit_skb() 及其直接或間接呼叫的函式。 該函式比較 SKB 請求的解除安裝特性(可能包括 TX 校驗和解除安裝之外的其他解除安裝),如果裝置不支援或未啟用這些特性(由 netdev->features 確定),則在軟體中執行相應的解除安裝。 對於 TX 校驗和解除安裝,這意味著呼叫 skb_csum_hwoffload_help(skb, features)。
LCO:本地校驗和解除安裝¶
LCO 是一種在內部校驗和即將被解除安裝時有效計算封裝資料報的外部校驗和的技術。
正確校驗和的 TCP 或 UDP 資料包的 ones-complement 和等於偽報頭之和的補碼,因為其他所有內容都被校驗和欄位“抵消”了。 這是因為總和在寫入校驗和欄位之前已取反。
更一般地,這適用於使用“IP 樣式” ones-complement 校驗和的任何情況,因此適用於 TX 校驗和解除安裝支援的任何校驗和。
也就是說,如果我們已使用 start/offset 對設定了 TX 校驗和解除安裝,我們知道在裝置填寫該校驗和後,從 csum_start 到資料包末尾的 ones-complement 和將等於我們在校驗和欄位中預先放入的任何值的補碼。 這允許我們計算外部校驗和而無需檢視有效負載:我們只需在到達 csum_start 時停止求和,然後新增 (csum_start + csum_offset) 處 16 位字的補碼。
然後,當真正的內部校驗和被填寫時(無論是透過硬體還是透過 skb_checksum_help()),外部校驗和將透過算術運算變得正確。
當為 VXLAN 或 GENEVE 等封裝構造外部 UDP 報頭時,堆疊會在 udp_set_csum() 中執行 LCO。 IPv6 等效項類似,在 udp6_set_csum() 中。
在 net/ipv4/ip_gre.c:build_header() 中構造 IPv4 GRE 報頭時也會執行此操作。 目前不會在構造 IPv6 GRE 報頭時執行此操作;GRE 校驗和在 net/ipv6/ip6_gre.c:ip6gre_xmit2() 中對整個資料包進行計算,但此處應該可以使用 LCO,因為 IPv6 GRE 仍然使用 IP 樣式的校驗和。
所有 LCO 實現都使用輔助函式 lco_csum(),位於 include/linux/skbuff.h 中。
LCO 可以安全地用於巢狀封裝;在這種情況下,外部封裝層將對它自己的報頭和“中間”報頭求和。 這確實意味著“中間”報頭將被多次求和,但似乎沒有辦法避免這種情況,而不會產生更大的成本(例如,SKB 膨脹)。
RCO:遠端校驗和解除安裝¶
RCO 是一種省略封裝資料報的內部校驗和的技術,允許解除安裝外部校驗和。 但是,它確實涉及對封裝協議的更改,接收器也必須支援該協議。 因此,預設情況下停用它。
RCO 在以下 Internet 草案中詳細說明
在 Linux 中,RCO 在每個封裝協議中單獨實現,並且大多數隧道型別都有控制其使用的標誌。 例如,VXLAN 具有標誌 VXLAN_F_REMCSUM_TX(每個 struct vxlan_rdst),以指示在傳輸到給定的遠端目標時應使用 RCO。