Skip to content

Commit 5d4df52

Browse files
committed
winredirect: simplify Windows verdict handling
1 parent 3a3849d commit 5d4df52

4 files changed

Lines changed: 52 additions & 53 deletions

File tree

internal/winredirect/driver/winredirect.c

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static NTSTATUS TriggerFatal(_In_ PDRIVER_CONTEXT Ctx, _In_ NTSTATUS Status, _In
6161
return previous;
6262
}
6363

64-
static void FailClosedClassify(
64+
static void TriggerFatalAndPermitClassify(
6565
_In_ PDRIVER_CONTEXT Ctx,
6666
_Inout_ FWPS_CLASSIFY_OUT0* classifyOut,
6767
_In_ NTSTATUS Status,
@@ -70,7 +70,7 @@ static void FailClosedClassify(
7070
if (Ctx) {
7171
TriggerFatal(Ctx, Status, Message);
7272
}
73-
BlockClassify(classifyOut);
73+
PermitClassify(classifyOut);
7474
}
7575

7676
static BOOLEAN IsLoopbackAddress(_In_ UINT8 AddressFamily, _In_reads_(16) const UINT8* Address)
@@ -129,7 +129,7 @@ static CONFIG_SNAPSHOT ReadConfigSnapshot(_In_ PDRIVER_CONTEXT Ctx)
129129
return snapshot;
130130
}
131131

132-
static NTSTATUS BestRouteForEntry(
132+
static BOOLEAN TryBestRouteForEntry(
133133
_In_ const CONFIG_SNAPSHOT* Snapshot,
134134
_In_ const PENDING_ENTRY* Entry,
135135
_Out_ BEST_ROUTE_RESULT* Result)
@@ -142,13 +142,13 @@ static NTSTATUS BestRouteForEntry(
142142
NETIO_STATUS status;
143143

144144
if (!Snapshot->HasTunLuid) {
145-
return STATUS_INVALID_DEVICE_STATE;
145+
return FALSE;
146146
}
147147
// GetBestRoute2 requires IRQL < DISPATCH_LEVEL. We do not currently
148148
// characterize every runtime context where route lookup can be unavailable,
149149
// so report a normal lookup failure and let the caller decide the fallback.
150150
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
151-
return STATUS_NOT_SUPPORTED;
151+
return FALSE;
152152
}
153153

154154
RtlZeroMemory(&sourceAddress, sizeof(sourceAddress));
@@ -173,19 +173,19 @@ static NTSTATUS BestRouteForEntry(
173173
sourceAddressPtr = &sourceAddress;
174174
}
175175
} else {
176-
return STATUS_INVALID_PARAMETER;
176+
return FALSE;
177177
}
178178

179179
status = GetBestRoute2(NULL, 0, sourceAddressPtr, &destinationAddress, 0, &bestRoute, &bestSourceAddress);
180-
if (status != STATUS_SUCCESS) {
181-
return status;
180+
if (status != 0) {
181+
return FALSE;
182182
}
183183
if (bestRoute.InterfaceLuid.Value == Snapshot->TunLuid.Value) {
184184
*Result = BestRouteTun;
185-
return STATUS_SUCCESS;
185+
return TRUE;
186186
}
187187
*Result = BestRouteOther;
188-
return STATUS_SUCCESS;
188+
return TRUE;
189189
}
190190

191191
static void CancelPendingIoctlRequests(_In_ PDRIVER_CONTEXT Ctx, _In_ NTSTATUS Status)
@@ -321,6 +321,7 @@ NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING Regi
321321
if (!NT_SUCCESS(status)) { WdfDeviceInitFree(deviceInit); return status; }
322322

323323
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttrs, DRIVER_CONTEXT);
324+
deviceAttrs.ExecutionLevel = WdfExecutionLevelPassive;
324325
status = WdfDeviceCreate(&deviceInit, &deviceAttrs, &device);
325326
if (!NT_SUCCESS(status)) return status;
326327

