バインドまたは接続リダイレクトの使用

Windows フィルタリング プラットフォーム (WFP) の接続/バインド リダイレクト機能を使用すると、アプリケーション レイヤー強制 (ALE) コールアウト ドライバーが検査し、必要に応じて接続をリダイレクトすることを可能にします。

この機能は Windows Vista 以降で使用できます。

WFP ドライバー サンプル内の ClassifyFunctions_ProxyCallouts.cpp モジュールには、接続/バインドのリダイレクトを示すコードが含まれています。

WFP 接続リダイレクト コールアウトは、アプリケーションが元の宛先ではなくプロキシ サービスに接続するように、アプリケーションの接続要求をリダイレクトします。 プロキシ サービスには 2 つのソケットがあります: 1 つはリダイレクトされた元の接続用、1 つは新しいプロキシ送信接続用です。

WFP リダイレクト レコードは、リダイレクトされた接続と元の接続が論理的に関連するように、WFP が FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V4 および FWPM_LAYER_ALE_AUTH_CONNECT_REDIRECT_V6 レイヤーの送信プロキシ接続に設定する必要がある不透明なデータのバッファーです。

フローのローカル アドレスとポートの変更は、バインド リダイレクト レイヤーでのみサポートされます。 これは、接続リダイレクト レイヤーではサポートされていません。

リダイレクトに使用されるレイヤー

リダイレクトは、「リダイレクト レイヤー」と呼ばれる次のレイヤーでコールアウト ドライバーによって実行できます。

  • FWPM_LAYER_ALE_BIND_REDIRECT_V4 (FWPS_LAYER_ALE_BIND_REDIRECT_V4)

  • FWPM_LAYER_ALE_BIND_REDIRECT_V6 (FWPS_LAYER_ALE_BIND_REDIRECT_V6)

  • FWPM_LAYER_ALE_CONNECT_REDIRECT_V4 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V4)

  • FWPM_LAYER_ALE_CONNECT_REDIRECT_V6 (FWPS_LAYER_ALE_CONNECT_REDIRECT_V6)

リダイレクトが実行されるレイヤーが、変更の影響を決めます。 接続レイヤーでの変更は、接続されているフローにのみ影響します。 バインド レイヤーでの変更は、そのソケットを使用しているすべての接続に影響します。

リダイレクト レイヤーは、Windows 7 以降のバージョンの Windows でのみ使用できます。 これらのレイヤーで分類をサポートするコールアウト ドライバーは、以前の FwpsCalloutRegister0 関数ではなく、FwpsCalloutRegister1 以降を使用して登録する必要があります。

重要

 リダイレクトは、すべての種類のネットワーク トラフィックで使用できるわけではありません。 リダイレクトでサポートされるパケットの種類を次の一覧に示します:

  • TCP
  • UDP
  • ヘッダー インクルード オプションのない生の UDPv4
  • 生 ICMP

リダイレクトの実行

接続をリダイレクトするには、コールアウト ドライバーが TCP 4 タプル情報の書き込み可能なコピーを取得し、必要に応じて変更を加え、それを適用する必要があります。 新しい一連の関数が提供され、書き込み可能なレイヤー データを取得し、そのデータをエンジンを介して適用することができます。 コールアウト ドライバーは、classifyFn 関数内でインラインで変更を行うオプションと、別の関数を使って非同期的に変更を行うオプションの両方を選択することができます。

リダイレクトを実装するコールアウト ドライバーは、分類コールアウト関数として lassifyFn0 の代わりに classifyFn1 以降を使用する必要があります。 classifyFn1 以降を使用するには、古い FwpsCalloutRegister0 ではなく FwpsCalloutRegister1 以降を呼び出してコールアウトを登録する必要があります。

