將大型 TCP 封包的分割處理卸載。
網路驅動程式介面規範(NDIS)的小型埠驅動程式可以卸載比網路媒介最大傳輸單位(MTU)更大的大型 TCP 封包的分割作業。 支援分割大型 TCP 封包的 NIC 也必須能夠:
計算包含IP選項之傳送封包的IP總和檢查碼。
計算包含 TCP 選項之傳送封包的 TCP 總和檢查碼。
NDIS 6.0 版和更新版本支援大型傳送卸載第 1 版(LSOv1),類似於 NDIS 5 中的大型傳送卸載(LSO)。x。 NDIS 6.0 版和更新版本也支援大型傳送卸載版本 2(LSOv2),其提供增強型的大型封包分割服務,包括對 IPv6 的支援。
支援 LSOv2 和 LSOv1 的迷你埠驅動程式必須從 NET_BUFFER_LIST 結構頻外 (OOB) 資訊判斷卸除類型。 驅動程式可以使用 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 結構的 Type 成員來判斷驅動程式堆疊是否使用 LSOv2 或 LSOv1,並執行適當的卸除服務。 包含 LSOv1 或 LSOv2 OOB 資料的 NET_BUFFER_LIST 結構中也包含一個 NET_BUFFER 結構。 如需 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO的詳細資訊,請參閱 存取 TCP/IP 卸載 NET_BUFFER_LIST 資訊。
不過,如果迷你埠已收到 OID_TCP_OFFLOAD_PARAMETERS 指示關閉迷你埠上的 LSO 功能,並在迷你埠成功完成該 OID 之後,迷你埠應丟棄所有包含任何非零 LSOv1 或 LSOv2 OOB 資料的 NET_BUFFER_LIST(NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO)。
TCP/IP 傳輸只會卸除符合下列準則的大型 TCP 封包:
封包是 TCP 封包。 TCP/IP 傳輸不會卸載大型 UDP 封包進行分段。
封包必須至少以小埠驅動程式所指定的最小段數目來劃分。 如需詳細資訊,請參閱 報告 NIC 的 LSOv1 TCP-Packet-Segmentation 功能 和 報告 NIC 的 LSOv2 TCP-Packet-Segmentation 功能。
封包不是回送封包。
封包不會透過通道傳送。
卸載大型 TCP 封包以進行分段之前,TCP/IP 傳輸:
- 更新與 NET_BUFFER_LIST 結構相關聯的大型封包分割資訊。 這項資訊是 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 結構,這是 NET_BUFFER_LIST 資訊的一部分,該資訊與 NET_BUFFER_LIST 結構相關聯。 如需 NET_BUFFER_LIST 資訊的詳細資訊,請參閱 存取 TCP/IP 卸除 NET_BUFFER_LIST 資訊。 TCP/IP 傳輸會將 MSS 值設定為最大區段大小。
針對 LSOv1,將大型 TCP 封包的總長度寫入封包的 ip 標頭
Total Length
字段。 總長度包括IP標頭的長度、如果存在則包括IP選項的長度、TCP標頭的長度、如果存在則包括TCP選項的長度,以及TCP有效負載的長度。 針對 LSOv2,將封包 IP 標頭的 [Total Length
] 字段設定為 0。 迷你端口驅動程序應該從 NET_BUFFER_LIST 結構中的第一個 NET_BUFFER 結構的長度來判斷封包的長度。計算 TCP 虛擬標頭的補碼總和,並將此總和寫入 TCP 標頭的
Checksum
字段。 TCP/IP 傳輸會計算虛擬標題中下列欄位的補碼總和:Source IP Address
、Destination IP Address
和Protocol
。 TCP/IP 傳輸提供的虛擬標頭的一的補數和,讓 NIC 可以及早開始計算從大型 TCP 封包中衍生出的每個封包的實際 TCP 校驗和,而無需檢查 IP 標頭。 請注意,RFC 793 規定偽標頭校驗和是透過Source IP Address
、Destination IP Address
、Protocol
和TCP Length
計算。 (TCP 長度是 TCP 標頭的長度加上 TCP 承載的長度。TCP 長度不包含虛擬標頭的長度。不過,由於基礎迷你埠驅動程式和 NIC 會從 TCP/IP 傳輸所傳遞的大型封包產生 TCP 區段,因此傳輸不知道每個 TCP 區段的 TCP 承載大小,因此無法在虛擬標頭中包含 TCP 長度。 相反地,如下所述,NIC 會擴充 TCP/IP 傳輸所提供的虛擬標頭總和檢查碼,以涵蓋每個產生的 TCP 區段的 TCP 長度。將正確的序號寫入 TCP 標頭
Sequence Number
欄位。 序號會識別 TCP 承載的第一個字節。
在 miniport 驅動程式在其 MiniportSendNetBufferLists 或 MiniportCoSendNetBufferLists 函式中取得 NET_BUFFER_LIST 結構之後,可以使用 _Id
呼叫 TcpLargeSendNetBufferListInfo
巨集,以取得 TCP/IP 傳輸所寫入的 MSS 值。
迷你埠驅動程式會從封包的IP標頭取得大型封包的總長度,並使用MSS值將大型TCP封包分割成較小的封包。 每個較小的封包都包含 MSS 或更少的用戶數據字節。 只有從分段的大型封包建立的最後一個封包應包含小於 MSS 的使用者數據位元組。 從區段封包建立的所有其他封包都應該包含 MSS 用戶數據位元組。 如果您未遵循此規則,建立和傳輸不必要的額外封包可能會降低效能。
迷你埠驅動程式會將 MAC、IP 和 TCP 標頭附加到從大型封包衍生的每個區段。 迷你埠驅動程式必須計算這些衍生封包的IP和TCP總和檢查碼。 若要計算衍生自大型 TCP 封包之每個封包的 TCP 總和檢查碼,NIC 會計算 TCP 總和檢查碼的變數部分(針對 TCP 標頭和 TCP 承載),並將此檢查碼加總到 TCP/IP 傳輸計算的虛擬標頭的一補碼總和,再計算總和檢查碼的 16 位一補碼。 如需有關如何計算這類總和檢查碼的詳細資訊,請參閱 RFC 793 和 RFC 1122。
下圖顯示大型封包的分割。
大型 TCP 封包中的 TCP 用戶數據長度應該等於或小於迷你埠驅動程式指派給 MaxOffLoadSize
值的值。 如需 MaxOffLoadSize
值的詳細資訊,請參閱 報告 NIC 的 LSOv1 TCP-Packet-Segmentation 功能 和 報告 NIC 的 LSOv2 TCP-Packet-Segmentation 功能。
當驅動程式發出狀態指示以指出變更 MaxOffLoadSize
值之後,如果驅動程式收到使用先前 MaxOffLoadSize
值的 LSO 傳送要求,則驅動程式不得當機。 相反地,驅動程式可能會失敗傳送要求。
獨立發出狀態指示以報告 MaxOffLoadSize
值變更的中繼驅動程式,必須確保尚未發出狀態指示的基礎迷你埠適配卡不會取得大小大於迷你埠適配卡所報告之 MaxOffLoadSize
值的任何封包。
回應 OID_TCP_OFFLOAD_PARAMETERS 關閉 LSO 服務的迷你埠中繼驅動程式,必須針對 LSO 傳送要求仍可連線到迷你埠驅動程式的小型時間範圍做好準備。
區段封包中 TCP 用戶數據的長度必須小於或等於 MSS。 MSS 是一個 ULONG 值,TCP 傳輸通過使用與 NET_BUFFER_LIST 結構相關聯的 LSO NET_BUFFER_LIST 資訊來傳遞該值。 只有從分段的大型封包建立的最後一個封包應包含小於 MSS 的使用者數據位元組。 從區段封包建立的所有其他封包都應該包含 MSS 用戶數據位元組。 如果您未遵循此規則,建立和傳輸不必要的額外封包可能會降低效能。
衍生自大型 TCP 封包的區段封包數目必須等於或大於迷你埠驅動程式所指定的 MinSegmentCount
值。 如需 MinSegmentCount
值的詳細資訊,請參閱 報告 NIC 的 LSOv1 TCP-Packet-Segmentation 功能 和 報告 NIC 的 LSOv2 TCP-Packet-Segmentation 功能。
下列假設和限制適用於處理任何支援 LSO 的迷你埠驅動程式的 IP 和 TCP 標頭,不論版本為何:
未設定由 TCP/IP 傳輸卸載的大型 TCP 封包的 IP 標頭中的 MF 位,而 IP 標頭中的片段位移將為零。
不會設定大型 TCP 封包 TCP 標頭中的 URG、RST和 SYN 旗標,且 TCP 標頭中的緊急位移(指標)為零。
如果設定大型封包之 TCP 標頭中的 FIN 位,迷你埠驅動程序必須在它從大型 TCP 封包建立的最後一個封包的 TCP 標頭中設定此位。
如果已設定大型 TCP 封包之 TCP 標頭中的 PSH 位,迷你埠驅動程式必須在它從大型 TCP 封包建立的最後一個封包的 TCP 標頭中設定此位。
如果設定大型 TCP 封包之 TCP 標頭中的 CWR 位,迷你埠驅動程式必須在它從大型 TCP 封包建立的第一個封包的 TCP 標頭中設定此位。 迷你埠驅動程式可能會選擇在它從大型 TCP 封包建立的最後一個封包的 TCP 標頭中設定此位,雖然這較不理想。
如果大型 TCP 封包包含 IP 選項或 TCP 選項(或兩者),迷你埠驅動程式會將這些選項複製到衍生自大型 TCP 封包的每個封包。 具體而言,NIC 不會遞增 時間戳 選項。
所有封包標頭(乙太網路、IP、TCP)都會位於封包的第一個 MDL 中。 標題不會分散到多個 MDL 中。
提示
啟用 LSO 時,此假設有效。 否則,如果未啟用 LSO,迷你埠驅動程式無法假設 IP 標頭位於與乙太網路標頭相同的 MDL 中。
迷你埠驅動程式必須按接收自 TCP/IP 傳輸的 NET_BUFFER_LIST 結構的順序,以 NET_BUFFER_LIST 結構傳送封包。
處理大型 TCP 封包時,迷你埠配接器只負責分割封包,並將 MAC、IP 和 TCP 標頭貼到衍生自大型 TCP 封包的封包。 TCP/IP 傳輸會執行所有其他工作(例如根據遠端主機的接收視窗大小調整傳送視窗大小)。
在完成大型封包的傳送作業之前(例如 NdisMSendNetBufferListsComplete 或 NdisMCoSendNetBufferListsComplete),迷你埠驅動程式會將 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 值(關於NET_BUFFER_LIST 的大型傳送卸載資訊)記錄為從大型 TCP 封包建立的所有封包中成功傳送的 TCP 使用者數據位元組總數。
除了先前的 LSO 需求之外,支援 LSOv2 的迷你埠驅動程式也必須:
支援 IPv4 或 IPv6,或同時支援 IPv4 和 IPv6。
支援將 IPv4 選項從大型封包複製到每個由網路介面卡 (NIC) 所產生的區段封包中。
支援從大型 TCP 封包中復寫 IPv6 延伸標頭至每個 TCP 區段封包。
支援迷你埠驅動程序產生之每個 TCP 區段封包中的 TCP 選項複寫。
使用 NET_BUFFER_LIST 結構中的IP和TCP標頭作為範本,為每個區段封包產生TCP/IP標頭。
使用範圍從 0x0000 到 0x7FFF的 IP 識別 (IP ID) 值。 (從0x8000到0xFFFF的範圍保留給支援 TCP 煙囪卸載的裝置)。例如,如果範本IP標頭中標識符字段的值為 0x7FFE,則第一個 TCP 區段封包必須具有 IP 識別碼值 0x7FFE,依次為 0x7FFF、0x0000、0x0001,等等。
使用 TcpHeaderOffset 中的位元組位移NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 成員來判斷 TCP 標頭的位置,從封包的第一個字節開始。
將每個 LSOv2 NET_BUFFER_LIST 結構相關聯的 NET_BUFFER 結構數目限制為一個。
注意
這是支援 LSOv2 的迷你埠驅動程式的新需求。 雖然建議這麼做,但不會明確針對 LSOv1 迷你埠驅動程序強制執行此規則。
從
NET_BUFFER
結構中第一個NET_BUFFER_LIST
結構的長度,判斷封包的總長度。 這與 LSOv1方法驅動程式不同。 支援 TCP 選項、IP 選項和 IP 擴展標頭。
當傳送作業完成時,迷你埠驅動程式必須將
LsoV2TransmitComplete.Reserved
結構的 成員設定為零,並將LsoV2TransmitComplete.Type
成員設定為NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE
。