바인딩 또는 연결 리디렉션 사용
WFP(Windows Filtering Platform)의 연결/바인딩 리디렉션 기능을 사용하면 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에서만 사용할 수 있습니다. 이러한 계층에서 분류를 지원하는 설명선 드라이버는 이전 FwpsCalloutRegister0 함수가 아닌 FwpsCalloutRegister1 이상을 사용하여 등록해야 합니다.
Important
리디렉션은 모든 유형의 네트워크 트래픽에서 사용할 수 없습니다. 리디렉션에 지원되는 패킷 유형은 다음 목록에 표시됩니다.
- TCP
- UDP
- 헤더 포함 옵션이 없는 원시 UDPv4
- 원시 ICMP
리디렉션 수행
연결을 리디렉션하려면 설명선 드라이버가 TCP 4 튜플 정보의 쓰기 가능한 복사본을 가져와서 필요에 따라 변경하고 변경 내용을 적용해야 합니다. 쓰기 가능한 계층 데이터를 가져오고 엔진을 통해 적용하기 위해 새 함수 집합이 제공됩니다. 설명선 드라이버는 classifyFn 함수에서 인라인으로 변경하거나 다른 함수에서 비동기적으로 변경할 수 있습니다.
리디렉션을 구현하는 설명선 드라이버는 classifyFn0 대신 classifyFn1 이상을 분류 설명선 함수로 사용해야 합니다. classifyFn1 이상을 사용하려면 이전 FwpsCalloutRegister0이 아닌 FwpsCalloutRegister1 이상을 호출하여 설명선이 등록되어야 합니다.
인라인으로 리디렉션을 수행하려면 설명선 드라이버가 classifyFn 구현에서 다음 단계를 수행해야 합니다.
FwpsRedirectHandleCreate0을 호출하여 TCP 연결을 리디렉션하는 데 사용할 수 있는 핸들을 가져옵니다. 이 핸들은 캐시되고 모든 리디렉션에 사용해야 합니다. (이 단계는 Windows 7 이하에서는 생략됩니다.)
Windows 8 이상에서는 설명선 드라이버에서 FwpsQueryConnectionRedirectState0 함수를 사용하여 연결의 리디렉션 상태를 쿼리해야 합니다. 무한 리디렉션을 방지하려면 이 작업을 수행해야 합니다.
FwpsAcquireClassifyHandle0을 호출하여 후속 함수 호출에 사용할 핸들을 가져옵니다.
FwpsAcquireWritableLayerDataPointer0을 호출하여 classifyFn이 호출된 계층에 대한 쓰기 가능한 데이터 구조를 가져옵니다. writableLayerData 매개 변수를 FWPS_BIND_REQUEST0 또는 FWPS_CONNECT_REQUEST0 계층에 해당하는 구조로 캐스팅합니다.
Windows 8부터 설명선 드라이버가 로컬 서비스로 리디렉션되는 경우 FwpsRedirectHandleCreate0을 호출하여 로컬 프록시 작업을 수행하려면 FWPS_CONNECT_REQUEST0 구조체의 localRedirectHandle 멤버를 채워야 합니다.
필요에 따라 계층 데이터를 변경합니다.
다음 예제와 같이 원래 대상을 로컬 리디렉션 컨텍스트에 저장합니다.
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(…);
다음 예제와 같이 원격 주소를 수정합니다.
// 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));
설명선 드라이버가 로컬 서비스로 리디렉션되는 경우 FWPS_CONNECT_REQUEST0 구조의 localRedirectTargetPID 멤버에서 로컬 프록시 PID를 설정해야 합니다.
설명선 드라이버가 로컬 서비스로 리디렉션되는 경우 FwpsRedirectHandleCreate0에서 반환된 리디렉션 핸들을 FWPS_CONNECT_REQUEST0 구조의 localRedirectHandle 멤버로 설정해야 합니다.
FwpsApplyModifiedLayerData0을 호출하여 데이터에 변경 내용을 적용합니다.
프록시 서비스(사용자 모드 또는 커널 모드일 수 있음)에서는 다음 예제와 같이 리디렉션 레코드 및 컨텍스트를 쿼리해야 합니다.
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
프록시 서비스(사용자 모드 또는 커널 모드일 수 있음)에서는 다음 예제와 같이 프록시 연결 소켓에 리디렉션 레코드를 설정하여 새 아웃바운드 소켓을 만들어야 합니다.
proxySock = WSASocket(…); result = WSAIoctl( proxySock, SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS, redirectRecords, …);
FwpsReleaseClassifyHandle0을 호출하여 2단계에서 얻은 분류 핸들을 해제합니다.
FwpsRedirectHandleDestroy0을 호출하여 1단계에서 가져온 핸들을 삭제합니다.
비동기적으로 리디렉션을 수행하려면 설명선 드라이버가 다음 단계를 수행해야 합니다.
FwpsRedirectHandleCreate0을 호출하여 TCP 연결을 리디렉션하는 데 사용할 수 있는 핸들을 가져옵니다. (이 단계는 Windows 7 이하에서는 생략됩니다.)
Windows 8 이상에서는 설명선 드라이버에서 FwpsQueryConnectionRedirectState0 함수를 사용하여 연결의 리디렉션 상태를 쿼리해야 합니다.
FwpsAcquireClassifyHandle0을 호출하여 후속 함수 호출에 사용할 핸들을 가져옵니다. 이 단계와 2단계와 3단계는 설명선 드라이버의 classifyFn 설명선 함수에서 수행됩니다.
다음 예제와 같이 FwpsPendClassify0을 호출하여 분류를 보류 중인 상태로 전환합니다.
FwpsPendClassify( redirectContext->classifyHandle, 0, &redirectContext->classifyOut); classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
참고 항목
Windows 7을 대상으로 하는 경우 별도의 작업자 함수에서 다음 단계를 수행해야 합니다. Windows 8 이상을 대상으로 하는 경우 classifyFn 내에서 비동기 리디렉션에 대한 모든 단계를 수행하고 5단계를 무시할 수 있습니다.
비동기 처리를 위해 분류 핸들 및 쓰기 가능한 계층 데이터를 다른 함수로 보냅니다. 나머지 단계는 설명선 드라이버의 classifyFn 구현이 아니라 해당 함수에서 수행됩니다.
FwpsAcquireWritableLayerDataPointer0을 호출하여 classifyFn이 호출된 계층에 대한 쓰기 가능한 데이터 구조를 가져옵니다. writableLayerData 매개 변수를 FWPS_BIND_REQUEST0 또는 FWPS_CONNECT_REQUEST0 계층에 해당하는 구조로 캐스팅합니다.
Windows 8부터 설명선 드라이버가 로컬로 리디렉션되는 경우 프록시 작업을 수행하려면 FwpsRedirectHandleCreate0을 호출하여 FWPS_CONNECT_REQUEST0 구조체의 localRedirectHandle 멤버를 채워야 합니다.
다음 예제와 같이 설명선별 컨텍스트 정보를 프라이빗 컨텍스트 구조에 저장합니다.
redirectContext->classifyHandle = classifyHandle; redirectContext->connectRequest = connectRequest; redirectContext->classifyOut = *classifyOut; // deep copy // store original destination IP, port
필요에 따라 계층 데이터를 변경합니다.
FwpsApplyModifiedLayerData0을 호출하여 데이터에 변경 내용을 적용합니다. 다른 설명선이 데이터를 추가로 수정하는 경우 다시 권한을 부여하려면 FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS 플래그를 설정합니다.
다음 예제와 같이 FwpsCompleteClassify0을 호출하여 분류 작업을 비동기적으로 완료합니다.
FwpsCompleteClassify( redirectContext->classifyHandle, 0, &redirectContext->classifyOut); classifyOut->actionType = FWP_ACTION_PERMIT; classifyOut->rights |= FWPS_RIGHT_ACTION_WRITE;
FwpsReleaseClassifyHandle0을 호출하여 1단계에서 얻은 분류 핸들을 해제합니다.
여러 설명선에서 연결 리디렉션 처리
둘 이상의 설명선 드라이버가 동일한 흐름에 대한 연결 리디렉션을 시작할 수 있습니다. 연결 리디렉션을 수행하는 설명선은 다른 요청을 인식하고 적절하게 응답해야 합니다.
설명선이 분류를 보류할 때마다 FWPS_RIGHT_ACTION_WRITE 플래그를 설정해야 합니다. 설명선은 FWPS_RIGHT_ACTION_WRITE 플래그를 테스트하여 설명선이 작업을 반환할 수 있는 권한을 확인해야 합니다. 이 플래그가 설정되지 않은 경우에도 설명선은 이전 설명선에서 반환한 FWP_ACTION_PERMIT 작업을 거부하기 위해 FWP_ACTION_BLOCK 작업을 반환할 수 있습니다.
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 플래그가 설정된 위치를 확인해야 합니다.
FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_PID 리디렉션된 흐름을 담당하는 프로세스에 대한 프로세스 식별자를 포함합니다.
FWPS_METADATA_FIELD_ORIGINAL_DESTINATION 흐름에 대한 원래 대상의 주소를 포함합니다.
FWPS_CONNECT_REQUEST0 구조체에는 localRedirectTargetPID라는 멤버가 포함됩니다. 루프백 연결 리디렉션이 유효하려면 이 필드를 리디렉션된 흐름을 담당하는 프로세스의 PID로 채워야 합니다. 이는 엔진이 FWPS_METADATA_FIELD_LOCAL_REDIRECT_TARGET_ID ALE 권한 부여 연결 계층에서 전달하는 것과 동일한 데이터입니다.
Windows 8부터 프록시 서비스는 프록시 서비스의 원래 엔드포인트에 대해 WSAIoctl을 사용하여 SIO_QUERY_WFP_CONNECTION_REDIRECT_RECORDS 발급하고 IOCTL을 SIO_QUERY_WFP_CONNECTION_REDIRECT_CONTEXT 합니다. 또한 새(프록시) 소켓에서 WSAIoctl을 사용하여 SIO_SET_WFP_CONNECTION_REDIRECT_RECORDS IOCTL을 발급해야 합니다.