リダイレクトをインラインで実行するには、コールアウト ドライバーは、classifyFn の実装で次の手順を実行する必要があります:

  1. FwpsRedirectHandleCreate0 を呼び出して、TCP 接続のリダイレクトに使用できるハンドルを取得します。 このハンドルをキャッシュし、すべてのリダイレクトに使用する必要があります。 (Windows 7 以前の場合、この手順は省略されています。)

  2. Windows 8 以降では、コールアウト ドライバーで FwpsQueryConnectionRedirectState0 関数を使用して、接続のリダイレクト状態をクエリする必要があります。 これは、無限リダイレクトを防ぐために行う必要があります。

  3. FwpsAcquireClassifyHandle0 を呼び出して、後続の関数の呼び出しに使用されるハンドルを取得します。

  4. FwpsAcquireWritableLayerDataPointer0 を呼び出して、classifyFn が呼び出されたレイヤーの書き込み可能なデータ構造を取得します。 書き込み可能なLayerDataout パラメーターを、FWPS_BIND_REQUEST0 または FWPS_CONNECT_REQUEST0 のいずれかのレイヤーに対応する構造体にキャストします。

    Windows 8 以降では、コールアウト ドライバーがローカル サービスにリダイレクトする場合、ローカル プロキシを機能させるためには、FwpsRedirectHandleCreate0 を呼び出して、FWPS_CONNECT_REQUEST0 構造体の localRedirectHandle メンバーを入力する必要があります。

  5. 必要に応じて、レイヤー データに変更を加えます:

    1. 次の例に示すように、元の宛先をローカル リダイレクト コンテキストに保存します:

      FWPS_CONNECT_REQUEST* connectRequest = redirectContext->connectRequest;
      // Replace "..." with your own redirect context size
      connectRequest->localRedirectContextSize = ...;
      // Store original destination IP/Port information in the localRedirectContext member
      connectRequest->localRedirectContext =    ExAllocatePoolWithTag(…);
      
    2. 次の例に示すように、リモート アドレスを変更します:

      // Ensure we don't need to worry about crossing any of the TCP/IP stack's zones
      if(INETADDR_ISANY((PSOCKADDR)&(connectRequest->localAddressAndPort)))
      {
         INETADDR_SETLOOPBACK((PSOCKADDR)&(connectRequest->remoteAddressAndPort));
      }
      else
      {
         INETADDR_SET_ADDRESS((PSOCKADDR)&(connectRequest->remoteAddressAndPort),
                               INETADDR_ADDRESS((PSOCKADDR)&(connectRequest->localAddressAndPort)));
      }
      INETADDR_SET_PORT((PSOCKADDR)&connectRequest->remoteAddressAndPort,
                        RtlUshortByteSwap(params->proxyPort));
      
    3. コールアウト ドライバーがローカル サービスにリダイレクトしている場合は、FWPS_CONNECT_REQUEST0 構造体の localRedirectTargetPID メンバーにローカル プロキシ PID を設定する必要があります。

    4. コールアウト ドライバーがローカル サービスにリダイレクトしている場合は、fwpsRedirectHandleCreate0 によって返されるリダイレクト ハンドルを、FWPS_CONNECT_REQUEST0 構造体の localRedirectHandle メンバーに設定する必要があります。

  6. FwpsApplyModifiedLayerData0 を呼び出して、データに加えられた変更を適用します。

  7. プロキシ サービス (ユーザー モードまたはカーネル モードの場合があります) では、次の例に示すようにリダイレクト レコードとコンテキストに対してをクエリする必要があります:

    BYTE* redirectRecords;
    BYTE redirectContext[CONTEXT_SIZE];
    listenSock = WSASocket(…);
    result = bind(listenSock, …);
    result = listen(listenSock, …);
    clientSock = WSAAccept(listenSock, …);
    // opaque data to be set on proxy connection
    result = WSAIoctl(clientSock,
                      SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDS,
                      redirectRecords, …);
    // callout allocated data, contains original destination information
    result = WSAIoctl(clientSock,
                      SIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT,
                      redirectContext, …);
    // extract original destination IP and port from above context
    
  8. プロキシ サービス (ユーザー モードまたはカーネル モードの場合があります) では、次の例に示すように、プロキシ接続ソケットにリダイレクト レコードを設定して、新しい送信ソケットを作成する必要があります:

    proxySock = WSASocket(…);
    result = WSAIoctl(
                 proxySock,
                 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS,
                 redirectRecords, …);
    
  9. FwpsReleaseClassifyHandle0 を呼び出して、手順 2 で取得した分類ハンドルを解放します。

  10. FwpsRedirectHandleDestroy0 を呼び出して、手順 1 で取得したハンドルを破棄します。

リダイレクトを非同期的に実行するには、コールアウト ドライバーは、次の手順を実行する必要があります:

  1. FwpsRedirectHandleCreate0 を呼び出して、TCP 接続のリダイレクトに使用できるハンドルを取得します。 (Windows 7 以前の場合、この手順は省略されています。)

  2. Windows 8 以降では、コールアウト ドライバーで FwpsQueryConnectionRedirectState0 関数を使用して、接続のリダイレクト状態をクエリする必要があります。

  3. FwpsAcquireClassifyHandle0 を呼び出して、後続の関数の呼び出しに使用されるハンドルを取得します。 このステップおよびステップ 2 および 3 は、コールアウト ドライバーの classifyFn コールアウト関数で実行します。

  4. FwpsPendClassify0 を呼び出して、次の例に示すように、分類を保留状態にします:

    FwpsPendClassify(
            redirectContext->classifyHandle,
            0,
            &redirectContext->classifyOut);
    classifyOut->actionType = FWP_ACTION_BLOCK;
    classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
    

Note

