大きな TCP パケットのセグメント化のオフロード
NDIS ミニポート ドライバーは、ネットワーク メディアの最大伝送単位 (MTU) を超える大きな TCP パケットのセグメント化をオフロードできます。 大きな TCP パケットのセグメント化をサポートする NIC では、以下のことが可能である必要があります。
IP オプションを含む送信パケットの IP チェックサムを計算する。
TCP オプションを含む送信パケットの TCP チェックサムを計算する。
NDIS バージョン 6.0 以降では、NDIS 5.x の大きな送信オフロード (LSO) に似た大きな送信オフロード バージョン 1 (LSOv1) がサポートされています。 NDIS バージョン 6.0 以降では、IPv6 のサポートを含む拡張された大きなパケット セグメント化サービスを提供する大きな送信オフロード バージョン 2 (LSOv2) もサポートされています。
LSOv2 と LSOv1 をサポートするミニポート ドライバーは、 NET_BUFFER_LIST 構造の OOB 情報からオフロードの型を決定する必要があります。 ドライバーは、 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 構造の Type メンバーを使用して、ドライバー スタックが LSOv2 または LSOv1 のどちらを使用しているかを判断し、適切なオフロード サービスを実行できます。 LSOv1 または LSOv2 の OOB データを含む NET_BUFFER_LIST 構造にも、1 つの NET_BUFFER 構造が含まれています。 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO の詳細については、 「TCP/IP オフロード NET_BUFFER_LIST 情報へのアクセス」を参照してください。
ただし、ミニポートが OID_TCP_OFFLOAD_PARAMETERS を受信してミニポート上の LSO 機能をオフにした場合、ミニポートが OID を正常に完了した後、 ミニポートは 0 以外の LSOv1 または LSOv2 の OOB データ(NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO)を含むすべての NET_BUFFER_LIST を削除する必要があります。
TCP/IP トランスポートは、以下の条件を満たす 大きな TCP パケットのみをオフロードします。
パケットは TCP パケットです。 TCP/IP トランスポートは、セグメント化のために大きな UDP パケットをオフロードしません。
パケットは、少なくともミニポート ドライバーによって指定されたセグメントの最小数以上で分割可能である必要があります。 詳細については、 「NIC の LSOv1 TCP パケット セグメント化機能の報告」 および 「NIC の LSOv2 TCP パケット セグメント化機能の報告」を参照してください。
パケットはループバック パケットではありません。
パケットはトンネルを経由して送信されません。
セグメント化のために大きな TCP パケットをオフロードする前に、TCP/IP トランスポートは、
- NET_BUFFER_LIST 構造に関連付けられた大きなパケットのセグメント化情報を更新します。 この情報は、 NET_BUFFER_LIST 構造に関連付けられてた NET_BUFFER_LIST 情報の一部である NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 構造です。 NET_BUFFER_LIST 情報の詳細については、 「TCP/IP オフロード NET_BUFFER_LIST 情報へのアクセス」を参照してください。 TCP/IP トランスポートは、 MSS 値を最大セグメント サイズ (MSS) に設定します。
LSOv1 の場合、大きな TCP パケットの合計長をパケットの IP ヘッダーの [Total Length] フィールドに書き込みます。 合計長には、IP ヘッダーの長さ、IP オプションが存在する場合の IP オプションの長さ、TCP ヘッダーの長さ、TCP オプションがある場合はその長さ、および TCP ペイロードの長さが含まれます。 LSOv2 の場合、パケットの IP ヘッダーの [Total Length] フィールドを 0 に設定します。 ミニポート ドライバーは、NET_BUFFER_LIST 構造の最初の NET_BUFFER 構造の長さからパケットの長さを決定する必要があります。
TCP 疑似ヘッダーの 1 の補数の合計を計算し、この合計を TCP ヘッダーのチェックサム フィールドに書き込みます。 TCP/IP トランスポートは、擬似ヘッダーの送信元 IP アドレス、送信先 IP アドレス、プロトコルの各フィールドの補数の合計を計算します。 TCP/IP トランスポートが提供する擬似ヘッダーの 1 の補数の合計により、NIC は IP ヘッダーを調べることなく、大きな TCP パケットから派生する各パケットの実際の TCP チェックサムの計算を早期に開始できます。 RFC 793 では、擬似ヘッダーのチェックサムは、送信元 IP アドレス、送信先 IP アドレス、プロトコル、および TCP 長で計算されると規定されていることに注意してください。 (TCP 長とは、TCP ヘッダーの長さに TCP ペイロードの長さを加えたものです。TCP 長には、擬似ヘッダーの長さは含まれません)。ただし、下位のミニポート ドライバーと NIC は、TCP/IP トランスポートによって渡される大きなパケットから TCP セグメントを生成するため、トランスポートは各 TCP セグメントの TCP ペイロードのサイズを認識しないため、擬似ヘッダーに TCP 長を含めることはできません。 その代わりに、以下で説明するように、NIC は、TCP/IP トランスポートによって提供された擬似ヘッダーのチェックサムを拡張して、生成された各 TCP セグメントの TCP 長さをカバーします。
TCP ヘッダーの [シーケンス番号] フィールドに正しいシーケンス番号を書き込みます。 シーケンス番号は、TCP ペイロードの最初のバイトを識別します。
ミニポート ドライバーは、 MiniportSendNetBufferLists または MiniportCoSendNetBufferLists 関数で NET_BUFFER_LIST 構造を取得した後、 この関数は、 NET_BUFFER_LIST_INFO マクロを TcpLargeSendNetBufferListInfo の _Id で呼び出し、TCP/IP トランスポートによって書き込まれた MSS 値を取得することができます。
ミニポート ドライバーは、パケットの IP ヘッダーから大きなパケットの合計の長さを取得し、MSS 値を使用して、大きな TCP パケットをより小さなパケットに分割します。 小さいパケットそれぞれには、MSS 以下のユーザー データ バイトが含まれます。 セグメント化された大きなパケットから生成された最後のパケットのみが、MSS ユーザー データ バイト未満である可能性があることに注意してください。 セグメント化されたパケットから生成された他のすべてのパケットには、MSS ユーザー データ バイトが含まれている必要があります。 この規則に従わない場合、不要な追加パケットの作成と送信によってパフォーマンスが低下する可能性があります。
ミニポート ドライバーは、大きなパケットから派生した各セグメントに MAC、IP、TCP ヘッダーを付加します。 ミニポート ドライバーは、これらの派生パケットの IP と TCP チェックサムを計算する必要があります。 大きな TCP パケットから派生した各パケットの TCP チェックサムを計算するために、NIC は TCP チェックサムの可変部分 (TCP ヘッダーと TCP ペイロード)を計算し、TCP/IP トランスポートが計算した擬似ヘッダーの 1 の補数の合計にこのチェックサムを加えてから、チェックサム の 16 ビットの 1 の補数を計算します。 このようなチェックサムの計算の詳細については、RFC 793 および RFC 1122 を参照してください。
以下の図は、大きなパケットのセグメント化を示しています。
大きな TCP パケット内の TCP ユーザー データの長さは、ミニポート ドライバーが MaxOffLoadSize 値に割り当てる値以下である必要があります。 MaxOffLoadSize 値の詳細については、 「NIC の LSOv1 TCP パケット セグメント化機能の報告」 と 「NIC の LSOv2 TCP パケット セグメント化機能の報告」を参照してください。
ドライバーが MaxOffLoadSize 値の変更を示すステータス表示を発行した後、ドライバーは以前の MaxOffLoadSize 値を使用する LSO 送信要求を受信しても、クラッシュしてはなりません。 その代わりに、ドライバーは送信要求を失敗させることができます。
MaxOffLoadSize 値の変更を報告するステータス表示を個別に発行する中間ドライバーは、ステータス表示を発行していない下位ミニポート アダプターが、ミニポート アダプターが報告した MaxOffLoadSize 値よりもサイズが大きいパケットを取得しないようにする必要があります。
OID_TCP_OFFLOAD_PARAMETERS に応答して LSO サービスを停止するミニポート中間ドライバーは、LSO 送信要求がミニポート ドライバーに到達する可能性のあるわずかな時間に備える必要があります。
セグメント パケット内の TCP ユーザー データの長さは、MSS 以下である必要があります。 MSS は、 NET_BUFFER_LIST 構造に関連付けられた LSO NET_BUFFER_LIST 情報を使用して TCP トランスポートが渡す ULONG 値です。 セグメント化された大きなパケットから生成された最後のパケットのみが、MSS ユーザー データ バイト未満である可能性があることに注意してください。 セグメント化されたパケットから生成された他のすべてのパケットには、MSS ユーザー データ バイトが含まれている必要があります。 この規則に従わない場合、不要な追加パケットの作成と送信によってパフォーマンスが低下する可能性があります。
大きな TCP パケットから派生したセグメント化パケットの数は、ミニポート ドライバーが指定する MinSegmentCount 値以上である必要があります。 MinSegmentCount 値の詳細については、 「NIC の LSOv1 TCP パケット セグメント化機能の報告」 と 「NIC の LSOv2 TCP パケット セグメント化機能の報告」を参照してください。
バージョンに関係なく、LSO 対応ミニポート ドライバーの IP ヘッダーと TCP ヘッダーの処理には、以下の前提条件と制限が適用されます。
TCP/IP トランスポートがオフロードした大きな TCP パケットの IP ヘッダーの MF ビットはセットされず、IP ヘッダーの フラグメント オフセット は 0 になります。
大きな TCP パケットの TCP ヘッダーの URG、RST、SYN フラグは設定されず、TCP ヘッダーの緊急オフセット (ポインター) は 0 になります。
大きなパケットの 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 パケットを処理する場合、ミニポート アダプターは、パケットをセグメント化し、大きな TCP パケットから派生したパケットに MAC、IP、TCP ヘッダーを付加することだけを担当します。 TCP/IP トランスポートは、他のすべてのタスク (リモート ホストの受信ウィンドウ サイズに基づいて送信ウィンドウ のサイズを調整するなど) を実行します。
大きいパケット (NdisMSendNetBufferListsComplete や NdisMCoSendNetBufferListsComplete など) の送信操作を完了する前に、ミニポート ドライバーは、大きな TCP パケットから作成された、すべてのパケットで正常に送信された TCP ユーザー データ バイトの合計数 (大きな送信オフロードの NET_BUFFER_LIST 情報) を NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 値に書き込みます。
以前の LSO 要件に加えて、LSOv2 対応ミニポート ドライバーには以下の要件があります:
IPv4 か IPv6、もしくは IPv4 と IPv6 の両方をサポートします。
ネットワーク インターフェイス カード (NIC) によって作成される各セグメント パケットで、大きなパケットからの IPv4 オプションの複製をサポートします。
各 TCP セグメント パケットで、大きな TCP パケットからの IPv6 拡張ヘッダーの複製をサポートします。
ミニポート ドライバーが作成する各 TCP セグメント パケットの TCP オプションの複製をサポートします。
NET_BUFFER_LIST 構造の IP ヘッダーと TCP ヘッダーをテンプレートとして使用して、セグメント化された各パケットの TCP/IP ヘッダーを作成します。
IP 識別 (IP ID) 値の範囲は、0x0000 から 0x7FFF です。 (0x8000 から 0xFFFF までの範囲は、TCP チムニー オフロード対応デバイス用に予約されています)。たとえば、テンプレート IP ヘッダーが 0x7FFE の識別フィールド値で始まる場合、最初の TCP セグメント パケットの IP ID 値は 0x7FFE、次に 0x7FFF、0x0000、0x0001 などである必要があります。
NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO の TcpHeaderOffset メンバーのバイト オフセットを使用して、パケットの最初のバイトから始まる TCP ヘッダーの位置を決定します。
各 LSOv2 の NET_BUFFER_LIST 構造に関連付けられた NET_BUFFER 構造の数を 1 つに制限します。
Note
これは、LSOv2 対応ミニポート ドライバーの新しい要件です。 LSOv1 ミニポート ドライバーでは、この規則は明示的に適用されませんが、推奨されます。
NET_BUFFER_LIST 構造の最初の NET_BUFFER 構造の長さからパケットの合計の長さを決定します。 これは、ドライバーが LSOv1 に使用するメソッドとは異なります。
TCP オプション、IP オプション、および IP 拡張機能ヘッダーをサポートします。
送信操作が完了すると、ミニポート ドライバーは、 NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO 構造の LsoV2TransmitComplete.Reserved メンバーを 0 に設定し、 LsoV2TransmitComplete.Type メンバーを NDIS_TCP_LARGE_SEND_OFFLOAD_V2_TYPE に設定する必要があります。