Educational DPI bypass tool. Runs on Linux and Windows.
whyDPI is a transparent TLS proxy (Linux) / packet-level TLS shaper (Windows) combined with an optional DNS forwarder. It ships zero hard-coded hostnames, domains or ISP-specific resolvers: what works for a given destination is discovered at runtime, cached per-SNI, and refined when conditions change.
whyDPI is research and education software. It does not ship a list of destinations to unblock — the operator (you) decides every address it ever touches. That neutrality makes the tool useful for network studies and equally easy to misuse, so before you install, please take five minutes to read
DISCLAIMER.md. In short:
- It is not a way around parental-control, school or corporate-policy filters.
- It is not a shield for accessing content that is unlawful in your jurisdiction regardless of transport (CSAM, NCII, court- ordered takedowns, unlicensed copyrighted material…).
- Running it elevates your attack surface (kernel-level packet interception, DNS hijacking) — only install from the official channels listed below.
- Legal responsibility for every packet whyDPI shapes is yours; the authors, maintainers and packagers carry none of it.
By running whyDPI you acknowledge
DISCLAIMER.mdin full.The graphical tray shows a one-time acceptable-use dialog the first time it starts (until you click I have read and accept). Automation may set
WHYDPI_SKIP_DISCLAIMER=1— documented for CI only, not for skipping consent on a personal machine.
- Netfilter hijack — iptables/ip6tables REDIRECT sends outbound TCP/443 to a local transparent proxy. A small set of rules also blocks QUIC (UDP/443) so browsers fall back to TCP, and (optionally) redirects UDP+TCP/53 to a local DoH stub resolver.
- ClientHello shaping — the proxy reads the complete ClientHello, reassembling it into one record when the client split it across several TLS records (modern Chrome does this with its large post-quantum + Encrypted-ClientHello hello), identifies the SNI, then applies a fragmentation strategy before forwarding the bytes upstream.
- Adaptive discovery — for each SNI the proxy resolves every
upstream address (client choice first, then DNS siblings), probes
passthrough on unseen hosts, then races fragmentation strategies in
parallel. When the client-chosen address is unusable — the TCP layer
never comes up, or every strategy on it is reset/dropped identically
(an IP-range block rather than SNI-based DPI) — discovery rotates onto
alternate addresses. Those alternates are gathered from the configured
DoH resolvers as well as the system resolver, so a CDN anycast range the
local resolver hides (and that the ISP is not blocking) is surfaced and
tried, preferring addresses on a different network prefix than the one
that just failed. Winners are persisted to
/run/whydpi/strategies.json. A cachedpassthroughverdict records only that passthrough worked, not the address it worked on, so when a later connection's own target turns out to be range-blocked the proxy falls back into discovery and rotates onto a reachable address instead of giving up. - DoH forwarding — the optional DNS stub forwards every query as a
DoH POST to a user-configured resolver IP. The DoH connection itself
transits the TLS proxy, so DNS traffic inherits the same fragmentation.
Because the whole mechanism depends on seeing the SNI, the stub also
answers HTTPS/SVCB (type 65 / 64) queries with NODATA by default
(
dns.neutralize_ech), withholding the advertised ECH config so every client — Chrome included — emits a cleartext SNI rather than routing an Encrypted ClientHello to the CDN's public-name edge. This keys on record type only, never on a hostname, so it stays site-free.
A strategy is a tuple (layer, offset):
| Spec | Meaning |
|---|---|
record:N |
Re-frame the ClientHello as two TLS records, split at payload byte N |
record:sni-mid |
Same, but split in the middle of the SNI extension |
record:half |
Same, split at the payload midpoint |
tcp:sni-mid |
Keep one TLS record, split the TCP send at the SNI midpoint |
chunked:N |
Split the raw bytes into N-byte TCP chunks |
decoy:N |
Windows-only packet layer. Inject a spoofed ClientHello for an innocuous SNI with IP TTL = N so it is dropped before reaching the server, polluting middlebox state before the real handshake. Requires WinDivert. |
passthrough |
Forward unchanged (probed first for new SNIs; final fallback in discovery) |
Two AUR packages are published:
whydpi— stable, built from the latest release tag (recommended)whydpi-git— tracks themainbranch, always bleeding-edge
paru -S whydpi # stable
# or: paru -S whydpi-git # bleeding-edge
sudo systemctl enable --now whydpiBinary .deb attached to each GitHub release (tested on Debian 12,
Ubuntu 22.04 and Ubuntu 24.04).
# Download the latest release's .deb for your distro:
curl -L -o whydpi.deb \
"https://github.com/byrdltd/whyDPI/releases/latest/download/whydpi_$(curl -s https://api.github.com/repos/byrdltd/whyDPI/releases/latest | grep tag_name | cut -d\" -f4 | sed s/^v//)-1_ubuntu24.04_all.deb"
sudo apt install ./whydpi.deb
sudo systemctl enable --now whydpiAvailable slugs on each release: debian12, ubuntu24.04, ubuntu22.04.
A future Launchpad PPA will bring apt auto-updates.
Grab the latest .rpm from the
Releases page (builds for
Fedora 40 and 41 are attached), then:
sudo dnf install ./whydpi-*.noarch.rpm
sudo systemctl enable --now whydpiA future Fedora COPR repo will bring dnf auto-updates.
git clone https://github.com/byrdltd/whyDPI.git
cd whyDPI
sudo ./install.shTwo installation options, pick whichever fits your workflow:
Installer (recommended) — double-click whydpi-*-setup.exe from the
Releases page. The
installer registers Start-menu shortcuts and offers an optional
"Start whyDPI automatically when I sign in to Windows" checkbox
(unchecked by default). Enabling it creates a per-user Task Scheduler
entry that re-uses the admin token you already granted during install,
so the tray launches silently at each login without re-prompting for
UAC. You can toggle the same behaviour anytime from the tray's
Launch on login menu entry.
Scoop — no account, no manual download, auto-update on scoop update:
scoop bucket add whydpi https://github.com/byrdltd/whyDPI
scoop install whydpiEither way, launching whyDPI triggers a single UAC prompt (the tray
needs admin rights to load the WinDivert kernel driver and to rewrite
adapter DNS via netsh). Right-click the tray icon → Start
whyDPI, browse, done.
The Windows build uses packet-layer TLS fragmentation rather than a userspace proxy; behaviour is otherwise identical to the Linux build (per-SNI strategy discovery, session-only cache, clean shutdown).
whyDPI Windows binaries are not yet code-signed (Authenticode signing requires a paid certificate we haven't sourced for this educational project). Because of that, both UAC and SmartScreen flag a fresh download with a warning:
- UAC: "Unknown publisher. Do you want to allow this app to make changes to your device?" — click Yes to continue.
- SmartScreen: "Windows protected your PC" — click More info then Run anyway.
Only install binaries downloaded from the official GitHub Releases
page linked above. Every release ships a SHA256SUMS.txt asset;
you can verify your download with:
Get-FileHash .\whydpi-<version>-setup.exe -Algorithm SHA256
# use the exact filename you downloaded; match the line in SHA256SUMS.txtA future release will introduce a signed build (tracking [issue #TBD]) which will silence both warnings. Until then, hash verification is the authoritative integrity check.
whyDPI reads ~/.config/whydpi/config.toml at startup. All values are
optional; env vars (WHYDPI_*) and CLI flags override the file. A fully
explicit example:
[dns]
mode = "doh" # "doh" | "altport" | "off"
doh_endpoint_ip = "1.1.1.1"
doh_endpoint_path = "/dns-query"
doh_fallback_ip = "9.9.9.9"
stub_address = "127.0.0.53"
neutralize_ech = true # answer HTTPS/SVCB with NODATA so the SNI stays in the clear
[tls]
default_strategy = "record:2"
fallback_strategies = [
"record:2", "record:1", "record:sni-mid",
"tcp:sni-mid", "record:half", "chunked:40",
"decoy:5", "decoy:3", "decoy:7", # Windows-only, ignored on Linux
]
decoy_sni = "www.example.com" # innocuous SNI used by decoy:* (Windows)
probe_timeout_s = 3.0
success_min_bytes = 6
[net]
ipv6_enabled = true
block_quic = true# start (optionally pin /etc/resolv.conf to the stub)
sudo whydpi start --configure-dns
# stop and remove rules
sudo whydpi stop
# inspect the per-SNI strategy cache
sudo whydpi cache list
sudo whydpi cache clear
sudo whydpi cache forget example.org
# stand-alone diagnostic: report the strategy each target needs
sudo whydpi probe example.org example.net
# DNS resolver
sudo whydpi dns-configure
sudo whydpi dns-restoreLinux
- Python 3.10+ (
tomllib; on 3.10 installtomliviarequirements.txt) iptablesoriptables-nft(IPv6 rules needip6tables)- Root privileges
Windows
- Windows 10 1809 or later, or Windows 11 (x64)
- Administrator rights (UAC prompt at launch; WinDivert driver +
netshboth require elevation) - Nothing to pre-install; the installer bundles Python,
pydivertand the signed WinDivert 2.x driver.
- No hostnames are shipped in code. The DoH endpoint is an IP. The SNI cache only contains hosts you have visited.
- A success in
whydpi probemeans the upstream produced a valid TLS handshake reply — not an HTTP 200. Middlebox block pages and RSTs are rejected explicitly. - IPv6 HTTPS is fully proxied. Disable with
net.ipv6_enabled = falseif the upstream breaks IPv6.
For educational and research purposes only. Use only where you are authorized to test, and comply with applicable laws and network policies.
