add gatunad server and winforms client v1 (TCP-only)
Raw-Ethernet tunnel over ethertype 0x6969 to bypass WFP killswitches. Server (Rust, AF_PACKET + classic BPF) relays TCP to 127.0.0.1 services. Client (.NET 8 WinForms, SharpPcap/Npcap) discovers upstreams and exposes local loopback listeners. Wire protocol: 6-byte header, 7 frame types, MANIFEST with labeled upstreams. UDP types reserved, unimplemented.
This commit is contained in:
@@ -1,3 +1,88 @@
|
||||
# gatuna
|
||||
|
||||
raw ethernet tunnel to circumvent WFP killswitches
|
||||
A raw-Ethernet tunnel for reaching services on a peer machine when a WFP
|
||||
killswitch on the local machine blocks normal IP traffic. Two programs carry
|
||||
bytes between their respective loopbacks over a private L2 protocol, bypassing
|
||||
the IP stack (and therefore the killswitch) entirely.
|
||||
|
||||
## Why this works
|
||||
|
||||
WireGuard-for-Windows installs its killswitch as WFP filters at the ALE layers
|
||||
only (`ALE_AUTH_CONNECT_V4/V6`, `ALE_AUTH_RECV_ACCEPT_V4/V6`). It never installs
|
||||
MAC-layer callouts — the L2 code in `tunnel/firewall/` is commented out. Raw
|
||||
Ethernet frames sent via a packet driver (Npcap on Windows, `AF_PACKET` on
|
||||
Linux) never enter the IP stack, so they never reach the ALE classify path and
|
||||
pass unimpeded. See `wireguard-windows/tunnel/firewall/blocker.go:156`.
|
||||
|
||||
## Components
|
||||
|
||||
- `gatunad` — Rust server. Runs on the peer (Linux) that owns the real
|
||||
services. Announces upstreams and relays TCP between the tunnel and
|
||||
`127.0.0.1:<port>`.
|
||||
- `gatuna` (planned) — .NET WinForms client. Runs on the killswitched Windows
|
||||
box. Discovers the server, presents its upstreams as local loopback
|
||||
listeners, and hauls bytes over the same L2 protocol.
|
||||
|
||||
This repository builds `gatunad` first.
|
||||
|
||||
## Transport
|
||||
|
||||
- **Medium:** raw Ethernet frames on a shared L2 segment.
|
||||
- **Ethertype:** `0x6969` (hardcoded).
|
||||
- **No IP stack involvement.** Frames carry only our 6-byte header + payload.
|
||||
- **BPF:** the server attaches a classic BPF filter `ether proto 0x6969` to its
|
||||
`AF_PACKET` socket so it only wakes on our ethertype. No eBPF authoring.
|
||||
|
||||
## Protocol
|
||||
|
||||
See [`PROTOCOL.md`](PROTOCOL.md) for the full wire format. Summary:
|
||||
|
||||
- 6-byte header, big-endian: `[ version:1 ][ type:1 ][ session_id:4 ][ payload:N ]`.
|
||||
- `version` = `1`. No length field (frame length comes from the capture). No
|
||||
CRC. Ethertype discriminates our frames from everything else.
|
||||
- Discovery: client broadcasts `DISCOVER`; server unicasts `MANIFEST` back.
|
||||
- Sessions: `OPEN` → `OPEN_ACK` (or `OPEN_NAK`) → `DATA`* ↔ `DATA`* → `CLOSE`.
|
||||
- v1 ships TCP only. UDP frame types are reserved but unimplemented.
|
||||
|
||||
## `gatunad` usage
|
||||
|
||||
```
|
||||
gatunad IFACE PORT [PORT...]
|
||||
```
|
||||
|
||||
- `IFACE` — L2 interface name (e.g. `eth0`).
|
||||
- `PORT` — `int[:label]`; a TCP upstream relayed to `127.0.0.1:int`. The label
|
||||
is optional, max 255 bytes, carried in the MANIFEST for the client to display.
|
||||
At least one required. Upstream ID = 1-based positional index in cmdline order.
|
||||
|
||||
Example:
|
||||
```
|
||||
sudo ./gatunad eth0 22 8800:site-a 8080:site-b
|
||||
```
|
||||
Exposes upstreams `1→127.0.0.1:22`, `2→127.0.0.1:8800` (label `site-a`),
|
||||
`3→127.0.0.1:8080` (label `site-b`).
|
||||
|
||||
## Privileges
|
||||
|
||||
`AF_PACKET` requires `CAP_NET_RAW`. Run as root, or grant the binary the
|
||||
capability once:
|
||||
```
|
||||
sudo setcap cap_net_raw+ep ./target/release/gatunad
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
Errors only, to stdout. Normal lifecycle (DISCOVER/OPEN/CLOSE) is silent.
|
||||
|
||||
## v1 limitations
|
||||
|
||||
- TCP only. UDP wire types reserved, code paths stubbed.
|
||||
- No retransmit at the L2 layer. A dropped `DATA` frame breaks the TCP session
|
||||
irrecoverably because the localhost socket already ACKed the bytes. Acceptable
|
||||
on a healthy switched link.
|
||||
- No auth/crypto. Anyone on the same L2 segment can `DISCOVER` and `OPEN`.
|
||||
- Single server instance per interface.
|
||||
|
||||
## License
|
||||
|
||||
CC0 1.0 Universal. See [`LICENSE`](LICENSE).
|
||||
|
||||
Reference in New Issue
Block a user