# Phantun: turning UDP into fake TCP to get past Layer 3 and Layer 4 firewalls

> Phantun is a Rust obfuscator that wraps a UDP stream in TCP-looking packets so it survives stateful NAT and firewall devices. It is not a VPN, it will not pass an L7 proxy, and it needs kernel forwarding plus NAT rules before it does anything.

**dndx/phantun** — Transforms UDP stream into (fake) TCP streams that can go through Layer 3 & Layer 4 (NAPT) firewalls/NATs.

- Repository: https://github.com/dndx/phantun
- Stars: 2,402 · Forks: 225
- Language: Rust
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/dndx-phantun

## The problem Phantun solves: UDP that is blocked but TCP that is not

Many networks allow TCP and quietly drop or throttle UDP. That breaks WireGuard, game traffic, QUIC and anything else that assumes a datagram transport. Phantun's answer is to make a UDP stream look like a TCP connection to the devices in the middle without actually becoming one. The README describes it as a project that "obfuscated UDP packets into TCP connections" and says it is commonly used "in environments where UDP is blocked/throttled but TCP is allowed through".

The intended user is an operator with control of both ends: a client behind a restrictive network and a server with a public address. Phantun is not a consumer app. It creates TUN interfaces, so the machine running it must have IPv4 and IPv6 forwarding enabled and firewall rules that NAT between the physical NIC and the TUN address. The README's own framing is useful here: the client behaves like a machine with a private address behind a router, and the server behaves like a server with a private address that needs its listening port DNATed from the outside. If that mental model does not match your setup, Phantun is the wrong tool.

## How the fake TCP stack works and why it keeps UDP semantics

Phantun does not terminate your UDP traffic into a real TCP socket. It converts a stream of UDP packets into obfuscated TCP stream packets using its own TCP stack, designed to satisfy the state tracking that Layer 3 and Layer 4 firewalls and NAT devices perform. The README states plainly that it will not pass through L7 proxies, because those inspect more than connection state.

The payoff is stated as a design property: the common UDP-over-TCP performance killers, retransmissions and flow control, do not occur, and UDP properties such as out-of-order delivery are preserved even though the connection looks like TCP to the middleboxes. That is a deliberate trade. You get firewall traversal, you do not get reliability. If your application already handles loss and reordering, that is fine. If it assumed a reliable byte stream, Phantun does not give it one.

Both sides create a TUN interface. The README gives the default addresses: the client assigns itself 192.168.200.2 and fcc8::2, the server 192.168.201.2 and fcc9::2. The interface name and addresses can be changed, and the executables print the available options under -h. IPv6 is supported on both the TCP and UDP sides, with bracketed address syntax such as [::1]:1234 and AAAA record resolution.

## Installing Phantun and getting a first tunnel running

The repository is a Cargo workspace with two members, fake-tcp and phantun, so a source build is a normal Rust build. There is no install section in the README that lists a package manager command; the release page is where prebuilt binaries live, including MIPS builds that the release notes describe as built with a nightly toolchain on a best effort basis because Rust only provides Tier 3 support for those platforms. Top-level directories include debian/, rpm/, docker/ and selinux/, which indicates packaging work exists in the tree, but the README does not document installing through them.

Start by enabling forwarding, which the README lists as step one:

```bash
# add net.ipv4.ip_forward=1 to /etc/sysctl.conf, then:
sudo sysctl -p /etc/sysctl.conf
```

For IPv6 you also need net.ipv6.conf.all.forwarding=1. Next, NAT. The README's client example masquerades traffic leaving the physical interface, and the nftables form is given as:

```
table inet nat {
    chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
        iifname tun0 oif eth0 masquerade
    }
}
```

The iptables equivalent is `iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE` plus the ip6tables version. On the server, the listening TCP port is DNATed to the TUN address, for example `iif eth0 tcp dport 4567 dnat ip to 192.168.201.2` and the matching `dnat ip6 to fcc9::2` rule. Replace eth0 and 4567 with your real interface and port.

Then run the two binaries with the addresses from the README's example: the server listens on 4567 and forwards to a UDP server at 127.0.0.1:1234, and the client listens on 127.0.0.1:1234 and connects to 10.0.0.1:4567. The README also notes an optional step for running the binaries as non-root, and the repository ships an selinux directory, but the README does not spell out the policy contents.

## MTU overhead and the WireGuard calculation

Encapsulation costs bytes, and Phantun has a dedicated MTU overhead section because the cost is not zero. The README includes an MTU calculation for WireGuard, which is the common pairing: WireGuard inside Phantun means the outer header plus Phantun's own overhead must fit under the path MTU. Get this wrong and you see the familiar symptoms of a tunnel that connects but stalls on larger packets, which is easy to misdiagnose as a firewall problem when it is really fragmentation.

The README does not hand you a single number to subtract. It gives the calculation, and the calculation depends on your outer transport. Treat the MTU budget as something you verify on the actual path rather than copy from a blog post. This is also where the fake TCP design shows a second cost: because the stack is not doing retransmission, a dropped encapsulated packet is simply lost, and the outer path MTU interacts with that in ways a normal TCP tunnel would paper over.

