ストリームの検査

インライン ストリーム検査

インライン ストリーム修飾子は、classifyFn コールアウト関数から FWP_ACTION_PERMIT または FWP_ACTION_BLOCK を返す FWPS_STREAM_CALLOUT_IO_PACKET0 構造体の countBytesEnforced メンバーの値を設定し、指定されたデータの一部を許可またはブロックすることで、ストリーム データを編集できます。 また、FwpsStreamInjectAsync0 関数を呼び出し、新しいコンテンツをストリームに追加することもできます。 このコンテンツは新しいものにすることも、ブロックされたデータを置き換えるものにすることもできます。

指定されたセグメントの途中で見つかったパターン (たとえば、n バイトの後の p バイトのパターンの後に m バイトが続くパターン) を置き換えるには、コールアウトは次の手順に従います。

  1. コールアウトの classifyFn 関数は、n + p + m バイトを使用して呼び出されます。

  2. コールアウトは、countBytesEnforced メンバーが n に設定された FWP_ACTION_PERMIT を返します。

  3. コールアウトの classifyFn 関数は、p + m バイトで再度呼び出されます。 countBytesEnforced が指定された量より小さい場合、WFP は classifyFn を再度呼び出します。

  4. classifyFn 関数から、コールアウトは FwpsStreamInjectAsync0 関数を呼び出して置換パターン p' を挿入します。 コールアウトは、countBytesEnforcedp に設定された FWP_ACTION_BLOCK を返します。

  5. コールアウトの classifyFn 関数は、m バイトで再度呼び出されます。

  6. コールアウトは、countBytesEnforcedm に設定された FWP_ACTION_PERMIT を返します。

指定されたデータがコールアウトによる検査判定に不十分な場合、FWPS_STREAM_CALLOUT_IO_PACKET0 構造体の streamAction メンバーを FWPS_STREAM_ACTION_NEED_MORE_DATA に設定し、countBytesRequired メンバーに、データが再び示されるまでに WFP が蓄積する必要がある最小量を設定できます。 streamAction が設定されている場合、コールアウトは classifyFn 関数から FWP_ACTION_NONE を返す必要があります。

WFP は、FWPS_STREAM_ACTION_NEED_MORE_DATAが設定されている場合、最大 8 MB のストリーム データを蓄積できます。 WFP は、コールアウトの classifyFn 関数を呼び出し、バッファー領域がなくなると、FWPS_CLASSIFY_OUT_FLAG_BUFFER_LIMIT_REACHED フラグを設定します。 後者のフラグが設定されている場合、コールアウトは指示されたデータを完全に受け入れる必要があります。 FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA フラグが設定されている場合、コールアウトは FWPS_STREAM_ACTION_NEED_MORE_DATA を返してはなりません。

フラット バッファーからストリーム パターンをスキャンできる利便性のため、WFP には指定されたストリーム データを連続したバッファーにコピーできる FwpsCopyStreamDataToBuffer0 ユーティリティ関数が用意されています。

帯域外ストリーム検査

帯域外検査や変更の場合、ストリームコールアウトはパケット検査コールアウトと同様のパターンに従います。最初に、遅延処理のために指定されたすべてのストリーム セグメントを複製してから、それらのセグメントをブロックします。 検査または変更されたデータは、後でデータ ストリームに挿入されます。 帯域外データを挿入する場合、結果のストリームの整合性を保証するために、コールアウトは指定されたすべてのセグメントで FWP_ACTION_BLOCK を返す必要があります。 帯域外検査モジュールは、送信データ ストリームに FIN (送信側からのデータがもうないことを示します) を恣意的に挿入してはなりません。 モジュールが接続を切断する必要がある場合、その classifyFn コールアウト関数は、FWPS_STREAM_CALLOUT_IO_PACKET0 構造体の streamAction メンバーを FWPS_STREAM_ACTION_DROP_CONNECTION に設定する必要があります。

