Spoof Tunnel is a Layer 3/Layer 4 tunneling proxy designed to bypass Deep Packet Inspection (DPI) and strict stateful firewalls through mutual bidirectional IP spoofing.
Unlike traditional tunneling protocols that establish a stateful connection between a fixed client IP and a fixed server IP, Spoof Tunnel completely decouples the logical session from the physical network addresses by forging the Source IP field in the IP header at both endpoints.
Important
Both your servers must be able to send spoofed packets.
To test this, you can use the following command temporarily on any of your servers:
iptables -t nat -A POSTROUTING -d target-ip -j SNAT --to-source spoof-ip
then:
ping target-ip
and use a tool like tcpdump on the opposite server:
tcpdump icmp
if you see the spoofed packets, means your source side can send spoofed packet.
The concept of a bidirectional spoofing tunnel emerged in response to the severe internet blackout in Iran following the bloody uprising on January 8 and 9, 2026 (18-19 Dey 1404). During this complete disconnection from the global internet, our primary objective was to reverse-engineer the exact scope and layer of the imposed restrictions.
Upon investigating the BGP routes for Iranian IP prefixes, we observed a surprising detail: unlike the internet shutdown in Afghanistan where BGP routes simply disappeared, Iran's IP ranges were still actively being announced globally. This strongly indicated that the international physical infrastructure was still intact.
Subsequently, it became apparent that certain government-affiliated Iranian entities were able to whitelist their specific IP addresses, successfully restoring their international connectivity. This observation led to the hypothesis that the restriction was being enforced at Layer 3, specifically filtering based on srcIP and dstIP.
This hypothesis was definitively confirmed when we discovered that a select few foreign IP addresses (such as specific ranges from Hetzner) could still establish inbound connections to Iran. The evidence clearly demonstrated that the "blackout" was not a physical severance, but rather a stringent, whitelist-based Layer 3 firewall policy.
In this highly restricted environment, the idea of a spoofing tunnel was conceived. By manipulating the IP headers, we could simulate whitelisted traffic. However, as is inherent to IP spoofing, if a spoofed packet is sent to a server, the server will inherently route its reply back to the spoofed IP address—not the actual origin host.
Therefore, a standard unidirectional spoof was insufficient. We required a robust bidirectional mutual spoofing mechanism where both the client and the server forge their IP headers and are predetermined instances well-aware of each other's actual physical IPs, enabling them to establish and maintain a logical connection despite the asymmetrical, forged routing.
In a typical scenario, the client and server agree on specific IP addresses to spoof:
- Client → Server (Upload): The client transmits packets with a forged source IP (e.g.,
Client_Spoof_IP) addressed to the server's actual listening IP. - Server → Client (Download): The server responds by transmitting packets with a forged source IP (e.g.,
Server_Spoof_IP) addressed to the client's actual IP.
This creates a scenario where intermediate firewalls see unidirectional UDP or ICMP flows that do not logically match any active state mappings, effectively bypassing connection tracking tables (conntrack) and traffic fingerprinting.
To inject packets with arbitrarily modified Layer 3 headers, Spoof Tunnel utilizes raw sockets (AF_INET, SOCK_RAW). It constructs the entire IPv4/IPv6 header manually, calculating the corresponding IP checksums in software.
gopacketandpcapare heavily utilized to bypass the host kernel's network stack.- BPF Filters: To prevent the host OS from dropping inbound spoofed packets or responding with
ICMP Destination Unreachable/TCP RST, an aggressive Berkeley Packet Filter (BPF) limits the capture scope strictly to the tunnel's expected flow, bypassing local routing limits.
The tunnel encapsulates encrypted chunks inside standard ICMP Echo Request (Type 8) and ICMP Echo Reply (Type 0) packets. To network middleboxes, the traffic appears as benign ping sweeps or monitoring traffic.
Standard UDP datagrams are utilized with dynamically shiftable source ports. The protocol mimics connectionless DNS or custom UDP application patterns.
Because ICMP and UDP provide no delivery guarantees, Spoof Tunnel implements a custom TCP-like reliability layer in user space. This is mandatory for maintaining stable TLS handshakes and in-order stream delivery.
- Packet Sequencing and ACKs: Every payload packet is wrapped in a
SeqDataPacketformat containing a monotonic sequence number (4 bytes). The recipient acknowledges data viaAckPacket, utilizing a base sequence number accompanied by a 64-bit acknowledgment bitmap for handling blocks of data at once. - Flow Control & Buffers: The
RecvBuffermaintains an internal map of sequences. Out-of-order packets are buffered. Data is strictly delivered to the internal SOCKS5/Target TCP socket in-order. - Retransmission Engine: An active background goroutine sweeps the
SendBufferevery 100ms. Unacknowledged packets exceeding theretransmit_timeoutare resent using exponential backoff up to a definedmax_retrieslimit.
Establishing a new tunnel session (INIT / INIT_ACK exchange) incurs significant latency. To mitigate this, Spoof Tunnel implements an internal multiplexer (Mux).
A single "Master Session" is established over the unreliable link. All incoming local TCP SOCKS5 connections are assigned a virtual 4-byte StreamID and multiplexed within this single master session.
0x01 MuxStreamOpen:Followed by [StreamID:4][TargetLen:2][Target String]0x02 MuxStreamData:Followed by [StreamID:4][Raw Payload]0x03 MuxStreamClose:Followed by [StreamID:4]0x04 MuxStreamAck:Server acknowledgment for successful proxy stream creation.
Security and obfuscation are enforced via ChaCha20-Poly1305 AEAD. AEAD ensures that not a single byte of the IP payload or tunnel header structure is visible or modifiable by an active MITM attacker without immediately dropping the connection.
Each session initializes a randomized nonce mechanism to prevent replay attacks, while the static pre-shared Base64 keys act as the master cryptographic secret.
Spoof Tunnel is written in Go. You can build it using the standard Go toolchain:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o spoof ./cmd/spoof/Before starting the tunnel, you need to generate a pair of Base64 private/public keys for both the server and the client.
./spoof generate-keysTake note of the Private Key and Public Key. The Server's Public Key must be placed in the Client's peer_public_key field, and the Client's Public Key must be placed in the Server's peer_public_key field.
Note: Raw sockets require elevated privileges. You must execute both the client and server binaries as
root(or assign theCAP_NET_RAWcapability).
On the Server:
sudo ./spoof server -c server-config.jsonOn the Client:
sudo ./spoof client -c client-config.jsonOnce the client connects, it will open a SOCKS5 proxy on 127.0.0.1:1080 (by default) that securely routes through the spoofed tunnel.
Here is a detailed breakdown of the config.json parameters required to operate the tunnel:
| Key Category/Path | Type | Description |
|---|---|---|
mode |
String | "client" or "server". Dictates the operational mode of the binary. |
transport |
||
transport.type |
String | "udp" or "icmp". The underlying protocol used for the tunnel. |
transport.icmp_mode |
String | "echo" or "reply". Used when type is ICMP. |
listen |
||
listen.address |
String | Client: SOCKS5 bind address (e.g., 127.0.0.1).Server: The IP address where the server expects the tunnel traffic to arrive. |
listen.port |
Integer | Client: SOCKS5 bind port (e.g., 1080).Server: The tunnel listening port (only applicable for UDP mode). |
server |
||
server.address |
String | Client only: The actual remote server IP address to send packets to. |
server.port |
Integer | Client only: The remote server port to send packets to. |
spoof |
||
spoof.source_ip |
String | The IP address this node will claim to be when sending outbound packets. |
spoof.peer_spoof_ip |
String | The specific expected fake source IP of the incoming packets from the peer. Used by the BPF filter to capture the tunnel traffic. |
crypto |
||
crypto.private_key |
String | The node's private key (Base64) generated via generate-keys. |
crypto.peer_public_key |
String | The peer's public key (Base64). Client puts Server's public key here, and vice versa. |
performance |
||
performance.buffer_size |
Integer | Buffer size |
performance.read_buffer |
Integer | |
performance.write_buffer |
Integer | |
performance.mtu |
Integer | Maximum payload sizing before tunnel encapsulation. Crucial to adjust down (e.g., 1300 or 1400) to avoid Layer 3 IP fragmentation. |
performance.session_timeout |
Integer | General session timeout duration in seconds. |
performance.workers |
Integer | Number of concurrent packet processing workers. |
fec |
||
fec.enabled |
Boolean | enable or disable the fec(true/false) |
fec.data_shards |
Integer | real data |
fec.parity_shards |
Integer | trash data |
reliability |
||
reliability.enabled |
Boolean | Set to true to enable the custom TCP-like delivery layer. |
reliability.window_size |
Integer | Maximum number of unacknowledged packets allowed in-flight simultaneously. |
reliability.retransmit_timeout_ms |
Integer | Base milliseconds to wait before triggering a packet retransmission. |
reliability.max_retries |
Integer | Maximum number of times to re-send a dropped packet before giving up. |
reliability.ack_interval_ms |
Integer | How frequently (in ms) to piggyback or send pending ACKs. |
keepalive |
||
keepalive.enabled |
Boolean | Enables periodic pinging to keep the NAT/State table (if any) and tunnel session alive. |
keepalive.interval_seconds |
Integer | Interval between ping packets. |
keepalive.timeout_seconds |
Integer | Time without activity before dropping the master session. |