分段解除安裝

簡介

本文件描述了 Linux 網路堆疊中的一組技術,用於利用各種網絡卡的分段解除安裝功能。

描述了以下技術
  • TCP 分段解除安裝 - TSO

  • UDP 分片解除安裝 - UFO

  • IPIP、SIT、GRE 和 UDP 隧道解除安裝

  • 通用分段解除安裝 - GSO

  • 通用接收解除安裝 - GRO

  • 部分通用分段解除安裝 - GSO_PARTIAL

  • 使用 GSO 加速 SCTP - GSO_BY_FRAGS

TCP 分段解除安裝

TCP 分段允許裝置將單個幀分段成多個幀,資料有效負載大小在 skb_shinfo()->gso_size 中指定。當請求 TCP 分段時,應在 skb_shinfo()->gso_type 中設定 SKB_GSO_TCPV4 或 SKB_GSO_TCPV6 的位,並且 skb_shinfo()->gso_size 應設定為非零值。

TCP 分段依賴於對部分校驗和解除安裝的支援。 因此,如果給定裝置的 Tx 校驗和解除安裝被停用,則通常會停用 TSO。

為了支援 TCP 分段解除安裝,有必要填充 skbuff 的網路和傳輸標頭偏移量,以便裝置驅動程式能夠確定 IP 或 IPv6 標頭以及 TCP 標頭的偏移量。 此外,由於需要 CHECKSUM_PARTIAL,csum_start 還應指向資料包的 TCP 標頭。

對於 IPv4 分段,我們支援 IP ID 方面的兩種型別之一。 預設行為是為每個段遞增 IP ID。 如果指定了 GSO 型別 SKB_GSO_TCP_FIXEDID,那麼我們將不遞增 IP ID,並且所有段將使用相同的 IP ID。 如果裝置設定了 NETIF_F_TSO_MANGLEID,則在執行 TSO 時可以忽略 IP ID,我們將遞增所有幀的 IP ID,或者根據驅動程式首選項將其保留為靜態值。

UDP 分片解除安裝

UDP 分片解除安裝允許裝置將超大的 UDP 資料報分片成多個 IPv4 片段。 UDP 分片解除安裝的許多要求與 TSO 相同。 但是,片段的 IPv4 ID 不應遞增,因為單個 IPv4 資料報被分片。

UFO 已棄用:現代核心將不再生成 UFO skb,但仍然可以從 tuntap 和類似裝置接收它們。 仍然支援基於 UDP 的隧道協議的解除安裝。

IPIP、SIT、GRE、UDP 隧道和遠端校驗和解除安裝

除了上述解除安裝之外,幀還可以包含其他標頭,例如外部隧道。 為了解決此類例項,引入了一組額外的分段解除安裝型別,包括 SKB_GSO_IPXIP4、SKB_GSO_IPXIP6、SKB_GSO_GRE 和 SKB_GSO_UDP_TUNNEL。 這些額外的分段型別用於識別存在多個標頭集的情況。 例如,在 IPIP 和 SIT 的情況下,我們應該將網路和傳輸標頭從標準標頭列表移動到“內部”標頭偏移量。

目前僅支援兩個級別的標頭。 慣例是將隧道標頭稱為外部標頭,而封裝的資料通常稱為內部標頭。 以下是訪問給定標頭的呼叫列表

IPIP/SIT 隧道

           Outer                  Inner
MAC        skb_mac_header
Network    skb_network_header     skb_inner_network_header
Transport  skb_transport_header

UDP/GRE 隧道

           Outer                  Inner
MAC        skb_mac_header         skb_inner_mac_header
Network    skb_network_header     skb_inner_network_header
Transport  skb_transport_header   skb_inner_transport_header

除了上述隧道型別之外,還有 SKB_GSO_GRE_CSUM 和 SKB_GSO_UDP_TUNNEL_CSUM。 這兩種額外的隧道型別反映了外部標頭還請求在外部標頭中包含非零校驗和的事實。

最後,還有 SKB_GSO_TUNNEL_REMCSUM,它指示給定的隧道標頭已請求遠端校驗和解除安裝。 在這種情況下,內部標頭將保留部分校驗和,並且僅計算外部標頭校驗和。

通用分段解除安裝

通用分段解除安裝是一種純軟體解除安裝,旨在處理裝置驅動程式無法執行上述解除安裝的情況。 GSO 中發生的情況是,給定的 skbuff 將其資料分解為多個 skbuff,這些 skbuff 已調整大小以匹配透過 skb_shinfo()->gso_size 提供的 MSS。

在啟用任何硬體分段解除安裝之前,需要在 GSO 中進行相應的軟體解除安裝。 否則,可能會在裝置之間重新路由幀,最終無法傳輸。

通用接收解除安裝

通用接收解除安裝是 GSO 的補充。 理想情況下,由 GRO 組裝的任何幀都應進行分段,以使用 GSO 建立相同的幀序列,並且 GSO 分段的任何幀序列都應能夠透過 GRO 重新組裝回原始幀。 唯一的例外是 IPv4 ID,在這種情況下,為給定的 IP 標頭設定了 DF 位。 如果 IPv4 ID 的值未按順序遞增,則當透過 GRO 組裝的幀透過 GSO 分段時,它將被更改為按順序遞增。

部分通用分段解除安裝

部分通用分段解除安裝是 TSO 和 GSO 的混合體。 它的有效作用是利用 TCP 和隧道的某些特性,因此不必為每個段重寫資料包標頭,而只需更新最內層的傳輸標頭,並且可能需要更新最外層的網路標頭。 這允許不支援隧道解除安裝或帶校驗和的隧道解除安裝的裝置仍然可以使用分段。

對於部分解除安裝,發生的情況是,除了內部傳輸標頭之外的所有標頭都會更新,以便它們包含如果標頭只是重複的正確值。 唯一的例外是外部 IPv4 ID 欄位。 裝置驅動程式有責任保證在給定標頭未設定 DF 位的情況下,IPv4 ID 欄位會遞增。

使用 GSO 加速 SCTP

SCTP - 儘管缺乏硬體支援 - 仍然可以利用 GSO 透過網路堆疊傳遞一個大資料包,而不是多個小資料包。

這需要一種不同於其他解除安裝的方法,因為 SCTP 資料包不能僅僅分段到 (P)MTU。 相反,塊必須包含在 IP 段中,並遵守填充。 因此,與常規 GSO 不同,SCTP 不能只生成一個大的 skb,將 gso_size 設定為分片點並將其傳遞到 IP 層。

相反,SCTP 協議層構建一個 skb,其中段已正確填充並存儲為連結的 skb,並且 skb_segment() 基於這些 skb 進行拆分。 為了發出此訊號,gso_size 設定為特殊值 GSO_BY_FRAGS。

因此,核心網路堆疊中的任何程式碼都必須意識到 gso_size 可能是 GSO_BY_FRAGS,並適當地處理這種情況。

有一些助手可以簡化此操作

  • skb_is_gso(skb) && skb_is_gso_sctp(skb) 是檢視 skb 是否為 SCTP GSO skb 的最佳方式。

  • 對於大小檢查,skb_gso_validate_*_len 系列助手會正確考慮 GSO_BY_FRAGS。

  • 對於操作資料包,skb_increase_gso_size 和 skb_decrease_gso_size 將檢查 GSO_BY_FRAGS,如果要求操作這些 skb,則發出 WARN。

這也影響設定了 NETIF_F_FRAGLIST & NETIF_F_GSO_SCTP 位的驅動程式。 另請注意,NETIF_F_GSO_SCTP 包含在 NETIF_F_GSO_SOFTWARE 中。