@@ -397,7 +398,7 @@ void EvtDriverUnload(_In_ WDFDRIVER Driver)
397398
fatalStatus = ReadFatalStatus(g_Ctx);
398399
ShutdownRedirect(
399400
g_Ctx,
400-
fatalStatus != STATUS_SUCCESS ? VERDICT_DROP : VERDICT_BYPASS,
401+
VERDICT_PERMIT,
401402
fatalStatus != STATUS_SUCCESS ? fatalStatus : STATUS_CANCELLED);
402403
}
403404
}
@@ -485,9 +486,9 @@ void EvtIoDeviceControl(
485486

486487
case IOCTL_WINREDIRECT_STOP:
487488
if (fatalStatus != STATUS_SUCCESS) {
488-
ShutdownRedirect(ctx, VERDICT_DROP, fatalStatus);
489+
ShutdownRedirect(ctx, VERDICT_PERMIT, fatalStatus);
489490
} else {
490-
ShutdownRedirect(ctx, VERDICT_BYPASS, STATUS_CANCELLED);
491+
ShutdownRedirect(ctx, VERDICT_PERMIT, STATUS_CANCELLED);
491492
}
492493
WdfRequestComplete(Request, STATUS_SUCCESS);
493494
break;
@@ -525,6 +526,10 @@ void EvtIoDeviceControl(
525526
break;
526527
}
527528
WINREDIRECT_VERDICT* v = (WINREDIRECT_VERDICT*)inBuf;
529+
if (v->Verdict != VERDICT_REDIRECT && v->Verdict != VERDICT_PERMIT) {
530+
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
531+
break;
532+
}
528533
PPENDING_ENTRY entry = PendingFindByID(ctx, v->ConnID);
529534
if (entry) {
530535
ExecuteVerdict(ctx, entry, v->Verdict);
@@ -609,7 +614,7 @@ void EvtTimeoutWorkItem(_In_ WDFWORKITEM WorkItem)
609614
break;
610615
}
611616

612-
ExecuteVerdict(g_Ctx, expired, VERDICT_BYPASS);
617+
ExecuteVerdict(g_Ctx, expired, VERDICT_PERMIT);
613618
ExFreePoolWithTag(expired, 'rniW');
614619
}
615620
}
@@ -633,7 +638,7 @@ void EvtFatalWorkItem(_In_ WDFWORKITEM WorkItem)
633638
return;
634639
}
635640

636-
ShutdownRedirect(g_Ctx, VERDICT_DROP, fatalStatus);
641+
ShutdownRedirect(g_Ctx, VERDICT_PERMIT, fatalStatus);
637642
}
638643

639644
// --- WFP Setup ---
@@ -732,10 +737,6 @@ NTSTATUS WfpSetup(_In_ PDRIVER_CONTEXT Ctx)
732737