## Where Phantun fails, and where it is simply the wrong layer

The clearest limitation is stated by the project itself: Phantun will not pass through L7 proxies. If your obstacle is a proxy that inspects application payloads or enforces protocol conformance, obfuscating the transport does nothing. That is a boundary of the approach, not a bug to wait out.

The second limitation follows from the same design. Because retransmissions and flow control are deliberately absent, Phantun does not repair a lossy path. It preserves out-of-order delivery and does not hide loss from the application. On a path with heavy packet loss, a real TCP tunnel would retransmit and appear smoother; Phantun will not. If your application cannot tolerate loss, Phantun is the wrong tool regardless of whether the firewall traversal works.

Third, the operational surface is real. Kernel forwarding must be on, NAT rules must be correct on both ends, and the TUN interface must exist with the expected addresses before any of it works. The README's own analogy is that you are configuring a router. Anyone expecting a single command that just works will spend their first hour in sysctl and nftables. There is also a version compatibility section, which implies that mixing client and server releases is a thing you have to check rather than assume.

## Phantun compared with udp2raw, and what the README does not cover

The README has a section comparing Phantun to udp2raw, so the project names its neighbour directly. Both exist to carry UDP through networks that block it. The difference the README draws is about how much work the wrapper does: Phantun's stated aim is maximum performance with minimum processing and encapsulation overhead, and its TCP stack is built to satisfy firewall and NAT state rather than to be a faithful TCP implementation. udp2raw, by contrast, is the older and more widely deployed option in this space, and the README's comparison section is where the project argues its own position.

What the README does not settle is day-to-day operational parity: it does not document rollback, does not document a management or metrics interface, and does not describe what happens to established connections when you restart a daemon. Packaging directories exist for deb, rpm, docker and selinux, but the README does not walk through any of them. If your deployment depends on those, you are reading the repository files, not the documentation.

## Licence, maintenance and the cost of staying current

Phantun is Apache-2.0 at the repository level, and the tree also contains a LICENSE-MIT file, which suggests the usual dual-licence arrangement for a Rust project. The README's licence section is the place to confirm which terms apply to which crate before you redistribute anything; this is a description of the files present, not legal advice.

The repository was last pushed on 2026-08-22 and is not archived. Releases are not frequent: v0.8.1 and v0.8.0 both landed in August 2025, and v0.7.0 before that in November 2024. That cadence matters for upgrade planning. The README includes a version compatibility section, so client and server are not guaranteed to interoperate across arbitrary versions, and upgrading one end without the other is a risk you should treat as real. Budget for upgrading both ends together, and read the release notes for the pair you are moving between.

## Conclusion

Adopt Phantun when UDP is blocked or throttled on a path where TCP still gets through and both endpoints are machines you control with root access: enable forwarding, add the SNAT or DNAT rules, then run the phantun_server and phantun_client binaries. Do not adopt it if the path crosses an L7 proxy, if you cannot edit sysctl and firewall rules, or if you need retransmission and flow control on the tunnel itself, which Phantun deliberately does not provide. Before deploying, verify the MTU budget on the real path, confirm the client and server versions are compatible, and check whether your kernel and interface setup match the tun0 examples the README assumes.

## FAQ

### What is Phantun used for?

It converts a stream of UDP packets into obfuscated TCP packets so the traffic can pass through Layer 3 and Layer 4 firewalls and NAT devices that block or throttle UDP. The README describes it as commonly used where UDP is blocked but TCP is allowed through.

### Does Phantun work through an L7 proxy?

No. The README states that Phantun will not be able to pass through L7 proxies, because its TCP stack is designed for Layer 3 and Layer 4 stateful and stateless firewalls and NAT devices.

### Does Phantun retransmit lost packets or apply flow control?

No. The README says the common UDP over TCP performance killers such as retransmissions and flow control do not occur, and that UDP properties such as out-of-order delivery are fully preserved even though the connection looks like TCP to firewalls and NAT devices.

### What addresses does Phantun assign to its TUN interface by default?

The client assigns itself 192.168.200.2 and fcc8::2, and the server assigns 192.168.201.2 and fcc9::2. The README notes that the interface name and assigned addresses can be customized, and that running the executable with -h shows how.

### Is Phantun written in Rust?

Yes. The README says Phantun is written in 100 percent safe Rust and has been optimized to scale on multi-core systems. The repository is a Cargo workspace with the fake-tcp and phantun members.

## Sources

- [dndx/phantun on GitHub](https://github.com/dndx/phantun)
- [Issues](https://github.com/dndx/phantun/issues)
- [License: Apache-2.0](https://github.com/dndx/phantun/blob/main/LICENSE)
- [README](https://github.com/dndx/phantun/blob/main/README.md)
- [Releases](https://github.com/dndx/phantun/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dndx-phantun
