struct sk_buff¶
sk_buff 是表示資料包的主要網路結構。
基本sk_buff幾何結構¶
struct sk_buff 本身是一個元資料結構,不包含任何資料包資料。 所有資料都儲存在關聯的緩衝區中。
sk_buff.head 指向主“頭”緩衝區。 頭緩衝區分為兩部分
資料緩衝區,包含標頭,有時還包含有效負載; 這是 skb 中由常用輔助函式(例如
skb_put()或skb_pull())操作的部分;共享資訊 (struct skb_shared_info),其中包含指向 (page, offset, length) 格式的只讀資料的指標陣列。
可選地,skb_shared_info.frag_list 可能會指向另一個 skb。
基本圖可能如下所示
---------------
| sk_buff |
---------------
,--------------------------- + head
/ ,----------------- + data
/ / ,----------- + tail
| | | , + end
| | | |
v v v v
-----------------------------------------------
| headroom | data | tailroom | skb_shared_info |
-----------------------------------------------
+ [page frag]
+ [page frag]
+ [page frag]
+ [page frag] ---------
+ frag_list --> | sk_buff |
---------
dataref和無標頭的skbs¶
傳輸層傳送出它們持有的有效負載 skb 的克隆,以進行重傳。 為了允許堆疊的較低層新增它們的標頭,我們將 skb_shared_info.dataref 分成兩半。 較低的 16 位計數總的引用次數。 較高的 16 位指示有多少引用是僅有效負載的。 skb_header_cloned() 檢查是否允許 skb 新增/寫入標頭。
skb 的建立者(例如,TCP)將其 skb 標記為 sk_buff.nohdr (透過 __skb_header_release())。 從標記的 skb 建立的任何克隆都將使用可用的預留空間填充 sk_buff.hdr_len。 如果只存在一個克隆,則它能夠隨意修改預留空間。 傳輸層中的呼叫順序是
<alloc skb>
skb_reserve()
__skb_header_release()
skb_clone()
// send the clone down the stack
這不是一個非常通用的結構,它取決於傳輸層是否做正確的事情。 實際上,通常只有一個僅有效負載的 skb。 擁有具有不同 hdr_len 長度的多個僅有效負載的 skb 是不可能的。 僅有效負載的 skb 絕不應該離開它們的擁有者。
校驗和資訊¶
堆疊和網路驅動程式之間的校驗和解除安裝的介面如下...
裝置接收的資料包的校驗和¶
校驗和驗證的指示在 sk_buff.ip_summed 中設定。 可能的值是
CHECKSUM_NONE裝置沒有校驗此資料包的校驗和,例如,由於缺乏能力。 資料包包含完整(但未驗證)的校驗和,但 skb->csum 中沒有。 因此,在這種情況下,skb->csum 是未定義的。
CHECKSUM_UNNECESSARY您正在處理的硬體不計算完整的校驗和(如
CHECKSUM_COMPLETE中),但它會解析標頭並驗證特定協議的校驗和。 對於此類資料包,如果它們的校驗和沒問題,則它將設定CHECKSUM_UNNECESSARY。 在這種情況下,sk_buff.csum仍然是未定義的。 驅動程式或裝置絕不能修改資料包中的校驗和欄位,即使校驗和已驗證。CHECKSUM_UNNECESSARY適用於以下協議TCP:IPv6 和 IPv4。
UDP:IPv4 和 IPv6。 裝置可以將 CHECKSUM_UNNECESSARY 應用於 IPv4 或 IPv6 的零 UDP 校驗和,在這種情況下,網路堆疊可能會執行進一步的驗證。
GRE:僅當標頭中存在校驗和時。
SCTP:指示 SCTP 標頭中的 CRC 已驗證。
FCOE:指示 FC 幀中的 CRC 已驗證。
sk_buff.csum_level指示在資料包中找到的連續校驗和的數量(減 1),這些校驗和已驗證為CHECKSUM_UNNECESSARY。 例如,如果裝置接收到 IPv6->UDP->GRE->IPv4->TCP 資料包,並且裝置能夠驗證 UDP(可能為零)、GRE(設定了校驗和標誌)和 TCP 的校驗和,則sk_buff.csum_level將設定為 2。 如果裝置只能驗證 UDP 校驗和,而不能驗證 GRE,可能是因為它不支援 GRE 校驗和或因為 GRE 校驗和錯誤,則 skb->csum_level 將設定為 0(在這種情況下不考慮 TCP 校驗和)。CHECKSUM_COMPLETE這是最通用的方法。 裝置提供
netif_rx()看到的 _整個_ 資料包的校驗和,並填寫sk_buff.csum。 這意味著硬體不需要解析 L3/L4 標頭來實現此目的。注意事項
即使裝置僅支援某些協議,但能夠生成 skb->csum,它也必須使用 CHECKSUM_COMPLETE,而不是 CHECKSUM_UNNECESSARY。
CHECKSUM_COMPLETE 不適用於 SCTP 和 FCoE 協議。
CHECKSUM_PARTIAL設定校驗和以解除安裝到裝置,如 CHECKSUM_PARTIAL 的輸出描述中所述。 這可能發生在直接從另一個 Linux OS 接收的資料包上,例如,同一主機上的虛擬化 Linux 核心,或者它可能在 GRO 或遠端校驗和解除安裝的輸入路徑中設定。 為了校驗和驗證的目的,skb->csum_start + skb->csum_offset 引用的校驗和以及資料包中任何前面的校驗和都被認為是已驗證的。 資料包中位於要解除安裝的校驗和之後的任何校驗和都不被認為是已驗證的。
非GSO的傳輸校驗和¶
堆疊在資料包的 sk_buff.ip_summed 中請求校驗和解除安裝。 值是
CHECKSUM_PARTIAL驅動程式需要校驗 hard_start_xmit() 從
sk_buff.csum_start到結尾看到的資料包的校驗和,並在偏移量sk_buff.csum_start+sk_buff.csum_offset處記錄/寫入校驗和。 驅動程式可能會驗證 csum_start 和 csum_offset 值是否是給定資料包的長度和偏移量的有效值,但它不應嘗試驗證校驗和是否引用合法的傳輸層校驗和 - 驗證 csum_start 和 csum_offset 是否正確設定是堆疊的職責。當堆疊請求資料包的校驗和解除安裝時,驅動程式必須確保正確設定校驗和。 驅動程式可以將校驗和計算解除安裝到裝置,或者呼叫 skb_checksum_help(如果裝置不支援特定校驗和的解除安裝)。
NETIF_F_IP_CSUM和NETIF_F_IPV6_CSUM正被棄用,取而代之的是NETIF_F_HW_CSUM。 新裝置應使用NETIF_F_HW_CSUM來指示校驗和解除安裝能力。 可以呼叫 skb_csum_hwoffload_help() 以基於網路裝置校驗和能力解析CHECKSUM_PARTIAL:如果資料包不匹配它們,則呼叫 skb_checksum_help() 或 skb_crc32c_help()(取決於sk_buff.csum_not_inet的值,請參見 非IP校驗和 (CRC) 解除安裝)來解析校驗和。CHECKSUM_NONEskb 已被協議校驗和,或者不需要校驗和。
CHECKSUM_UNNECESSARY這與輸出上的校驗和解除安裝的 CHECKSUM_NONE 含義相同。
CHECKSUM_COMPLETE未用於校驗和輸出。 如果驅動程式觀察到 skbuff 中設定了此值的資料包,則應將資料包視為設定了
CHECKSUM_NONE。
非IP校驗和(CRC)解除安裝¶
|
此功能指示裝置能夠解除安裝資料包中的 SCTP CRC。 為了執行此解除安裝,堆疊將相應地設定 csum_start 和 csum_offset,將 ip_summed 設定為 |
|
此功能指示裝置能夠解除安裝資料包中的 FCOE CRC。 為了執行此解除安裝,堆疊會將 ip_summed 設定為 |
帶有GSO的輸出校驗和¶
在 GSO 資料包的情況下(skb_is_gso() 為真),校驗和解除安裝由 gso_type 中的 SKB_GSO_* 標誌暗示。 最明顯的是,如果 gso_type 是 SKB_GSO_TCPV4 或 SKB_GSO_TCPV6,則意味著作為 GSO 操作一部分的 TCP 校驗和解除安裝。 如果正在使用 GSO 解除安裝校驗和,則 ip_summed 是 CHECKSUM_PARTIAL,並且 csum_start 和 csum_offset 都設定為引用要解除安裝的最外層校驗和(使用 UDP 封裝可以解除安裝兩個校驗和)。