733738
void WfpCleanup(_In_ PDRIVER_CONTEXT Ctx)
734739
{
735-
if (Ctx->RedirectHandle) {
736-
FwpsRedirectHandleDestroy0(Ctx->RedirectHandle);
737-
Ctx->RedirectHandle = NULL;
738-
}
739740
if (Ctx->CalloutIdV4) {
740741
FwpsCalloutUnregisterById0(Ctx->CalloutIdV4);
741742
Ctx->CalloutIdV4 = 0;
@@ -744,6 +745,10 @@ void WfpCleanup(_In_ PDRIVER_CONTEXT Ctx)
744745
FwpsCalloutUnregisterById0(Ctx->CalloutIdV6);
745746
Ctx->CalloutIdV6 = 0;
746747
}
748+
if (Ctx->RedirectHandle) {
749+
FwpsRedirectHandleDestroy0(Ctx->RedirectHandle);
750+
Ctx->RedirectHandle = NULL;
751+
}
747752
if (Ctx->EngineHandle) {
748753
FwpmEngineClose0(Ctx->EngineHandle);
749754
Ctx->EngineHandle = NULL;
@@ -795,7 +800,7 @@ static void ClassifyFnCommon(
795800

796801
fatalStatus = ReadFatalStatus(ctx);
797802
if (fatalStatus != STATUS_SUCCESS) {
798-
BlockClassify(classifyOut);
803+
PermitClassify(classifyOut);
799804
return;
800805
}
801806

@@ -832,9 +837,9 @@ static void ClassifyFnCommon(
832837
}
833838

834839
// Allocate pending entry
835-
entry = (PPENDING_ENTRY)ExAllocatePoolZero(PagedPool, sizeof(PENDING_ENTRY), 'rniW');
840+
entry = (PPENDING_ENTRY)ExAllocatePoolZero(NonPagedPoolNx, sizeof(PENDING_ENTRY), 'rniW');
836841
if (!entry) {
837-
FailClosedClassify(ctx, classifyOut, STATUS_INSUFFICIENT_RESOURCES, "allocate pending entry");
842+
TriggerFatalAndPermitClassify(ctx, classifyOut, STATUS_INSUFFICIENT_RESOURCES, "allocate pending entry");
838843
return;
839844
}
840845
entry->ConnID = InterlockedIncrement64(&ctx->NextConnID);
@@ -858,7 +863,7 @@ static void ClassifyFnCommon(
858863
RtlCopyMemory(entry->DstAddr, dstArr->byteArray16, 16);
859864
} else {
860865
ExFreePoolWithTag(entry, 'rniW');
861-
FailClosedClassify(ctx, classifyOut, STATUS_INVALID_ADDRESS_COMPONENT, "ipv6 null destination");
866+
TriggerFatalAndPermitClassify(ctx, classifyOut, STATUS_INVALID_ADDRESS_COMPONENT, "ipv6 null destination");
862867
return;
863868
}
864869
}
@@ -871,16 +876,15 @@ static void ClassifyFnCommon(
871876
return;
872877
}
873878

874-
status = BestRouteForEntry(&snapshot, entry, &bestRoute);
875-
// Windows auto-redirect is best-effort: only redirect connections that are
876-
// positively identified as already routed to the TUN. If route lookup says
877-
// "not TUN" or fails for a context we do not currently characterize, leave
878-
// the original connect alone instead of redirecting or blocking unknown traffic.
879-
if (!NT_SUCCESS(status) || bestRoute == BestRouteOther) {
879+
if (!TryBestRouteForEntry(&snapshot, entry, &bestRoute) || bestRoute == BestRouteOther) {
880880
ExFreePoolWithTag(entry, 'rniW');
881881
PermitClassify(classifyOut);
882882
return;
883883
}
884+
// Windows auto-redirect is best-effort: only redirect connections that are
885+
// positively identified as already routed to the TUN. If route lookup says
886+
// "not TUN" or fails for a context we do not currently characterize, leave
887+
// the original connect alone instead of redirecting unknown traffic.
884888

885889
// Extract PID from metadata
886890
if (FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_PROCESS_ID)) {
@@ -889,15 +893,15 @@ static void ClassifyFnCommon(
889893

890894
if (!classifyContext) {
891895
ExFreePoolWithTag(entry, 'rniW');
892-
FailClosedClassify(ctx, classifyOut, STATUS_INVALID_DEVICE_STATE, "no classify context");
896+
TriggerFatalAndPermitClassify(ctx, classifyOut, STATUS_INVALID_DEVICE_STATE, "no classify context");
893897
return;
894898
}
895899

896900
// Pend the classify
897901
status = FwpsAcquireClassifyHandle0((void*)classifyContext, 0, &classifyHandle);
898902
if (!NT_SUCCESS(status)) {
899903
ExFreePoolWithTag(entry, 'rniW');
900-
FailClosedClassify(ctx, classifyOut, status, "acquire classify handle");
904+
TriggerFatalAndPermitClassify(ctx, classifyOut, status, "acquire classify handle");
901905
return;
902906
}
903907

@@ -910,7 +914,7 @@ static void ClassifyFnCommon(
910914
if (!NT_SUCCESS(status) || !entry->WritableLayerData) {
911915
FwpsReleaseClassifyHandle0(classifyHandle);
912916
ExFreePoolWithTag(entry, 'rniW');
913-
FailClosedClassify(ctx, classifyOut, !NT_SUCCESS(status) ? status : STATUS_INVALID_DEVICE_STATE, "acquire writable layer data");
917+
TriggerFatalAndPermitClassify(ctx, classifyOut, !NT_SUCCESS(status) ? status : STATUS_INVALID_DEVICE_STATE, "acquire writable layer data");
914918
return;
915919
}
916920

@@ -922,7 +926,7 @@ static void ClassifyFnCommon(
922926
FWPS_CLASSIFY_FLAG_REAUTHORIZE_IF_MODIFIED_BY_OTHERS);
923927
FwpsReleaseClassifyHandle0(classifyHandle);
924928
ExFreePoolWithTag(entry, 'rniW');
925-
FailClosedClassify(ctx, classifyOut, status, "pend classify");
929+
TriggerFatalAndPermitClassify(ctx, classifyOut, status, "pend classify");
926930
return;
927931
}
928932

@@ -1038,7 +1042,7 @@ void ExecuteVerdict(_In_ PDRIVER_CONTEXT Ctx, _In_ PPENDING_ENTRY Entry, _In_ UI
10381042
redirectStatus = STATUS_INVALID_DEVICE_STATE;
10391043
} else {
10401044
SOCKADDR_STORAGE* redirectContext =
1041-
(SOCKADDR_STORAGE*)ExAllocatePoolZero(PagedPool, sizeof(SOCKADDR_STORAGE) * 2, 'rniW');
1045+
(SOCKADDR_STORAGE*)ExAllocatePoolZero(NonPagedPoolNx, sizeof(SOCKADDR_STORAGE) * 2, 'rniW');
10421046
if (!redirectContext) {
10431047
redirectStatus = STATUS_INSUFFICIENT_RESOURCES;
10441048
} else {
@@ -1084,7 +1088,7 @@ void ExecuteVerdict(_In_ PDRIVER_CONTEXT Ctx, _In_ PPENDING_ENTRY Entry, _In_ UI
10841088

10851089
if (!NT_SUCCESS(redirectStatus)) {
10861090
TriggerFatal(Ctx, redirectStatus, "execute redirect");
1087-
Verdict = VERDICT_DROP;
1091+
Verdict = VERDICT_PERMIT;
10881092
}
10891093
}
10901094

@@ -1096,13 +1100,8 @@ void ExecuteVerdict(_In_ PDRIVER_CONTEXT Ctx, _In_ PPENDING_ENTRY Entry, _In_ UI
10961100
Entry->WritableLayerData = NULL;
10971101
}
10981102

1099-
if (Verdict == VERDICT_REDIRECT || Verdict == VERDICT_BYPASS) {
1100-
classifyOut.actionType = FWP_ACTION_PERMIT;
1101-
classifyOut.rights &= ~FWPS_RIGHT_ACTION_WRITE;
1102-
} else { // VERDICT_DROP
1103-
classifyOut.actionType = FWP_ACTION_BLOCK;
1104-
classifyOut.rights &= ~FWPS_RIGHT_ACTION_WRITE;
1105-
}
1103+
classifyOut.actionType = FWP_ACTION_PERMIT;
1104+
classifyOut.rights &= ~FWPS_RIGHT_ACTION_WRITE;
11061105

11071106
FwpsCompleteClassify0(Entry->ClassifyHandle, 0, &classifyOut);
11081107
FwpsReleaseClassifyHandle0(Entry->ClassifyHandle);

internal/winredirect/driver/winredirect.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424

2525
// Verdict values
2626
#define VERDICT_REDIRECT 0
27-
#define VERDICT_BYPASS 1
28-
#define VERDICT_DROP 2
27+
#define VERDICT_PERMIT 1
2928

3029
// Shared structures - must match Go types_windows.go layout
3130

internal/winredirect/types_windows.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ const (
1313

1414
const (
1515
VerdictRedirect = 0
16-
VerdictBypass = 1
17-
VerdictDrop = 2
16+
// VerdictPermit allows the original TUN-bound connect to continue
17+
// without local redirection.
18+
VerdictPermit = 1
1819
)
1920

2021
// Config is sent to the driver via IOCTL_SET_CONFIG.

redirect_windows.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,11 @@ func (r *autoRedirect) evaluateConnection(conn *winredirect.PendingConn) uint32
235235

236236
// Proxy process outbound connections must never be redirected back into itself.
237237
if conn.ProcessID == r.selfPID {
238-
return winredirect.VerdictBypass
238+
return winredirect.VerdictPermit
239239
}
240240

241241
if dst.Addr.IsLoopback() {
242-
return winredirect.VerdictBypass
242+
return winredirect.VerdictPermit
243243
}
244244

245245
if !r.tunOptions.EXP_DisableDNSHijack && dst.Port == 53 {
@@ -250,7 +250,7 @@ func (r *autoRedirect) evaluateConnection(conn *winredirect.PendingConn) uint32
250250
ctx := r.newConnContext(metadata)
251251
_, err := r.handler.PrepareConnection(ctx, "tcp", src, dst, nil, 0)
252252
if errors.Is(err, ErrDrop) {
253-
return winredirect.VerdictDrop
253+
return winredirect.VerdictPermit
254254
}
255255
r.redirectServer.connTable.StoreDNS(src, dst, M.SocksaddrFrom(dnsServer, 53), ctx)
256256
return winredirect.VerdictRedirect
@@ -259,22 +259,22 @@ func (r *autoRedirect) evaluateConnection(conn *winredirect.PendingConn) uint32
259259
}
260260

261261
if r.tunOptions.StrictRoute && r.isDisabledFamily(dst.Addr) {
262-
return winredirect.VerdictDrop
262+
return winredirect.VerdictPermit
263263
}
264264

265265
metadata := r.resolveMetadata(conn)
266266
ctx := r.newConnContext(metadata)
267267

268268
_, err := r.handler.PrepareConnection(ctx, N.NetworkTCP, src, dst, nil, 0)
269269
if errors.Is(err, ErrDrop) {
270-
return winredirect.VerdictDrop
270+
return winredirect.VerdictPermit
271271
}
272272
if errors.Is(err, ErrReset) {
273273
// Pending entries reaching userspace here have already been identified as
274-
// TUN-bound by the driver. Bypass means "do not locally redirect to the
274+
// TUN-bound by the driver. Permit means "do not locally redirect to the
275275
// Windows redirect listener"; the original connect continues into the TUN,
276276
// where reset semantics are enforced by the TUN stack.
277-
return winredirect.VerdictBypass
277+
return winredirect.VerdictPermit
278278
}
279279
if errors.Is(err, ErrBypass) && r.logger != nil {
280280
r.logger.Debug("bypass not supported on Windows, redirecting: ", src, " -> ", dst)

0 commit comments

Comments
 (0)