: コールアウトが帯域外からインラインに切り替わることは契約違反であり、予期しない動作が発生する可能性があります。 帯域外コールアウトが指定された各条件を満たしていることを確認します。

ストリーム データは NET_BUFFER_LIST チェーンとして示すことができるため、FWP は、ネット バッファー リスト チェーンで動作する FwpsCloneStreamData0 および FwpsDiscardClonedStreamData0 ユーティリティ関数を提供します。

WFP は、受信方向のストリーム データ スロットリングにも対応しています。 コールアウトが受信データ レートに追いつくことができない場合、ストリームを「一時停止」する FWPS_STREAM_ACTION_DEFER を返すことができます。 その後、FwpsStreamContinue0 関数を呼び出すことでストリームを「再開」できます。 この関数でストリームを遅延させると、TCP/IP スタックは受信データの ACK 処理を停止します。 これにより、TCP スライディング ウィンドウは 0 に向かって減少します。

帯域外ストリーム検査コールアウトの場合、FwpsStreamInjectAsync0 関数が呼び出されている間は、FwpsStreamContinue0 を呼び出してはなりません。

挿入されたストリーム データはコールアウトに再表示されませんが、下位サブレイヤーからのストリーム コールアウトが利用できるようになります。

GitHub の Windows ドライバー サンプル リポジトリにある Windows フィルタリング プラットフォーム ストリーム編集サンプルは、ストリーム レイヤーでインライン編集と帯域外編集を行なう方法を示しています。

: Windows Server 2008 以降では、次に示す処理中のストリーム フィルターの削除はサポートされていません。

  • コールアウトは帯域外パケット挿入を実行しています。

  • コールアウトは、FWPS_STREAM_CALLOUT_IO_PACKET0 構造体の streamAction メンバーを FWPS_STREAM_ACTION_NEED_MORE_DATAに設定することで、より多くのデータを要求しています。

  • コールアウトは、FWPS_STREAM_CALLOUT_IO_PACKET0 構造体の streamAction メンバーを FWPS_STREAM_ACTION_DEFER に設定することでストリームを遅延させます。

動的ストリーム検査

Windows 7 以降では、動的ストリーム検査に対応しています。 動的ストリーム検査は、新しいストリーム データ フローを作成して破棄するのではなく、既存のストリーム データ フローで動作します。 動的ストリーム検査を実行できるコールアウト ドライバーは、FWPS_CALLOUT1 または FWPS_CALLOUT2 構造体の Flags メンバーに FWP_CALLOUT_FLAG_ALLOW_MID_STREAM_INSPECTION フラグを設定する必要があります。

不要な検査の回避

ドライバーが関心を持つ接続に対してのみストリーム検査を実行するために、FWPS_CALLOUT0 構造体の Flags メンバーに FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW フラグを設定できます。 このコールアウトは、他のすべての接続では無視されます。 パフォーマンスが向上し、ドライバーは不要な状態データを維持する必要がなくなります。

ストリーム レイヤー ウォーターフォール モデル

WFP のストリーム レイヤーは、厳密なウォーターフォール モデルに従っています。つまり、このレイヤーのコールアウトは、前のコールアウト (存在する場合) が明示的に許可した場合にのみ、ストリーム セグメントの検査が許可されます。 コールアウトが指定したセグメントをブロックした場合、そのセグメントはストリームから完全に取り除かれ、他のコールアウトが検査することはできなくなります。

さらに:

  1. ストリーム レイヤーのすべての非検査コールアウトは、classifyOut パラメーターの actionType メンバーの以前の値がどのようなものであっても、明示的に値を割り当てる必要があります。
  2. classifyOut パラメーターの rights メンバーの FWPS_RIGHT_ACTION_WRITE フラグは、WFP ストリーム レイヤーでは意味を持ちません。 このレイヤーのコールアウトは、このフラグの有無をチェックすべきではありません。 コールアウトは、classifyOut->rights の値に関係なく、指定された layerData パラメーターを処理できます。