diff --git a/Cargo.lock b/Cargo.lock index 46d5ff0..dd6c8fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -242,100 +242,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "futures" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" - -[[package]] -name = "futures-executor" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" - -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" - -[[package]] -name = "futures-task" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" - -[[package]] -name = "futures-util" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" -dependencies = [ - "autocfg", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - [[package]] name = "getrandom" version = "0.2.3" @@ -580,7 +486,6 @@ dependencies = [ "anyhow", "boringtun", "clap", - "futures", "lockfree", "log", "pretty_env_logger", @@ -625,12 +530,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pretty_env_logger" version = "0.3.1" @@ -642,18 +541,6 @@ dependencies = [ "log", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - [[package]] name = "proc-macro2" version = "1.0.29" @@ -765,12 +652,6 @@ dependencies = [ "libc", ] -[[package]] -name = "slab" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" - [[package]] name = "slog" version = "2.7.0" diff --git a/Cargo.toml b/Cargo.toml index 9f288a0..aa09475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,3 @@ anyhow = "1" smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", branch = "master" } tokio = { version = "1", features = ["full"] } lockfree = "0.5.1" -futures = "0.3.17" diff --git a/README.md b/README.md index e48c852..022b430 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A cross-platform, user-space WireGuard port-forwarder that requires no system network configurations. -## How it works +## Usage **onetun** opens a TCP port on your local system, from which traffic is forwarded to a TCP port on a peer in your WireGuard network. It requires no changes to your operating system's network interfaces: you don't need to have `root` @@ -71,6 +71,65 @@ $ curl 127.0.0.1:8080 Hello world! ``` +## Download + +Normally I would publish `onetun` to crates.io. However, it depends on some features +in [smoltcp](https://github.com/smoltcp-rs/smoltcp) and +[boringtun](https://github.com/cloudflare/boringtun) that haven't been published yet, so I'm forced to use their Git +repos as dependencies for now. + +In the meantime, you can download the binary for Windows, macOS (Intel), and Linux (amd64) from +the [Releases](https://github.com/aramperes/onetun/releases) page. + +You can also build onetun locally, using Rust: + +```shell +$ git clone https://github.com/aramperes/onetun && cd onetun +$ cargo build --release +$ ./target/release/onetun +``` + +## Architecture + +onetun uses [tokio](https://github.com/tokio-rs/tokio), the async runtime, to listen for new TCP connections on the +given port. + +When a client connects to the local TCP port, it uses [smoltcp](https://github.com/smoltcp-rs/smoltcp) to +create a "virtual interface", with a "virtual client" and a "virtual server" for the connection. These "virtual" +components are the crux of how onetun works. They essentially replace the host's TCP/IP stack with smoltcp's, which +fully runs inside onetun. An ephemeral "virtual port" is also assigned to the connection, in order to route packets +back to the right connection. + +When the real client opens the connection, the virtual client socket opens a TCP connection to the virtual server. +The virtual interface (implemented by smoltcp) in turn crafts the `SYN` segment and wraps it in an IP packet. +Because of how the virtual client and server are configured, the IP packet is crafted with a source address +being the configured `source-peer-ip` (`192.168.4.3` in the example above), +and the destination address is the remote peer's (`192.168.4.2`). + +By doing this, we let smoltcp handle the crafting of the IP packets, and the handling of the client's TCP states. +Instead of actually sending those packets to the virtual server, +we can intercept them in the virtual interface and encrypt the packets using [boringtun](https://github.com/cloudflare/boringtun), +and send them to the WireGuard endpoint's UDP port. + +Once the WireGuard endpoint receives an encrypted IP packet, it decrypts it using its private key and reads the IP packet. +It reads the destination address, re-encrypts the IP packet using the matching peer's public key, and sends it off to +the peer's UDP endpoint. + +The remote peer receives the encrypted IP and decrypts it. It can then read the inner payload (the TCP segment), +forward it to the server's port, which handles the TCP segment. The server responds with `SYN-ACK`, which goes back through +the peer's local WireGuard interface, gets encrypted, forwarded to the WireGuard endpoint, and then finally back to onetun's UDP port. + +When onetun receives an encrypted packet from the WireGuard endpoint, it decrypts it using boringtun. +The resulting IP packet is broadcasted to all virtual interfaces running inside onetun; once the corresponding +interface is matched, the IP packet is read and unpacked, and the virtual client's TCP state is updated. + +Whenever data is sent by the real client, it is simply "sent" by the virtual client, which kicks off the whole IP encapsulation +and WireGuard encryption again. When data is sent by the real server, it ends up routed in the virtual interface, which allows +the virtual client to read it. When the virtual client reads data, it simply pushes the data back to the real client. + +This work is all made possible by [smoltcp](https://github.com/smoltcp-rs/smoltcp) and [boringtun](https://github.com/cloudflare/boringtun), +so special thanks to the developers of those libraries. + ## License MIT. See `LICENSE` for details.