UDP 分割卸載(USO,UDP Segmentation Offload)
Windows 10 版本 2004 和更新版本中支援的 UDP 區段卸載(USO)是一項功能,允許網路介面卡(NIC)卸載大於網路媒介最大傳輸單位(MTU)的 UDP 數據報的分割。 如此一來,Windows 會減少與每個封包 TCP/IP 處理相關聯的 CPU 使用率。 USO 的需求類似於 大型發送卸載第 2 版(LSOv2),這適用於 TCP 傳輸協定。
USO 的要求
本節主要是指 NDIS 通訊協定和迷你埠驅動程式。 修改或傳送封包時,NDIS 輕量型篩選驅動程式 (LFS) 必須遵循通訊協定驅動程式需求,也可以假設提供給其 FilterSendNetBufferLists 處理程式的任何封包都符合通訊協定驅動程式需求。
迷你埠驅動程式可以卸載大型 UDP 封包的分割,這些封包大於網路媒體的 MTU。 支援分割大型 UDP 封包的 NIC 也必須能夠執行下列動作:
- 計算包含 IPv4 選項的傳送中封包的 IP 檢查碼
- 計算已傳送封包的 UDP 檢核碼
支援 USO 的迷你埠驅動程式必須從 NET_BUFFER_LIST 結構的頻外 (OOB) 資訊判斷卸除類型。 如果 NDIS_UDP_SEGMENTATION_OFFLOAD_NET_BUFFER_LIST_INFO 結構的值為非零值,則迷你埠驅動程序必須執行 USO。 任何包含 USO OOB 資料的 NET_BUFFER_LIST
也包含單一 NET_BUFFER 結構。 不過,當迷你埠驅動程式收到 OID_TCP_OFFLOAD_PARAMETERS 以關閉 USO 時,當迷你埠驅動程式成功完成此 OID 任務後,應該拒絕並回傳任何已設置 USO OOB 欄位的 NET_BUFFER_LIST
。
TCP/IP 傳輸只會卸除符合下列準則的 UDP 封包:
- 封包是 UDP 封包。
- 封包長度必須大於
(MSS) * (MinSegmentCount - 1)
的最大區段大小。 - 如果迷你連接埠驅動程式未設定
SubMssFinalSegmentSupported
功能,則傳輸所卸載的每個大型 UDP 封包都必須有Length % MSS == 0
。 也就是說,大封包會被分割成 N 個封包,每個封包區段正好包含 MSS 使用者位元組。 如果迷你埠驅動程式設定SubMssFinalSegmentSupported
功能,則傳輸上的這個封包長度可除條件不適用。 換句話說,最終區段可以小於 MSS。 - 封包不是回送封包。
- 在 TCP/IP 傳輸卸除之大型 UDP 封包的 IP 標頭中,MF 位將不會被設定,而 片段位移 將為零。
- 應用程式已指定
UDP_SEND_MSG_SIZE
/WSASetUdpSendMessageSize。
卸除大型 UDP 封包以進行分割之前,TCP/IP 傳輸會執行下列動作:
- 更新與 NET_BUFFER_LIST 結構相關聯的大型封包分割資訊。 這項資訊是 NDIS_UDP_SEGMENTATION_OFFLOAD_NET_BUFFER_LIST_INFO 結構,是
NET_BUFFER_LIST
結構 OOB 資訊的一部分。 TCP/IP 傳輸會將 MSS 值設定為所需的 MSS。 - 計算 UDP 虛擬標頭的 補碼總和,並將此總和寫入 UDP 標頭的
Checksum
字段。 TCP/IP 傳輸會計算偽首部中下列欄位的補數和:來源 IP 位址、目的地 IP 位址和協定。
TCP/IP 傳輸所提供虛擬標頭的一的補碼加總,能讓 NIC 不需檢查 IP 標頭,就能提早開始計算從大型 UDP 封包中衍生的每個封包的實際 UDP 總和。
請注意,RFC 768 和 RFC 2460 規定,虛擬標頭是透過來源 IP 位址、目的地 IP 位址、通訊協定和 UDP 長度計算的(UDP 標頭的長度加上 UDP 承載的長度,不包括虛擬標頭的長度)。 不過,由於基礎迷你埠驅動程式和 NIC 會從 TCP/IP 傳輸所傳遞的大型封包產生 UDP 數據報,因此傳輸不知道每個 UDP 數據報的 UDP 承載大小,因此無法在虛擬header 計算中包含 UDP 長度。 相反地,如下一節所述,NIC 會擴充由 TCP/IP 傳輸提供的偽頭檢查碼,以涵蓋每個生成的 UDP 數據報的 UDP 長度。
重要
如果 TCP/IP 傳輸所提供的 UDP 標頭總和檢查碼欄位為零,NIC 不應該執行 UDP 總和檢查碼計算。
使用USO傳送封包
當迷你埠驅動程式在 MiniportSendNetBufferLists 回呼函式中取得 NET_BUFFER_LIST 之後,可以透過具 _Id
UdpSegmentationOffloadInfo
的 NET_BUFFER_LIST_INFO 巨集來呼叫,以獲取 MSS 值和 IP 協定。
迷你埠驅動程式會從第一個 NET_BUFFER 結構的長度取得大型封包的總長度,並使用 MSS 值,將大型 UDP 封包分割成較小的 UDP 封包。 每個較小的封包都包含 MSS 或較少的用戶數據位元組。 只有從分段大型封包建立的最後一個封包應包含少於 MSS 用户数据字節。 從區段封包建立的所有其他封包都必須包含 MSS 用戶數據位元組。 如果迷你埠驅動程式不符合此規則,則 UDP 數據報未正確傳遞。 如果迷你埠驅動程式未設定 SubMssFinalSegmentSupported
功能,則封包長度會除以 MSS,而每個區段封包都包含 MSS 使用者位元組。
迷你埠驅動程式會將 MAC、IP 和 UDP 標頭加到衍生自大型封包的每個區段。 迷你埠驅動程式必須計算這些衍生封包的IP和UDP總和檢查碼。 要計算從大型 UDP 封包衍生的每個封包的 UDP 檢查碼,NIC 會計算 UDP 檢查碼的變數部分(針對 UDP 標頭和 UDP 負載),然後將此檢查碼加到 TCP/IP 傳輸層已計算的虛擬標頭的反碼總和中,再計算該檢查碼的 16 位反碼。 如需計算這類總和檢查碼的詳細資訊,請參閱 RFC 768 和 RFC 2460。
大型 UDP 封包中的 UDP 用戶數據長度必須小於或等於迷你埠驅動程式指派給 MaxOffLoadSize
的值。
當驅動程式發出狀態指示以指出變更為 MaxOffLoadSize
之後,即使收到使用先前 MaxOffLoadSize
值的 LSO 傳送要求,驅動程式也不得引發系統錯誤檢查。 相反地,驅動程式必須失敗傳送要求。 驅動程式 必須 拒絕任何因為各種原因(包括大小、最小區段數量、IP 選項等等)而無法執行的傳送要求。 如果驅動程式的功能變更,則必須儘快傳送狀態指示。
獨立發出狀態指示,回報 MaxOffLoadSize
值變更的中繼驅動程式,必須確保尚未發出狀態指示的基礎迷你埠適配卡不會取得大於迷你埠配接器所報告之 MaxOffLoadSize
值的任何封包。
針對回應 OID_TCP_OFFLOAD_PARAMETERS 以關閉 USO 服務的迷你埠中繼驅動程式,必須準備應對在短暫時間內 USO 請求仍可能到達迷你埠驅動程式的情況。
衍生自大型 UDP 封包的分割封包數目必須等於或大於迷你埠驅動程式所指定的 MinSegmentCount
值。
處理大型 UDP 封包時,迷你埠驅動程式只負責分割封包,並將 MAC、IP 和 UDP 標頭貼到衍生自大型 UDP 封包的封包。 如果迷你埠無法傳送至少一個區段封包,NBL 最終必須以失敗狀態完成。 迷你埠可以繼續傳送後續封包,但不需要這麼做。 NBL 無法返回到 NDIS,直到所有分段封包都傳輸完成或失敗為止。
支援 USO 的迷你連接埠驅動程式也必須執行下列動作:
- 支援 IPv4 和 IPv6。
- 支援在 NIC 產生的每個區段封包中複寫自大型封包的 IPv4 選項。
- 使用 NET_BUFFER_LIST 結構中的IP和UDP標頭作為範本,為每個區段封包產生UDP和IP標頭。
- 使用範圍從 0x0000 到 0xFFFF的IP識別(IP ID) 值。 例如,如果範本IP標頭開頭的標識符域值為 0xFFFE,則第一個UDP數據報封包的值必須是 0xFFFE,後面接著 0xFFFF、0x0000、0x0001等等。
- 如果大型 UDP 封包包含 IP 選項,迷你埠驅動程式會將這些選項不作更動地複製到由大型 UDP 封包衍生的每個封包中。
- 使用 NDIS_UDP_SEGMENTATION_OFFLOAD_NET_BUFFER_LIST_INFO
UdpHeaderOffset
成員中的位元組偏移量來判斷 UDP 標頭的位置,從封包的第一個位元組開始。 - 根據封包分段增加傳輸統計數據。 例如,包含每個封包區段的乙太網路、IP 和 UDP 標頭位元組的計數,以及封包計數是以 MSS大小的
區段數目計算,而不是 1 。 - 根據每個分割的數據報大小,設定 UDP 總長度和 IP 長度字段。
NDIS 介面變更
本節介紹 NDIS 6.83 的變更,這些變更使主機 TCP/IP 協定堆疊能夠利用小型端口驅動程式所公開的 USO 功能。
NDIS 和迷你埠驅動程序會執行下列動作:
- 公告 NIC 支援 USO 功能
- 啟用或停用 USO
- 取得目前的 USO 功能狀態
廣告 USO 功能
迷你埠驅動程式會填入 NDIS_OFFLOAD 結構的 UdpSegmentation
字段來公告 USO 功能,其會傳入 NdisMSetMiniportAttributes的參數中。
NDIS_OFFLOAD
結構中的 Header.Revision
欄位必須設定為 NDIS_OFFLOAD_REVISION_6,而且 Header.Size
欄位必須設定為 NDIS_SIZEOF_NDIS_OFFLOAD_REVISION_6
。
查詢 USO 狀態
您可以使用 OID_TCP_OFFLOAD_CURRENT_CONFIG查詢目前的 USO 狀態。 NDIS 會處理此 OID,且不會將其傳遞至迷你埠驅動程式。
變更 USO 狀態
您可以使用 OID_TCP_OFFLOAD_PARAMETERS來啟用或停用 USO。 迷你埠驅動程序處理 OID 之後,它必須傳送一個 NDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG 狀態指示,並更新其卸載狀態。
USO 關鍵詞
USO 列舉關鍵詞如下所示:
*UsoIPv4
*UsoIPv6
這些值描述是否為該特定IP通訊協定啟用或停用USO。 USO 設定並不相依於 NDIS_TCP_IP_CHECKSUM_OFFLOAD 組態。 例如,停用 *UDPChecksumOffloadIPv4
並不會隱含停用 *UsoIPv4
。
子鍵名稱 | 參數描述 | 價值 | 列舉描述 |
---|---|---|---|
*UsoIPv4 |
USO (IPv4) | 0 | 禁用 |
1 | 啟用 | ||
*UsoIPv6 |
USO(IPV6) | 0 | 禁用 |
1 | 啟用 |