使用绑定或连接重定向

Windows 筛选平台 (WFP) 的连接/绑定重定向功能使应用程序层强制 (ALE) 标注驱动程序来检查连接,并根据需要重定向连接。

此功能在 Windows 7 及更高版本中可用。

注意WFP 驱动程序示例中的 ClassifyFunctions_ProxyCallouts.cpp 模块包含演示连接/绑定重定向的代码。

WFP 连接重定向标注会重定向应用程序的连接请求,以便应用程序连接到代理服务而不是原始目标。 代理服务有两个套接字:一个用于重定向的原始连接,一个用于新的代理出站连接。

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。 在这些层支持分类的标注驱动程序必须使用 FwpsCalloutRegister1 或更高版本注册,而不是使用较旧的 FwpsCalloutRegister0 函数。

重要

 重定向不适用于所有类型的网络流量。 以下列表显示了支持重定向的数据包类型:

  • TCP
  • UDP
  • 没有标头包含选项的原始 UDPv4
  • 原始 ICMP

执行重定向

若要重定向连接,标注驱动程序必须获取 TCP 4 元组信息的可写副本,根据需要对其进行更改,并应用更改。 提供了一组新函数来获取可写层数据并通过引擎应用这些数据。 标注驱动程序可以选择在其 classifyFn 函数中以内联方式或在另一个函数中异步进行更改。

实现重定向的标注驱动程序必须使用 classificationFn1 或更高版本,而不是 classificationFn0 作为其分类标注函数。 若要使用 classifyFn1 或更高版本,必须通过调用 FwpsCalloutRegister1 或更高版本(而不是旧版 FwpsCalloutRegister0)注册标注。

若要执行内联重定向,标注驱动程序必须在实现 classifyFn 时执行以下步骤:

  1. 调用 FwpsRedirectHandleCreate0 以获取可用于重定向 TCP 连接的句柄。 此句柄应缓存并用于所有重定向。 (Windows 7 及更早版本省略此步骤。)

  2. 在 Windows 8 及更高版本中,必须使用标注驱动程序中的 FwpsQueryConnectionRedirectState0 函数查询连接的重定向状态。 必须执行此操作以防止无限重定向。

  3. 调用 FwpsAcquireClassifyHandle0 以获取将用于后续函数调用的句柄。

  4. 调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData out 参数强制转换为与层对应的结构( FWPS_BIND_REQUEST0FWPS_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. 如果标注驱动程序重定向到本地服务,则应在 FWPS_CONNECT_REQUEST0 结构的 localRedirectHandle 成员中设置 FwpsRedirectHandleCreate0 返回的重定向句柄。

  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;
    

注意

如果面向 Windows 7,则必须在单独的辅助角色函数中执行以下步骤。 如果面向Windows 8或更高版本,则可以从 classifyFn 中执行异步重定向的所有步骤,并忽略步骤 5。

  1. 将分类句柄和可写层数据发送到另一个函数进行异步处理。 其余步骤在该函数中执行,而不是在标注驱动程序的 classifyFn 实现中执行。

  2. 调用 FwpsAcquireWritableLayerDataPointer0 以获取调用 classifyFn 的层的可写数据结构。 将 writableLayerData out 参数强制转换为与层对应的结构( FWPS_BIND_REQUEST0FWPS_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_V4FWPS_LAYER_ALE_AUTH_CONNECT_V6) ,并检查以下两个元数据值来指示设置FWP_CONDITION_FLAG_IS_CONNECTION_REDIRECTED标志:

  • 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_RECORDSSIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT IOCTL。 此外,必须在新的 (代理) 套接字上使用 WSAIoctl 颁发SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS IOCTL。

WFP Version-Independent Windows 的特定版本的名称和目标