TL;DR:
My transparent proxy was architecturally correct, fully implemented, and thoroughly tested — yet it silently failed on Wi-Fi. The reason wasn’t a bug, a missing flag, or bad code. It was a Windows + Wi-Fi driver boundary that user-mode tools like WinDivert simply cannot cross.
This article documents the journey, the dead ends, the hard evidence, and the final, correct conclusion.
Background
I was building a transparent TCP proxy on Windows using:
- Rust
- WinDivert (user-mode packet capture/injection)
- SOCKS5 upstream
- Full DNAT / SNAT redirection
- No explicit proxy configuration on the client
The goal was simple:
Intercept outbound TCP connections (ports 80/443), transparently redirect them to a local proxy, and forward traffic upstream — without breaking applications or TCP semantics.
This is a solved problem on Linux (iptables + REDIRECT).
On Windows, it’s much harder.
Phase 1: The Wrong Idea (Packet Forging)
The initial design attempted to:
- Intercept outbound SYN packets
- Drop them
- Forge a SYN-ACK pretending to be the real server
- Maintain a user-space TCP state machine
- Relay data via SOCKS5
On paper, everything worked:
- Correct TCP flags
- Correct SEQ/ACK numbers
- Correct IP + TCP checksums
- Verified hex dumps
- Passing unit tests
Reality:
Windows silently dropped every forged SYN-ACK.
No ACK.
No handshake.
No error.
Root Cause
Windows enforces TCP anti-spoofing below WinDivert.
User-mode code cannot inject packets claiming to originate from external IPs and expect the TCP stack to accept them.
This is not configurable.
No WinDivert layer fixes this.
Not Impostor, not REFLECT, not FORWARD.
Conclusion:
Forging TCP peers in user mode on Windows is architecturally impossible.
Phase 2: The Correct Architecture (Redirection)
After discarding packet forging entirely, I implemented the only correct approach:
Transparent Redirection (DNAT / SNAT)
- Intercept outbound SYN
- Record original destination
(dst_ip, dst_port) - Rewrite destination →
LOCAL_IP:8899 - Flip packet direction to INBOUND
- Let Windows complete a real TCP handshake
- Proxy connects upstream via SOCKS5
- Rewrite source on return packets back to original server
- Flip direction to OUTBOUND
- Clean up mappings on FIN/RST
Key rules:
- No forged packets
- No custom TCP state machine
- Windows owns TCP semantics
This is how all real transparent proxies work on Windows.
Everything Was Correct — Yet Nothing Worked
Even with the correct architecture:
-
curlhung - No outbound SYN visible
- WinDivert captured only inbound packets
-
Outbound=1never appeared -
NETWORK_FORWARDlayer captured nothing - Filters were correct
- Injection logic was correct
At this point, the usual suspects were gone.
The Critical Discovery: Wi-Fi Fast-Path
The system used an Intel Wi-Fi adapter.
After exhaustive testing:
- WinDivert never saw outbound TCP packets
- Even with filter
"tcp" - Even after disabling all configurable offloads
- Even after restarting the adapter
- Even at different WinDivert layers
PowerShell confirmed it:
Get-NetAdapterAdvancedProperty -Name "Wi-Fi"
Only WoWLAN offloads existed.
No TCP offload toggles.
No segmentation controls.
What This Means
On this Wi-Fi adapter:
- Outbound TCP packets are constructed below NDIS
- They bypass filter drivers entirely
- They never appear as discrete IP packets
- User-mode tools cannot intercept them
Inbound packets still appear — because they must traverse the receive path.
This is a driver + firmware fast-path, not a bug.
Why Ethernet Works (and Wi-Fi Doesn’t)
Ethernet drivers typically:
- Do not use hard fast-paths
- Expose outbound packets through NDIS
- Allow WinDivert to see and modify traffic
Wi-Fi drivers often:
- Offload TCP construction to firmware
- Skip NDIS filter layers
- Make outbound interception impossible in user mode
This is why:
- Commercial VPNs use kernel-mode WFP drivers
- User-mode tools work reliably on Ethernet
- Wi-Fi support requires deeper hooks
Final Resolution
I chose the most pragmatic and correct next step:
USB-to-Ethernet Adapter
- ~$15
- Zero code changes
- Immediate validation
This isn’t a workaround — it’s crossing a known platform boundary.
On Ethernet:
- Outbound packets appear
- DNAT/SNAT works
- TCP handshake completes
- Proxy exit IP is visible
- Architecture is fully validated
Key Takeaways
1. You cannot forge TCP peers on Windows in user mode
Anti-spoofing is enforced below WinDivert.
2. Transparent proxies must use redirection, not forgery
Let the OS own TCP.
3. WinDivert is correct — but limited by NIC drivers
Especially on Wi-Fi.
4. Absence of packets can be a hardware truth, not a bug
Empirical testing matters more than assumptions.
5. Ethernet validates architecture; Wi-Fi dictates tooling
User mode vs kernel mode is not a preference — it’s a requirement.
When Do You Need WFP?
Only if:
- Transparent interception over Wi-Fi is mandatory
- Production deployment is required
- Kernel development is acceptable
Otherwise:
- User-mode + Ethernet is sufficient
- Architecture is sound
- Complexity stays manageable
Closing
This project did not fail.
It reached a correct architectural conclusion, backed by:
- Clean code
- Empirical testing
- Layer isolation
- Hardware-level evidence
Sometimes the hardest bugs aren’t bugs at all — they’re boundaries.
If this saves you weeks of chasing ghosts, it did its job.
Top comments (0)