Windows 7 を対象としている場合は、別の worker 関数で次の手順を実行する必要があります。 Windows 8 以降を対象としている場合は、classifyFn内から非同期リダイレクトのすべての手順を実行し、手順 5 を無視できます。

  1. 分類ハンドルと書き込み可能なレイヤー データを別の関数に送信し、非同期処理を行います。 残りの手順は、コールアウト ドライバーの classifyFn の実装ではなく、その関数内で実行します。

  2. FwpsAcquireWritableLayerDataPointer0 を呼び出して、classifyFn が呼び出されたレイヤーの書き込み可能なデータ構造を取得します。 書き込み可能なLayerDataout パラメーターを、FWPS_BIND_REQUEST0 または FWPS_CONNECT_REQUEST0 のいずれかのレイヤーに対応する構造体にキャストします。

    Windows 8 以降では、コールアウト ドライバーがローカルにリダイレクトする場合、ローカル プロキシを機能させるためには、FwpsRedirectHandleCreate0 を呼び出して、FWPS_CONNECT_REQUEST0 構造体の localRedirectHandle メンバーを入力する必要があります。

  3. 次の例に示すように、コールアウト固有のコンテキスト情報をプライベート コンテキスト構造に格納します:

    redirectContext->classifyHandle = classifyHandle;
    redirectContext->connectRequest = connectRequest;
    redirectContext->classifyOut = *classifyOut; // deep copy
    // store original destination IP, port
    
  4. 必要に応じて、レイヤー データに変更を加えます。

  5. FwpsApplyModifiedLayerData0 を呼び出して、データに加えられた変更を適用します。 別のコールアウトがデータをさらに変更する場合に再承認を受けたい場合は、FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS フラグを設定します。

  6. 次の例に示すように、FwpsCompleteClassify0 を呼び出して、分類操作を非同期的に完了します:

    FwpsCompleteClassify(
            redirectContext->classifyHandle,
            0,
            &redirectContext->classifyOut);
    classifyOut->actionType = FWP_ACTION_PERMIT;
    classifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
    
  7. FwpsReleaseClassifyHandle0 を呼び出して、手順 1 で取得した分類ハンドルを解放します。

複数のコールアウトからの接続リダイレクトの処理

複数のコールアウト ドライバーが同じフローの接続リダイレクトを開始する可能性があります。 接続リダイレクトを実行するコールアウトは、他の要求を認識し、適切に応答する必要があります。

コールアウトが分類を保留にするたびに、FWPS_RIGHT_ACTION_WRITE フラグを設定する必要があります。 コールアウトは、FWPS_RIGHT_ACTION_WRITE フラグをテストして、コールアウトがアクションを返す権限を確認する必要があります。 このフラグが設定されていない場合でも、コールアウトは、FWP_ACTION_BLOCK アクションを返して前のコールアウトによって返された FWP_ACTION_PERMIT アクションを拒否することができます。

Windows 8 以降では、コールアウト ドライバーは、FwpsQueryConnectionRedirectState0 関数を使用して、接続のリダイレクト状態をクエリする必要があります (コールアウト ドライバーまたは別のコールアウト ドライバーが変更したかどうかを確認するため)。 接続がコールアウト ドライバーによってリダイレクトされた場合、または以前にコールアウト ドライバーによってリダイレクトされた場合、コールアウト ドライバーは何も行いません。 それ以外の場合は、次の例に示すように、ローカル リダイレクトも確認する必要があります:

FwpsAcquireWritableLayerDataPointer(...,(PVOID*)&connectRequest), ...);
if(connectRequest->previousVersion->modifierFilterId != filterId)
{
    if(connectRequest->previousVersion->localRedirectHandle)
    {
        classifyOut->actionType = FWP_ACTION_PERMIT;
        classifyOut->rights &= FWPS_RIGHT_ACTION_WRITE;
        FwpsApplyModifiedLayerData(
                classifyHandle,
                (PVOID)connectRequest,
                FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS);
    }
}

接続先がローカル プロキシである場合、コールアウト ドライバーはリダイレクトを試行しないでください。

接続リダイレクトを使用するコールアウト ドライバーは、ALE 承認接続レイヤー (FWPS_LAYER_ALE_AUTH_CONNECT_V4またはFWPS_LAYER_ALE_AUTH_CONNECT_V6) に登録し、FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED フラグが設定されていることを示すために、次の 2 つのメタデータ値を確認する必要があります:

  • FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID には、リダイレクトされたフローを担当するプロセスのプロセス識別子が含まれています。

  • FWPS_METADATA_FIELD_ORIGINAL_DESTINATION には、フローの元の宛先のアドレスが含まれます。

FWPS_CONNECT_REQUEST0 構造体には、localRedirectTargetPID と呼ばれるメンバーが含まれています。 ループバック接続リダイレクトを有効にするには、このフィールドにリダイレクトされたフローを担当するプロセスの PID を入力する必要があります。 これは、エンジンが ALE 承認接続レイヤーで渡すデータであり、FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_ID と同じデータです。

Windows 8 以降では、プロキシ サービスは、WSAIoctl を使用して、プロキシ サービスの元のエンドポイントに対して SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDS およびSIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT IOCTL を発行する必要があります。 さらに、WSAIoctl を使用して、新しい (プロキシされた) ソケット上で SSIO_SET_WFP_CONNECTION_REDIRECT_RECORDS IOCTL を発行する必要があります。

WFP バージョンに依存しない名称と特定の Windows バージョンを対象