copied from: github.com/meh/rust-tun
This commit is contained in:
45
Cargo.toml
Normal file
45
Cargo.toml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
[package]
|
||||||
|
name = "tun"
|
||||||
|
version = "0.5.4"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
authors = ["meh. <meh@schizofreni.co>"]
|
||||||
|
license = "WTFPL"
|
||||||
|
|
||||||
|
description = "TUN device creation and handling."
|
||||||
|
repository = "https://github.com/meh/rust-tun"
|
||||||
|
keywords = ["tun", "network", "tunnel", "bindings"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
thiserror = "1"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "android"))'.dependencies]
|
||||||
|
tokio = { version = "1", features = ["net", "macros"], optional = true }
|
||||||
|
tokio-util = { version = "0.6", features = ["codec"], optional = true }
|
||||||
|
bytes = { version = "1", optional = true }
|
||||||
|
byteorder = { version = "1", optional = true }
|
||||||
|
# This is only for the `ready` macro.
|
||||||
|
futures-core = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||||
|
ioctl = { version = "0.6", package = "ioctl-sys" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
packet = "0.1"
|
||||||
|
futures = "0.3"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
async = ["tokio", "tokio-util", "bytes", "byteorder", "futures-core"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "read-async"
|
||||||
|
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "read-async-codec"
|
||||||
|
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "ping-tun"
|
||||||
|
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||||
107
README.md
107
README.md
@@ -1,3 +1,106 @@
|
|||||||
# meh-rust-tun
|
TUN interfaces [](https://crates.io/crates/tun)  
|
||||||
|
==============
|
||||||
|
This crate allows the creation and usage of TUN interfaces, the aim is to make this cross-platform.
|
||||||
|
|
||||||
https://github.com/meh/rust-tun
|
Usage
|
||||||
|
-----
|
||||||
|
First, add the following to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
tun = "0.5"
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, add this to your crate root:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
extern crate tun;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to use the TUN interface with mio/tokio, you need to enable the `async` feature:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
tun = { version = "0.5", features = ["async"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
The following example creates and configures a TUN interface and starts reading
|
||||||
|
packets from it.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
extern crate tun;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut config = tun::Configuration::default();
|
||||||
|
config.address((10, 0, 0, 1))
|
||||||
|
.netmask((255, 255, 255, 0))
|
||||||
|
.up();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
config.platform(|config| {
|
||||||
|
config.packet_information(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dev = tun::create(&config).unwrap();
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let amount = dev.read(&mut buf).unwrap();
|
||||||
|
println!("{:?}", &buf[0 .. amount]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Platforms
|
||||||
|
=========
|
||||||
|
Not every platform is supported.
|
||||||
|
|
||||||
|
Linux
|
||||||
|
-----
|
||||||
|
You will need the `tun` module to be loaded and root is required to create
|
||||||
|
interfaces.
|
||||||
|
|
||||||
|
macOS
|
||||||
|
-----
|
||||||
|
It just werks, but you have to set up routing manually.
|
||||||
|
|
||||||
|
iOS
|
||||||
|
----
|
||||||
|
You can pass the file descriptor of the TUN device to `rust-tun` to create the interface.
|
||||||
|
|
||||||
|
Here is an example to create the TUN device on iOS and pass the `fd` to `rust-tun`:
|
||||||
|
```swift
|
||||||
|
// Swift
|
||||||
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
let tunnelNetworkSettings = createTunnelSettings() // Configure TUN address, DNS, mtu, routing...
|
||||||
|
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
|
||||||
|
let tunFd = self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
|
||||||
|
DispatchQueue.global(qos: .default).async {
|
||||||
|
start_tun(tunFd)
|
||||||
|
}
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn start_tun(fd: std::os::raw::c_int) {
|
||||||
|
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
let mut cfg = tun::Configuration::default();
|
||||||
|
cfg.raw_fd(fd);
|
||||||
|
let mut tun = tun::create_as_async(&cfg).unwrap();
|
||||||
|
let mut framed = tun.into_framed();
|
||||||
|
while let Some(packet) = framed.next().await {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
78
examples/ping-tun.rs
Normal file
78
examples/ping-tun.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use futures::{SinkExt, StreamExt};
|
||||||
|
use packet::{builder::Builder, icmp, ip, Packet};
|
||||||
|
use tun::{self, Configuration, TunPacket};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let mut config = Configuration::default();
|
||||||
|
|
||||||
|
config
|
||||||
|
.address((10, 0, 0, 1))
|
||||||
|
.netmask((255, 255, 255, 0))
|
||||||
|
.up();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
config.platform(|config| {
|
||||||
|
config.packet_information(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let dev = tun::create_as_async(&config).unwrap();
|
||||||
|
|
||||||
|
let mut framed = dev.into_framed();
|
||||||
|
|
||||||
|
while let Some(packet) = framed.next().await {
|
||||||
|
match packet {
|
||||||
|
Ok(pkt) => match ip::Packet::new(pkt.get_bytes()) {
|
||||||
|
Ok(ip::Packet::V4(pkt)) => match icmp::Packet::new(pkt.payload()) {
|
||||||
|
Ok(icmp) => match icmp.echo() {
|
||||||
|
Ok(icmp) => {
|
||||||
|
let reply = ip::v4::Builder::default()
|
||||||
|
.id(0x42)
|
||||||
|
.unwrap()
|
||||||
|
.ttl(64)
|
||||||
|
.unwrap()
|
||||||
|
.source(pkt.destination())
|
||||||
|
.unwrap()
|
||||||
|
.destination(pkt.source())
|
||||||
|
.unwrap()
|
||||||
|
.icmp()
|
||||||
|
.unwrap()
|
||||||
|
.echo()
|
||||||
|
.unwrap()
|
||||||
|
.reply()
|
||||||
|
.unwrap()
|
||||||
|
.identifier(icmp.identifier())
|
||||||
|
.unwrap()
|
||||||
|
.sequence(icmp.sequence())
|
||||||
|
.unwrap()
|
||||||
|
.payload(icmp.payload())
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
framed.send(TunPacket::new(reply)).await.unwrap();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Err(err) => println!("Received an invalid packet: {:?}", err),
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Err(err) => panic!("Error: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
examples/read-async-codec.rs
Normal file
61
examples/read-async-codec.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use packet::{ip::Packet, Error};
|
||||||
|
use tokio_util::codec::{Decoder, FramedRead};
|
||||||
|
|
||||||
|
pub struct IPPacketCodec;
|
||||||
|
|
||||||
|
impl Decoder for IPPacketCodec {
|
||||||
|
type Item = Packet<BytesMut>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = buf.split_to(buf.len());
|
||||||
|
Ok(match Packet::no_payload(buf) {
|
||||||
|
Ok(pkt) => Some(pkt),
|
||||||
|
Err(err) => {
|
||||||
|
println!("error {:?}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let mut config = tun::Configuration::default();
|
||||||
|
|
||||||
|
config
|
||||||
|
.address((10, 0, 0, 1))
|
||||||
|
.netmask((255, 255, 255, 0))
|
||||||
|
.up();
|
||||||
|
|
||||||
|
let dev = tun::create_as_async(&config).unwrap();
|
||||||
|
|
||||||
|
let mut stream = FramedRead::new(dev, IPPacketCodec);
|
||||||
|
|
||||||
|
while let Some(packet) = stream.next().await {
|
||||||
|
match packet {
|
||||||
|
Ok(pkt) => println!("pkt: {:#?}", pkt),
|
||||||
|
Err(err) => panic!("Error: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
examples/read-async.rs
Normal file
42
examples/read-async.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use futures::StreamExt;
|
||||||
|
use packet::ip::Packet;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let mut config = tun::Configuration::default();
|
||||||
|
|
||||||
|
config
|
||||||
|
.address((10, 0, 0, 1))
|
||||||
|
.netmask((255, 255, 255, 0))
|
||||||
|
.up();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
config.platform(|config| {
|
||||||
|
config.packet_information(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let dev = tun::create_as_async(&config).unwrap();
|
||||||
|
|
||||||
|
let mut stream = dev.into_framed();
|
||||||
|
|
||||||
|
while let Some(packet) = stream.next().await {
|
||||||
|
match packet {
|
||||||
|
Ok(pkt) => println!("pkt: {:#?}", Packet::unchecked(pkt.get_bytes())),
|
||||||
|
Err(err) => panic!("Error: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
examples/read.rs
Normal file
37
examples/read.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut config = tun::Configuration::default();
|
||||||
|
|
||||||
|
config
|
||||||
|
.address((10, 0, 0, 1))
|
||||||
|
.netmask((255, 255, 255, 0))
|
||||||
|
.up();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
config.platform(|config| {
|
||||||
|
config.packet_information(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dev = tun::create(&config).unwrap();
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let amount = dev.read(&mut buf).unwrap();
|
||||||
|
println!("{:?}", &buf[0..amount]);
|
||||||
|
}
|
||||||
|
}
|
||||||
82
flake.lock
generated
Normal file
82
flake.lock
generated
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"fenix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1629858211,
|
||||||
|
"narHash": "sha256-YyQvCmRekZ5QGN9RObUZWg6Pqmi5+idTZ5Ew1Ct5tEk=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "495510be4bc7ccf03b54ff4226fcc7b01fcf62ee",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1629827819,
|
||||||
|
"narHash": "sha256-cysfDbdjTluO+UtuT6iRrf/2Lt7Qo/qRlpz9F5rz3Sc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "503209808cd613daed238e21e7a18ffcbeacebe3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"fenix": "fenix",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-analyzer-src": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1629817082,
|
||||||
|
"narHash": "sha256-qdKZss+bNccS8FJsKbkVGZGISiAGlQ/Se1PFlcroVEk=",
|
||||||
|
"owner": "rust-analyzer",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"rev": "ce4670f299d72a0f23f347b5df9790aca72da617",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rust-analyzer",
|
||||||
|
"ref": "nightly",
|
||||||
|
"repo": "rust-analyzer",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1629481132,
|
||||||
|
"narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "997f7efcb746a9c140ce1f13c72263189225f482",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
28
flake.nix
Normal file
28
flake.nix
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
fenix = {
|
||||||
|
url = "github:nix-community/fenix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, utils, nixpkgs, fenix, }: utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
rust = fenix.packages.${system};
|
||||||
|
lib = pkgs.lib;
|
||||||
|
in {
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; with llvmPackages; [
|
||||||
|
# For building.
|
||||||
|
clang rust.stable.toolchain pkg-config openssl libsodium libclang.lib
|
||||||
|
];
|
||||||
|
|
||||||
|
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
|
RUST_BACKTRACE = 1;
|
||||||
|
# RUST_LOG = "info,sqlx::query=warn";
|
||||||
|
RUSTFLAGS = "-C target-cpu=native";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
128
src/address.rs
Normal file
128
src/address.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
use std::net::{SocketAddr, SocketAddrV4};
|
||||||
|
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// Helper trait to convert things into IPv4 addresses.
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
pub trait IntoAddress {
|
||||||
|
/// Convert the type to an `Ipv4Addr`.
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for u32 {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Ok(Ipv4Addr::new(
|
||||||
|
((*self) & 0xff) as u8,
|
||||||
|
((*self >> 8) & 0xff) as u8,
|
||||||
|
((*self >> 16) & 0xff) as u8,
|
||||||
|
((*self >> 24) & 0xff) as u8,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for i32 {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(*self as u32).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for (u8, u8, u8, u8) {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Ok(Ipv4Addr::new(self.0, self.1, self.2, self.3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for str {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
self.parse().map_err(|_| Error::InvalidAddress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a str {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(*self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for String {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a String {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for Ipv4Addr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a Ipv4Addr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for IpAddr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
match *self {
|
||||||
|
IpAddr::V4(ref value) => Ok(*value),
|
||||||
|
|
||||||
|
IpAddr::V6(_) => Err(Error::InvalidAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a IpAddr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for SocketAddrV4 {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Ok(*self.ip())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a SocketAddrV4 {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoAddress for SocketAddr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
match *self {
|
||||||
|
SocketAddr::V4(ref value) => Ok(*value.ip()),
|
||||||
|
|
||||||
|
SocketAddr::V6(_) => Err(Error::InvalidAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAddress for &'a SocketAddr {
|
||||||
|
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||||
|
(&**self).into_address()
|
||||||
|
}
|
||||||
|
}
|
||||||
149
src/async/codec.rs
Normal file
149
src/async/codec.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt};
|
||||||
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
|
use tokio_util::codec::{Decoder, Encoder};
|
||||||
|
|
||||||
|
/// A packet protocol IP version
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum PacketProtocol {
|
||||||
|
IPv4,
|
||||||
|
IPv6,
|
||||||
|
Other(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: the protocol in the packet information header is platform dependent.
|
||||||
|
impl PacketProtocol {
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||||
|
match self {
|
||||||
|
PacketProtocol::IPv4 => Ok(libc::ETH_P_IP as u16),
|
||||||
|
PacketProtocol::IPv6 => Ok(libc::ETH_P_IPV6 as u16),
|
||||||
|
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"neither an IPv4 or IPv6 packet",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||||
|
match self {
|
||||||
|
PacketProtocol::IPv4 => Ok(libc::PF_INET as u16),
|
||||||
|
PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16),
|
||||||
|
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
"neither an IPv4 or IPv6 packet",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Tun Packet to be sent or received on the TUN interface.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TunPacket(PacketProtocol, Bytes);
|
||||||
|
|
||||||
|
/// Infer the protocol based on the first nibble in the packet buffer.
|
||||||
|
fn infer_proto(buf: &[u8]) -> PacketProtocol {
|
||||||
|
match buf[0] >> 4 {
|
||||||
|
4 => PacketProtocol::IPv4,
|
||||||
|
6 => PacketProtocol::IPv6,
|
||||||
|
p => PacketProtocol::Other(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TunPacket {
|
||||||
|
/// Create a new `TunPacket` based on a byte slice.
|
||||||
|
pub fn new(bytes: Vec<u8>) -> TunPacket {
|
||||||
|
let proto = infer_proto(&bytes);
|
||||||
|
TunPacket(proto, Bytes::from(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return this packet's bytes.
|
||||||
|
pub fn get_bytes(&self) -> &[u8] {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_bytes(self) -> Bytes {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A TunPacket Encoder/Decoder.
|
||||||
|
pub struct TunPacketCodec(bool, i32);
|
||||||
|
|
||||||
|
impl TunPacketCodec {
|
||||||
|
/// Create a new `TunPacketCodec` specifying whether the underlying
|
||||||
|
/// tunnel Device has enabled the packet information header.
|
||||||
|
pub fn new(pi: bool, mtu: i32) -> TunPacketCodec {
|
||||||
|
TunPacketCodec(pi, mtu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for TunPacketCodec {
|
||||||
|
type Item = TunPacket;
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
|
if buf.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pkt = buf.split_to(buf.len());
|
||||||
|
|
||||||
|
// reserve enough space for the next packet
|
||||||
|
if self.0 {
|
||||||
|
buf.reserve(self.1 as usize + 4);
|
||||||
|
} else {
|
||||||
|
buf.reserve(self.1 as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the packet information is enabled we have to ignore the first 4 bytes
|
||||||
|
if self.0 {
|
||||||
|
let _ = pkt.split_to(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
let proto = infer_proto(pkt.as_ref());
|
||||||
|
Ok(Some(TunPacket(proto, pkt.freeze())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder<TunPacket> for TunPacketCodec {
|
||||||
|
type Error = io::Error;
|
||||||
|
|
||||||
|
fn encode(&mut self, item: TunPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||||
|
dst.reserve(item.get_bytes().len() + 4);
|
||||||
|
match item {
|
||||||
|
TunPacket(proto, bytes) if self.0 => {
|
||||||
|
// build the packet information header comprising of 2 u16
|
||||||
|
// fields: flags and protocol.
|
||||||
|
let mut buf = Vec::<u8>::with_capacity(4);
|
||||||
|
|
||||||
|
// flags is always 0
|
||||||
|
buf.write_u16::<NativeEndian>(0).unwrap();
|
||||||
|
// write the protocol as network byte order
|
||||||
|
buf.write_u16::<NetworkEndian>(proto.into_pi_field()?)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dst.put_slice(&buf);
|
||||||
|
dst.put(bytes);
|
||||||
|
}
|
||||||
|
TunPacket(_, bytes) => dst.put(bytes),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
201
src/async/device.rs
Normal file
201
src/async/device.rs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::io::{IoSlice, Read, Write};
|
||||||
|
|
||||||
|
use core::pin::Pin;
|
||||||
|
use core::task::{Context, Poll};
|
||||||
|
use futures_core::ready;
|
||||||
|
use tokio::io::unix::AsyncFd;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use tokio_util::codec::Framed;
|
||||||
|
|
||||||
|
use crate::device::Device as D;
|
||||||
|
use crate::platform::{Device, Queue};
|
||||||
|
use crate::r#async::codec::*;
|
||||||
|
|
||||||
|
/// An async TUN device wrapper around a TUN device.
|
||||||
|
pub struct AsyncDevice {
|
||||||
|
inner: AsyncFd<Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncDevice {
|
||||||
|
/// Create a new `AsyncDevice` wrapping around a `Device`.
|
||||||
|
pub fn new(device: Device) -> io::Result<AsyncDevice> {
|
||||||
|
device.set_nonblock()?;
|
||||||
|
Ok(AsyncDevice {
|
||||||
|
inner: AsyncFd::new(device)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Returns a shared reference to the underlying Device object
|
||||||
|
pub fn get_ref(&self) -> &Device {
|
||||||
|
self.inner.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the underlying Device object
|
||||||
|
pub fn get_mut(&mut self) -> &mut Device {
|
||||||
|
self.inner.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface)
|
||||||
|
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||||
|
let pi = self.get_mut().has_packet_information();
|
||||||
|
let codec = TunPacketCodec::new(pi, self.inner.get_ref().mtu().unwrap_or(1504));
|
||||||
|
Framed::new(self, codec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for AsyncDevice {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||||
|
let rbuf = buf.initialize_unfilled();
|
||||||
|
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||||
|
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for AsyncDevice {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||||
|
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||||
|
Ok(res) => return Poll::Ready(res),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||||
|
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||||
|
Ok(res) => return Poll::Ready(res),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> Poll<Result<usize, io::Error>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||||
|
match guard.try_io(|inner| inner.get_mut().write_vectored(bufs)) {
|
||||||
|
Ok(res) => return Poll::Ready(res),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An async TUN device queue wrapper around a TUN device queue.
|
||||||
|
pub struct AsyncQueue {
|
||||||
|
inner: AsyncFd<Queue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncQueue {
|
||||||
|
/// Create a new `AsyncQueue` wrapping around a `Queue`.
|
||||||
|
pub fn new(queue: Queue) -> io::Result<AsyncQueue> {
|
||||||
|
queue.set_nonblock()?;
|
||||||
|
Ok(AsyncQueue {
|
||||||
|
inner: AsyncFd::new(queue)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Returns a shared reference to the underlying Queue object
|
||||||
|
pub fn get_ref(&self) -> &Queue {
|
||||||
|
self.inner.get_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mutable reference to the underlying Queue object
|
||||||
|
pub fn get_mut(&mut self) -> &mut Queue {
|
||||||
|
self.inner.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes this AsyncQueue and return a Framed object (unified Stream and Sink interface)
|
||||||
|
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||||
|
let pi = self.get_mut().has_packet_information();
|
||||||
|
let codec = TunPacketCodec::new(pi, 1504);
|
||||||
|
Framed::new(self, codec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for AsyncQueue {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||||
|
let rbuf = buf.initialize_unfilled();
|
||||||
|
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||||
|
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for AsyncQueue {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||||
|
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||||
|
Ok(res) => return Poll::Ready(res),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
loop {
|
||||||
|
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||||
|
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||||
|
Ok(res) => return Poll::Ready(res),
|
||||||
|
Err(_wb) => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/async/mod.rs
Normal file
32
src/async/mod.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Async specific modules.
|
||||||
|
|
||||||
|
use crate::error;
|
||||||
|
|
||||||
|
use crate::configuration::Configuration;
|
||||||
|
use crate::platform::create;
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use self::device::{AsyncDevice, AsyncQueue};
|
||||||
|
|
||||||
|
mod codec;
|
||||||
|
pub use self::codec::{TunPacket, TunPacketCodec};
|
||||||
|
|
||||||
|
/// Create a TUN device with the given name.
|
||||||
|
pub fn create_as_async(configuration: &Configuration) -> Result<AsyncDevice, error::Error> {
|
||||||
|
let device = create(&configuration)?;
|
||||||
|
AsyncDevice::new(device).map_err(|err| err.into())
|
||||||
|
}
|
||||||
126
src/configuration.rs
Normal file
126
src/configuration.rs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
|
||||||
|
use crate::address::IntoAddress;
|
||||||
|
use crate::platform;
|
||||||
|
|
||||||
|
/// TUN interface OSI layer of operation.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Layer {
|
||||||
|
L2,
|
||||||
|
L3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Layer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Layer::L3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration builder for a TUN interface.
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub(crate) name: Option<String>,
|
||||||
|
pub(crate) platform: platform::Configuration,
|
||||||
|
|
||||||
|
pub(crate) address: Option<Ipv4Addr>,
|
||||||
|
pub(crate) destination: Option<Ipv4Addr>,
|
||||||
|
pub(crate) broadcast: Option<Ipv4Addr>,
|
||||||
|
pub(crate) netmask: Option<Ipv4Addr>,
|
||||||
|
pub(crate) mtu: Option<i32>,
|
||||||
|
pub(crate) enabled: Option<bool>,
|
||||||
|
pub(crate) layer: Option<Layer>,
|
||||||
|
pub(crate) queues: Option<usize>,
|
||||||
|
pub(crate) raw_fd: Option<RawFd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Configuration {
|
||||||
|
/// Access the platform dependant configuration.
|
||||||
|
pub fn platform<F>(&mut self, f: F) -> &mut Self
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut platform::Configuration),
|
||||||
|
{
|
||||||
|
f(&mut self.platform);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the name.
|
||||||
|
pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
|
||||||
|
self.name = Some(name.as_ref().into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the address.
|
||||||
|
pub fn address<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||||
|
self.address = Some(value.into_address().unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the destination address.
|
||||||
|
pub fn destination<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||||
|
self.destination = Some(value.into_address().unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the broadcast address.
|
||||||
|
pub fn broadcast<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||||
|
self.broadcast = Some(value.into_address().unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the netmask.
|
||||||
|
pub fn netmask<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||||
|
self.netmask = Some(value.into_address().unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the MTU.
|
||||||
|
pub fn mtu(&mut self, value: i32) -> &mut Self {
|
||||||
|
self.mtu = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interface to be enabled once created.
|
||||||
|
pub fn up(&mut self) -> &mut Self {
|
||||||
|
self.enabled = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interface to be disabled once created.
|
||||||
|
pub fn down(&mut self) -> &mut Self {
|
||||||
|
self.enabled = Some(false);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the OSI layer of operation.
|
||||||
|
pub fn layer(&mut self, value: Layer) -> &mut Self {
|
||||||
|
self.layer = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the number of queues.
|
||||||
|
pub fn queues(&mut self, value: usize) -> &mut Self {
|
||||||
|
self.queues = Some(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the raw fd.
|
||||||
|
pub fn raw_fd(&mut self, fd: RawFd) -> &mut Self {
|
||||||
|
self.raw_fd = Some(fd);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/device.rs
Normal file
95
src/device.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use crate::configuration::Configuration;
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// A TUN device.
|
||||||
|
pub trait Device: Read + Write {
|
||||||
|
type Queue: Read + Write;
|
||||||
|
|
||||||
|
/// Reconfigure the device.
|
||||||
|
fn configure(&mut self, config: &Configuration) -> Result<()> {
|
||||||
|
if let Some(ip) = config.address {
|
||||||
|
self.set_address(ip)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ip) = config.destination {
|
||||||
|
self.set_destination(ip)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ip) = config.broadcast {
|
||||||
|
self.set_broadcast(ip)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ip) = config.netmask {
|
||||||
|
self.set_netmask(ip)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mtu) = config.mtu {
|
||||||
|
self.set_mtu(mtu)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(enabled) = config.enabled {
|
||||||
|
self.enabled(enabled)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the device name.
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
|
/// Set the device name.
|
||||||
|
fn set_name(&mut self, name: &str) -> Result<()>;
|
||||||
|
|
||||||
|
/// Turn on or off the interface.
|
||||||
|
fn enabled(&mut self, value: bool) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get the address.
|
||||||
|
fn address(&self) -> Result<Ipv4Addr>;
|
||||||
|
|
||||||
|
/// Set the address.
|
||||||
|
fn set_address(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get the destination address.
|
||||||
|
fn destination(&self) -> Result<Ipv4Addr>;
|
||||||
|
|
||||||
|
/// Set the destination address.
|
||||||
|
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get the broadcast address.
|
||||||
|
fn broadcast(&self) -> Result<Ipv4Addr>;
|
||||||
|
|
||||||
|
/// Set the broadcast address.
|
||||||
|
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get the netmask.
|
||||||
|
fn netmask(&self) -> Result<Ipv4Addr>;
|
||||||
|
|
||||||
|
/// Set the netmask.
|
||||||
|
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get the MTU.
|
||||||
|
fn mtu(&self) -> Result<i32>;
|
||||||
|
|
||||||
|
/// Set the MTU.
|
||||||
|
fn set_mtu(&mut self, value: i32) -> Result<()>;
|
||||||
|
|
||||||
|
/// Get a device queue.
|
||||||
|
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue>;
|
||||||
|
}
|
||||||
54
src/error.rs
Normal file
54
src/error.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::{ffi, io, num};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("invalid configuration")]
|
||||||
|
InvalidConfig,
|
||||||
|
|
||||||
|
#[error("not implementated")]
|
||||||
|
NotImplemented,
|
||||||
|
|
||||||
|
#[error("device name too long")]
|
||||||
|
NameTooLong,
|
||||||
|
|
||||||
|
#[error("invalid device name")]
|
||||||
|
InvalidName,
|
||||||
|
|
||||||
|
#[error("invalid address")]
|
||||||
|
InvalidAddress,
|
||||||
|
|
||||||
|
#[error("invalid file descriptor")]
|
||||||
|
InvalidDescriptor,
|
||||||
|
|
||||||
|
#[error("unsuported network layer of operation")]
|
||||||
|
UnsupportedLayer,
|
||||||
|
|
||||||
|
#[error("invalid queues number")]
|
||||||
|
InvalidQueuesNumber,
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
Nul(#[from] ffi::NulError),
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
ParseNum(#[from] num::ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
53
src/lib.rs
Normal file
53
src/lib.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
pub use crate::error::*;
|
||||||
|
|
||||||
|
mod address;
|
||||||
|
pub use crate::address::IntoAddress;
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use crate::device::Device;
|
||||||
|
|
||||||
|
mod configuration;
|
||||||
|
pub use crate::configuration::{Configuration, Layer};
|
||||||
|
|
||||||
|
pub mod platform;
|
||||||
|
pub use crate::platform::create;
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "async",
|
||||||
|
any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "ios",
|
||||||
|
target_os = "android"
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
pub mod r#async;
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "async",
|
||||||
|
any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "ios",
|
||||||
|
target_os = "android"
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
pub use r#async::*;
|
||||||
|
|
||||||
|
pub fn configure() -> Configuration {
|
||||||
|
Configuration::default()
|
||||||
|
}
|
||||||
214
src/platform/android/device.rs
Normal file
214
src/platform/android/device.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::configuration::Configuration;
|
||||||
|
use crate::device::Device as D;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::platform::posix::{self, Fd};
|
||||||
|
|
||||||
|
/// A TUN device for Android.
|
||||||
|
pub struct Device {
|
||||||
|
queue: Queue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
/// Create a new `Device` for the given `Configuration`.
|
||||||
|
pub fn new(config: &Configuration) -> Result<Self> {
|
||||||
|
let fd = match config.raw_fd {
|
||||||
|
Some(raw_fd) => raw_fd,
|
||||||
|
_ => return Err(Error::InvalidConfig),
|
||||||
|
};
|
||||||
|
let device = {
|
||||||
|
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
Device {
|
||||||
|
queue: Queue { tun: tun },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the interface into a `Reader` and `Writer`.
|
||||||
|
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||||
|
let fd = Arc::new(self.queue.tun);
|
||||||
|
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether the device has packet information
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
self.queue.has_packet_information()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set non-blocking mode
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.queue.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Device {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Device {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.queue.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl D for Device {
|
||||||
|
type Queue = Queue;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destination(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mtu(&self) -> Result<i32> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||||
|
if index > 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(&mut self.queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Device {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.queue.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Device {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.queue.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Queue {
|
||||||
|
tun: Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
// on Android this is always the case
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.tun.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Queue {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.tun.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Queue {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.tun.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Queue {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Queue {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/platform/android/mod.rs
Normal file
30
src/platform/android/mod.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Android specific functionality.
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use self::device::{Device, Queue};
|
||||||
|
|
||||||
|
use crate::configuration::Configuration as C;
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// Android-only interface configuration.
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub struct Configuration {}
|
||||||
|
|
||||||
|
/// Create a TUN device with the given name.
|
||||||
|
pub fn create(configuration: &C) -> Result<Device> {
|
||||||
|
Device::new(&configuration)
|
||||||
|
}
|
||||||
214
src/platform/ios/device.rs
Normal file
214
src/platform/ios/device.rs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::configuration::Configuration;
|
||||||
|
use crate::device::Device as D;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::platform::posix::{self, Fd};
|
||||||
|
|
||||||
|
/// A TUN device for iOS.
|
||||||
|
pub struct Device {
|
||||||
|
queue: Queue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
/// Create a new `Device` for the given `Configuration`.
|
||||||
|
pub fn new(config: &Configuration) -> Result<Self> {
|
||||||
|
let fd = match config.raw_fd {
|
||||||
|
Some(raw_fd) => raw_fd,
|
||||||
|
_ => return Err(Error::InvalidConfig),
|
||||||
|
};
|
||||||
|
let mut device = unsafe {
|
||||||
|
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
Device {
|
||||||
|
queue: Queue { tun: tun },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the interface into a `Reader` and `Writer`.
|
||||||
|
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||||
|
let fd = Arc::new(self.queue.tun);
|
||||||
|
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether the device has packet information
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
self.queue.has_packet_information()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set non-blocking mode
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.queue.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Device {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Device {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.queue.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl D for Device {
|
||||||
|
type Queue = Queue;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destination(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mtu(&self) -> Result<i32> {
|
||||||
|
Err(Error::NotImplemented)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||||
|
if index > 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(&mut self.queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Device {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.queue.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Device {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.queue.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Queue {
|
||||||
|
tun: Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
// on ios this is always the case
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.tun.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Queue {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.tun.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Queue {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.tun.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Queue {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Queue {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/platform/ios/mod.rs
Normal file
30
src/platform/ios/mod.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! iOS specific functionality.
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use self::device::{Device, Queue};
|
||||||
|
|
||||||
|
use crate::configuration::Configuration as C;
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// iOS-only interface configuration.
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub struct Configuration {}
|
||||||
|
|
||||||
|
/// Create a TUN device with the given name.
|
||||||
|
pub fn create(configuration: &C) -> Result<Device> {
|
||||||
|
Device::new(&configuration)
|
||||||
|
}
|
||||||
452
src/platform/linux/device.rs
Normal file
452
src/platform/linux/device.rs
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::mem;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
use std::ptr;
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use libc;
|
||||||
|
use libc::{c_char, c_short};
|
||||||
|
use libc::{AF_INET, O_RDWR, SOCK_DGRAM};
|
||||||
|
|
||||||
|
use crate::configuration::{Configuration, Layer};
|
||||||
|
use crate::device::Device as D;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::platform::linux::sys::*;
|
||||||
|
use crate::platform::posix::{Fd, SockAddr};
|
||||||
|
|
||||||
|
/// A TUN device using the TUN/TAP Linux driver.
|
||||||
|
pub struct Device {
|
||||||
|
name: String,
|
||||||
|
queues: Vec<Queue>,
|
||||||
|
ctl: Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
/// Create a new `Device` for the given `Configuration`.
|
||||||
|
pub fn new(config: &Configuration) -> Result<Self> {
|
||||||
|
let mut device = unsafe {
|
||||||
|
let dev = match config.name.as_ref() {
|
||||||
|
Some(name) => {
|
||||||
|
let name = CString::new(name.clone())?;
|
||||||
|
|
||||||
|
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||||
|
return Err(Error::NameTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut queues = Vec::new();
|
||||||
|
|
||||||
|
let mut req: ifreq = mem::zeroed();
|
||||||
|
|
||||||
|
if let Some(dev) = dev.as_ref() {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
dev.as_ptr() as *const c_char,
|
||||||
|
req.ifrn.name.as_mut_ptr(),
|
||||||
|
dev.as_bytes().len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let device_type: c_short = config.layer.unwrap_or(Layer::L3).into();
|
||||||
|
|
||||||
|
let queues_num = config.queues.unwrap_or(1);
|
||||||
|
if queues_num < 1 {
|
||||||
|
return Err(Error::InvalidQueuesNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.ifru.flags = device_type
|
||||||
|
| if config.platform.packet_information {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
IFF_NO_PI
|
||||||
|
}
|
||||||
|
| if queues_num > 1 { IFF_MULTI_QUEUE } else { 0 };
|
||||||
|
|
||||||
|
for _ in 0..queues_num {
|
||||||
|
let tun = Fd::new(libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR))
|
||||||
|
.map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
if tunsetiff(tun.0, &mut req as *mut _ as *mut _) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
queues.push(Queue {
|
||||||
|
tun,
|
||||||
|
pi_enabled: config.platform.packet_information,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||||
|
.map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
Device {
|
||||||
|
name: CStr::from_ptr(req.ifrn.name.as_ptr())
|
||||||
|
.to_string_lossy()
|
||||||
|
.into(),
|
||||||
|
queues,
|
||||||
|
ctl,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
device.configure(config)?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare a new request.
|
||||||
|
unsafe fn request(&self) -> ifreq {
|
||||||
|
let mut req: ifreq = mem::zeroed();
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
self.name.as_ptr() as *const c_char,
|
||||||
|
req.ifrn.name.as_mut_ptr(),
|
||||||
|
self.name.len(),
|
||||||
|
);
|
||||||
|
|
||||||
|
req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make the device persistent.
|
||||||
|
pub fn persist(&mut self) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
if tunsetpersist(self.as_raw_fd(), &1) < 0 {
|
||||||
|
Err(io::Error::last_os_error().into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the owner of the device.
|
||||||
|
pub fn user(&mut self, value: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
if tunsetowner(self.as_raw_fd(), &value) < 0 {
|
||||||
|
Err(io::Error::last_os_error().into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the group of the device.
|
||||||
|
pub fn group(&mut self, value: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
if tunsetgroup(self.as_raw_fd(), &value) < 0 {
|
||||||
|
Err(io::Error::last_os_error().into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether the device has packet information
|
||||||
|
pub fn has_packet_information(&mut self) -> bool {
|
||||||
|
self.queues[0].has_packet_information()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set non-blocking mode
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.queues[0].set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Device {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.queues[0].read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.queues[0].read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Device {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.queues[0].write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.queues[0].flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.queues[0].write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl D for Device {
|
||||||
|
type Queue = Queue;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let name = CString::new(value)?;
|
||||||
|
|
||||||
|
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||||
|
return Err(Error::NameTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut req = self.request();
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
name.as_ptr() as *const c_char,
|
||||||
|
req.ifru.newname.as_mut_ptr(),
|
||||||
|
value.len(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if siocsifname(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.name = value.into();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if value {
|
||||||
|
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||||
|
} else {
|
||||||
|
req.ifru.flags &= !IFF_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.addr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destination(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.netmask).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.netmask = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mtu(&self) -> Result<i32> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(req.ifru.mtu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.mtu = value;
|
||||||
|
|
||||||
|
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||||
|
self.queues.get_mut(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Device {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.queues[0].as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Device {
|
||||||
|
fn into_raw_fd(mut self) -> RawFd {
|
||||||
|
// It is Ok to swap the first queue with the last one, because the self will be dropped afterwards
|
||||||
|
let queue = self.queues.swap_remove(0);
|
||||||
|
queue.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Queue {
|
||||||
|
tun: Fd,
|
||||||
|
pi_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
pub fn has_packet_information(&mut self) -> bool {
|
||||||
|
self.pi_enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.tun.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Queue {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Queue {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Queue {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.tun.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Queue {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.tun.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Layer> for c_short {
|
||||||
|
fn from(layer: Layer) -> Self {
|
||||||
|
match layer {
|
||||||
|
Layer::L2 => IFF_TAP,
|
||||||
|
Layer::L3 => IFF_TUN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/platform/linux/mod.rs
Normal file
43
src/platform/linux/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Linux specific functionality.
|
||||||
|
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use self::device::{Device, Queue};
|
||||||
|
|
||||||
|
use crate::configuration::Configuration as C;
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// Linux-only interface configuration.
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub(crate) packet_information: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Configuration {
|
||||||
|
/// Enable or disable packet information, when enabled the first 4 bytes of
|
||||||
|
/// each packet is a header with flags and protocol type.
|
||||||
|
pub fn packet_information(&mut self, value: bool) -> &mut Self {
|
||||||
|
self.packet_information = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a TUN device with the given name.
|
||||||
|
pub fn create(configuration: &C) -> Result<Device> {
|
||||||
|
Device::new(configuration)
|
||||||
|
}
|
||||||
111
src/platform/linux/sys.rs
Normal file
111
src/platform/linux/sys.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Bindings to internal Linux stuff.
|
||||||
|
|
||||||
|
use ioctl::*;
|
||||||
|
use libc::sockaddr;
|
||||||
|
use libc::{c_char, c_int, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
||||||
|
|
||||||
|
pub const IFNAMSIZ: usize = 16;
|
||||||
|
|
||||||
|
pub const IFF_UP: c_short = 0x1;
|
||||||
|
pub const IFF_RUNNING: c_short = 0x40;
|
||||||
|
|
||||||
|
pub const IFF_TUN: c_short = 0x0001;
|
||||||
|
pub const IFF_TAP: c_short = 0x0002;
|
||||||
|
pub const IFF_NO_PI: c_short = 0x1000;
|
||||||
|
pub const IFF_MULTI_QUEUE: c_short = 0x0100;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifmap {
|
||||||
|
pub mem_start: c_ulong,
|
||||||
|
pub mem_end: c_ulong,
|
||||||
|
pub base_addr: c_ushort,
|
||||||
|
pub irq: c_uchar,
|
||||||
|
pub dma: c_uchar,
|
||||||
|
pub port: c_uchar,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifsu {
|
||||||
|
pub raw_hdlc_proto: *mut c_void,
|
||||||
|
pub cisco: *mut c_void,
|
||||||
|
pub fr: *mut c_void,
|
||||||
|
pub fr_pvc: *mut c_void,
|
||||||
|
pub fr_pvc_info: *mut c_void,
|
||||||
|
pub sync: *mut c_void,
|
||||||
|
pub te1: *mut c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct if_settings {
|
||||||
|
pub type_: c_uint,
|
||||||
|
pub size: c_uint,
|
||||||
|
pub ifsu: ifsu,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifrn {
|
||||||
|
pub name: [c_char; IFNAMSIZ],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifru {
|
||||||
|
pub addr: sockaddr,
|
||||||
|
pub dstaddr: sockaddr,
|
||||||
|
pub broadaddr: sockaddr,
|
||||||
|
pub netmask: sockaddr,
|
||||||
|
pub hwaddr: sockaddr,
|
||||||
|
|
||||||
|
pub flags: c_short,
|
||||||
|
pub ivalue: c_int,
|
||||||
|
pub mtu: c_int,
|
||||||
|
pub map: ifmap,
|
||||||
|
pub slave: [c_char; IFNAMSIZ],
|
||||||
|
pub newname: [c_char; IFNAMSIZ],
|
||||||
|
pub data: *mut c_void,
|
||||||
|
pub settings: if_settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifreq {
|
||||||
|
pub ifrn: ifrn,
|
||||||
|
pub ifru: ifru,
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl!(bad read siocgifflags with 0x8913; ifreq);
|
||||||
|
ioctl!(bad write siocsifflags with 0x8914; ifreq);
|
||||||
|
ioctl!(bad read siocgifaddr with 0x8915; ifreq);
|
||||||
|
ioctl!(bad write siocsifaddr with 0x8916; ifreq);
|
||||||
|
ioctl!(bad read siocgifdstaddr with 0x8917; ifreq);
|
||||||
|
ioctl!(bad write siocsifdstaddr with 0x8918; ifreq);
|
||||||
|
ioctl!(bad read siocgifbrdaddr with 0x8919; ifreq);
|
||||||
|
ioctl!(bad write siocsifbrdaddr with 0x891a; ifreq);
|
||||||
|
ioctl!(bad read siocgifnetmask with 0x891b; ifreq);
|
||||||
|
ioctl!(bad write siocsifnetmask with 0x891c; ifreq);
|
||||||
|
ioctl!(bad read siocgifmtu with 0x8921; ifreq);
|
||||||
|
ioctl!(bad write siocsifmtu with 0x8922; ifreq);
|
||||||
|
ioctl!(bad write siocsifname with 0x8923; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write tunsetiff with b'T', 202; c_int);
|
||||||
|
ioctl!(write tunsetpersist with b'T', 203; c_int);
|
||||||
|
ioctl!(write tunsetowner with b'T', 204; c_int);
|
||||||
|
ioctl!(write tunsetgroup with b'T', 206; c_int);
|
||||||
438
src/platform/macos/device.rs
Normal file
438
src/platform/macos/device.rs
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::mem;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libc;
|
||||||
|
use libc::{c_char, c_uint, c_void, sockaddr, socklen_t, AF_INET, SOCK_DGRAM};
|
||||||
|
|
||||||
|
use crate::configuration::{Configuration, Layer};
|
||||||
|
use crate::device::Device as D;
|
||||||
|
use crate::error::*;
|
||||||
|
use crate::platform::macos::sys::*;
|
||||||
|
use crate::platform::posix::{self, Fd, SockAddr};
|
||||||
|
|
||||||
|
/// A TUN device using the TUN macOS driver.
|
||||||
|
pub struct Device {
|
||||||
|
name: String,
|
||||||
|
queue: Queue,
|
||||||
|
ctl: Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device {
|
||||||
|
/// Create a new `Device` for the given `Configuration`.
|
||||||
|
pub fn new(config: &Configuration) -> Result<Self> {
|
||||||
|
let id = if let Some(name) = config.name.as_ref() {
|
||||||
|
if name.len() > IFNAMSIZ {
|
||||||
|
return Err(Error::NameTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !name.starts_with("utun") {
|
||||||
|
return Err(Error::InvalidName);
|
||||||
|
}
|
||||||
|
|
||||||
|
name[4..].parse()?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if config.layer.filter(|l| *l != Layer::L3).is_some() {
|
||||||
|
return Err(Error::UnsupportedLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
let queues_number = config.queues.unwrap_or(1);
|
||||||
|
if queues_number != 1 {
|
||||||
|
return Err(Error::InvalidQueuesNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut device = unsafe {
|
||||||
|
let tun = Fd::new(libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL))
|
||||||
|
.map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
let mut info = ctl_info {
|
||||||
|
ctl_id: 0,
|
||||||
|
ctl_name: {
|
||||||
|
let mut buffer = [0; 96];
|
||||||
|
for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) {
|
||||||
|
*o = *i as _;
|
||||||
|
}
|
||||||
|
buffer
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctliocginfo(tun.0, &mut info as *mut _ as *mut _) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let addr = sockaddr_ctl {
|
||||||
|
sc_id: info.ctl_id,
|
||||||
|
sc_len: mem::size_of::<sockaddr_ctl>() as _,
|
||||||
|
sc_family: AF_SYSTEM,
|
||||||
|
ss_sysaddr: AF_SYS_CONTROL,
|
||||||
|
sc_unit: id as c_uint,
|
||||||
|
sc_reserved: [0; 5],
|
||||||
|
};
|
||||||
|
|
||||||
|
if libc::connect(
|
||||||
|
tun.0,
|
||||||
|
&addr as *const sockaddr_ctl as *const sockaddr,
|
||||||
|
mem::size_of_val(&addr) as socklen_t,
|
||||||
|
) < 0
|
||||||
|
{
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name = [0u8; 64];
|
||||||
|
let mut name_len: socklen_t = 64;
|
||||||
|
|
||||||
|
if libc::getsockopt(
|
||||||
|
tun.0,
|
||||||
|
SYSPROTO_CONTROL,
|
||||||
|
UTUN_OPT_IFNAME,
|
||||||
|
&mut name as *mut _ as *mut c_void,
|
||||||
|
&mut name_len as *mut socklen_t,
|
||||||
|
) < 0
|
||||||
|
{
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||||
|
.map_err(|_| io::Error::last_os_error())?;
|
||||||
|
|
||||||
|
Device {
|
||||||
|
name: CStr::from_ptr(name.as_ptr() as *const c_char)
|
||||||
|
.to_string_lossy()
|
||||||
|
.into(),
|
||||||
|
queue: Queue { tun: tun },
|
||||||
|
ctl: ctl,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
device.configure(&config)?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare a new request.
|
||||||
|
pub unsafe fn request(&self) -> ifreq {
|
||||||
|
let mut req: ifreq = mem::zeroed();
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
self.name.as_ptr() as *const c_char,
|
||||||
|
req.ifrn.name.as_mut_ptr(),
|
||||||
|
self.name.len(),
|
||||||
|
);
|
||||||
|
|
||||||
|
req
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the IPv4 alias of the device.
|
||||||
|
pub fn set_alias(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req: ifaliasreq = mem::zeroed();
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
self.name.as_ptr() as *const c_char,
|
||||||
|
req.ifran.as_mut_ptr(),
|
||||||
|
self.name.len(),
|
||||||
|
);
|
||||||
|
|
||||||
|
req.addr = SockAddr::from(addr).into();
|
||||||
|
req.broadaddr = SockAddr::from(broadaddr).into();
|
||||||
|
req.mask = SockAddr::from(mask).into();
|
||||||
|
|
||||||
|
if siocaifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split the interface into a `Reader` and `Writer`.
|
||||||
|
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||||
|
let fd = Arc::new(self.queue.tun);
|
||||||
|
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether the device has packet information
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
self.queue.has_packet_information()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set non-blocking mode
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.queue.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Device {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Device {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.queue.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.queue.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl D for Device {
|
||||||
|
type Queue = Queue;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: Cannot set interface name on Darwin.
|
||||||
|
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||||
|
Err(Error::InvalidName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if value {
|
||||||
|
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||||
|
} else {
|
||||||
|
req.ifru.flags &= !IFF_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn address(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.addr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destination(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddr::unchecked(&req.ifru.addr).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.addr = SockAddr::from(value).into();
|
||||||
|
|
||||||
|
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mtu(&self) -> Result<i32> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
|
||||||
|
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(req.ifru.mtu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
let mut req = self.request();
|
||||||
|
req.ifru.mtu = value;
|
||||||
|
|
||||||
|
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||||
|
return Err(io::Error::last_os_error().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||||
|
if index > 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(&mut self.queue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Device {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.queue.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Device {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.queue.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Queue {
|
||||||
|
tun: Fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Queue {
|
||||||
|
pub fn has_packet_information(&self) -> bool {
|
||||||
|
// on macos this is always the case
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
self.tun.set_nonblock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Queue {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.tun.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Queue {
|
||||||
|
fn into_raw_fd(self) -> RawFd {
|
||||||
|
self.tun.into_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Queue {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.tun.read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Queue {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.tun.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.tun.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.tun.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/platform/macos/mod.rs
Normal file
32
src/platform/macos/mod.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! macOS specific functionality.
|
||||||
|
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
pub use self::device::{Device, Queue};
|
||||||
|
|
||||||
|
use crate::configuration::Configuration as C;
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// macOS-only interface configuration.
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub struct Configuration {}
|
||||||
|
|
||||||
|
/// Create a TUN device with the given name.
|
||||||
|
pub fn create(configuration: &C) -> Result<Device> {
|
||||||
|
Device::new(&configuration)
|
||||||
|
}
|
||||||
138
src/platform/macos/sys.rs
Normal file
138
src/platform/macos/sys.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Bindings to internal macOS stuff.
|
||||||
|
|
||||||
|
use ioctl::*;
|
||||||
|
use libc::sockaddr;
|
||||||
|
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void};
|
||||||
|
|
||||||
|
pub const IFNAMSIZ: usize = 16;
|
||||||
|
|
||||||
|
pub const IFF_UP: c_short = 0x1;
|
||||||
|
pub const IFF_RUNNING: c_short = 0x40;
|
||||||
|
|
||||||
|
pub const AF_SYS_CONTROL: c_ushort = 2;
|
||||||
|
pub const AF_SYSTEM: c_char = 32;
|
||||||
|
pub const PF_SYSTEM: c_int = AF_SYSTEM as c_int;
|
||||||
|
pub const SYSPROTO_CONTROL: c_int = 2;
|
||||||
|
pub const UTUN_OPT_IFNAME: c_int = 2;
|
||||||
|
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ctl_info {
|
||||||
|
pub ctl_id: c_uint,
|
||||||
|
pub ctl_name: [c_char; 96],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct sockaddr_ctl {
|
||||||
|
pub sc_len: c_char,
|
||||||
|
pub sc_family: c_char,
|
||||||
|
pub ss_sysaddr: c_ushort,
|
||||||
|
pub sc_id: c_uint,
|
||||||
|
pub sc_unit: c_uint,
|
||||||
|
pub sc_reserved: [c_uint; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifrn {
|
||||||
|
pub name: [c_char; IFNAMSIZ],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifdevmtu {
|
||||||
|
pub current: c_int,
|
||||||
|
pub min: c_int,
|
||||||
|
pub max: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifku {
|
||||||
|
pub ptr: *mut c_void,
|
||||||
|
pub value: c_int,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifkpi {
|
||||||
|
pub module_id: c_uint,
|
||||||
|
pub type_: c_uint,
|
||||||
|
pub ifku: ifku,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union ifru {
|
||||||
|
pub addr: sockaddr,
|
||||||
|
pub dstaddr: sockaddr,
|
||||||
|
pub broadaddr: sockaddr,
|
||||||
|
|
||||||
|
pub flags: c_short,
|
||||||
|
pub metric: c_int,
|
||||||
|
pub mtu: c_int,
|
||||||
|
pub phys: c_int,
|
||||||
|
pub media: c_int,
|
||||||
|
pub intval: c_int,
|
||||||
|
pub data: *mut c_void,
|
||||||
|
pub devmtu: ifdevmtu,
|
||||||
|
pub wake_flags: c_uint,
|
||||||
|
pub route_refcnt: c_uint,
|
||||||
|
pub cap: [c_int; 2],
|
||||||
|
pub functional_type: c_uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifreq {
|
||||||
|
pub ifrn: ifrn,
|
||||||
|
pub ifru: ifru,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct ifaliasreq {
|
||||||
|
pub ifran: [c_char; IFNAMSIZ],
|
||||||
|
pub addr: sockaddr,
|
||||||
|
pub broadaddr: sockaddr,
|
||||||
|
pub mask: sockaddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl!(readwrite ctliocginfo with 'N', 3; ctl_info);
|
||||||
|
|
||||||
|
ioctl!(write siocsifflags with 'i', 16; ifreq);
|
||||||
|
ioctl!(readwrite siocgifflags with 'i', 17; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocsifaddr with 'i', 12; ifreq);
|
||||||
|
ioctl!(readwrite siocgifaddr with 'i', 33; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocsifdstaddr with 'i', 14; ifreq);
|
||||||
|
ioctl!(readwrite siocgifdstaddr with 'i', 34; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocsifbrdaddr with 'i', 19; ifreq);
|
||||||
|
ioctl!(readwrite siocgifbrdaddr with 'i', 35; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocsifnetmask with 'i', 22; ifreq);
|
||||||
|
ioctl!(readwrite siocgifnetmask with 'i', 37; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocsifmtu with 'i', 52; ifreq);
|
||||||
|
ioctl!(readwrite siocgifmtu with 'i', 51; ifreq);
|
||||||
|
|
||||||
|
ioctl!(write siocaifaddr with 'i', 26; ifaliasreq);
|
||||||
|
ioctl!(write siocdifaddr with 'i', 25; ifreq);
|
||||||
70
src/platform/mod.rs
Normal file
70
src/platform/mod.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! Platform specific modules.
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub mod posix;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub mod linux;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use self::linux::{create, Configuration, Device, Queue};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub mod macos;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub use self::macos::{create, Configuration, Device, Queue};
|
||||||
|
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
pub mod ios;
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
pub use self::ios::{create, Configuration, Device, Queue};
|
||||||
|
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub mod android;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub use self::android::{create, Configuration, Device, Queue};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::configuration::Configuration;
|
||||||
|
use crate::device::Device;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create() {
|
||||||
|
let dev = super::create(
|
||||||
|
Configuration::default()
|
||||||
|
.name("utun6")
|
||||||
|
.address("192.168.50.1")
|
||||||
|
.netmask("255.255.0.0")
|
||||||
|
.mtu(1400)
|
||||||
|
.up(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"192.168.50.1".parse::<Ipv4Addr>().unwrap(),
|
||||||
|
dev.address().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
"255.255.0.0".parse::<Ipv4Addr>().unwrap(),
|
||||||
|
dev.netmask().unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(1400, dev.mtu().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
124
src/platform/posix/fd.rs
Normal file
124
src/platform/posix/fd.rs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||||
|
|
||||||
|
use crate::error::*;
|
||||||
|
use libc::{self, fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
|
||||||
|
|
||||||
|
/// POSIX file descriptor support for `io` traits.
|
||||||
|
pub struct Fd(pub RawFd);
|
||||||
|
|
||||||
|
impl Fd {
|
||||||
|
pub fn new(value: RawFd) -> Result<Self> {
|
||||||
|
if value < 0 {
|
||||||
|
return Err(Error::InvalidDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Fd(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable non-blocking mode
|
||||||
|
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||||
|
match unsafe { fcntl(self.0, F_SETFL, fcntl(self.0, F_GETFL) | O_NONBLOCK) } {
|
||||||
|
0 => Ok(()),
|
||||||
|
_ => Err(io::Error::last_os_error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Fd {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
|
||||||
|
|
||||||
|
if amount < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(amount as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let iov = bufs.as_ptr().cast();
|
||||||
|
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||||
|
|
||||||
|
let n = libc::readv(self.0, iov, iovcnt);
|
||||||
|
if n < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Fd {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
|
||||||
|
|
||||||
|
if amount < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(amount as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let iov = bufs.as_ptr().cast();
|
||||||
|
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||||
|
|
||||||
|
let n = libc::writev(self.0, iov, iovcnt);
|
||||||
|
if n < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Fd {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for Fd {
|
||||||
|
fn into_raw_fd(mut self) -> RawFd {
|
||||||
|
let fd = self.0;
|
||||||
|
self.0 = -1;
|
||||||
|
fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Fd {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if self.0 >= 0 {
|
||||||
|
libc::close(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/platform/posix/mod.rs
Normal file
24
src/platform/posix/mod.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
//! POSIX compliant support.
|
||||||
|
|
||||||
|
mod sockaddr;
|
||||||
|
pub use self::sockaddr::SockAddr;
|
||||||
|
|
||||||
|
mod fd;
|
||||||
|
pub use self::fd::Fd;
|
||||||
|
|
||||||
|
mod split;
|
||||||
|
pub use self::split::{Reader, Writer};
|
||||||
96
src/platform/posix/sockaddr.rs
Normal file
96
src/platform/posix/sockaddr.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
use libc::c_uchar;
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
use libc::c_ushort;
|
||||||
|
|
||||||
|
use libc::AF_INET as _AF_INET;
|
||||||
|
use libc::{in_addr, sockaddr, sockaddr_in};
|
||||||
|
|
||||||
|
use crate::error::*;
|
||||||
|
|
||||||
|
/// A wrapper for `sockaddr_in`.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct SockAddr(sockaddr_in);
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
|
const AF_INET: c_ushort = _AF_INET as c_ushort;
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
const AF_INET: c_uchar = _AF_INET as c_uchar;
|
||||||
|
|
||||||
|
impl SockAddr {
|
||||||
|
/// Create a new `SockAddr` from a generic `sockaddr`.
|
||||||
|
pub fn new(value: &sockaddr) -> Result<Self> {
|
||||||
|
if value.sa_family != AF_INET {
|
||||||
|
return Err(Error::InvalidAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { Self::unchecked(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Create a new `SockAddr` and not check the source.
|
||||||
|
pub unsafe fn unchecked(value: &sockaddr) -> Result<Self> {
|
||||||
|
Ok(SockAddr(ptr::read(value as *const _ as *const _)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Get a generic pointer to the `SockAddr`.
|
||||||
|
pub unsafe fn as_ptr(&self) -> *const sockaddr {
|
||||||
|
&self.0 as *const _ as *const sockaddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ipv4Addr> for SockAddr {
|
||||||
|
fn from(ip: Ipv4Addr) -> SockAddr {
|
||||||
|
let octets = ip.octets();
|
||||||
|
let mut addr = unsafe { mem::zeroed::<sockaddr_in>() };
|
||||||
|
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = 0;
|
||||||
|
addr.sin_addr = in_addr {
|
||||||
|
s_addr: u32::from_ne_bytes(octets),
|
||||||
|
};
|
||||||
|
|
||||||
|
SockAddr(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SockAddr> for Ipv4Addr {
|
||||||
|
fn from(addr: SockAddr) -> Ipv4Addr {
|
||||||
|
let ip = addr.0.sin_addr.s_addr;
|
||||||
|
let [a, b, c, d] = ip.to_ne_bytes();
|
||||||
|
|
||||||
|
Ipv4Addr::new(a, b, c, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SockAddr> for sockaddr {
|
||||||
|
fn from(addr: SockAddr) -> sockaddr {
|
||||||
|
unsafe { mem::transmute(addr.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SockAddr> for sockaddr_in {
|
||||||
|
fn from(addr: SockAddr) -> sockaddr_in {
|
||||||
|
addr.0
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/platform/posix/split.rs
Normal file
105
src/platform/posix/split.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// Version 2, December 2004
|
||||||
|
//
|
||||||
|
// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
|
||||||
|
//
|
||||||
|
// Everyone is permitted to copy and distribute verbatim or modified
|
||||||
|
// copies of this license document, and changing it is allowed as long
|
||||||
|
// as the name is changed.
|
||||||
|
//
|
||||||
|
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
|
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
//
|
||||||
|
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||||
|
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::mem;
|
||||||
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::platform::posix::Fd;
|
||||||
|
use libc;
|
||||||
|
|
||||||
|
/// Read-only end for a file descriptor.
|
||||||
|
pub struct Reader(pub(crate) Arc<Fd>);
|
||||||
|
|
||||||
|
/// Write-only end for a file descriptor.
|
||||||
|
pub struct Writer(pub(crate) Arc<Fd>);
|
||||||
|
|
||||||
|
impl Read for Reader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let amount = libc::read(self.0.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len());
|
||||||
|
|
||||||
|
if amount < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(amount as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let mut msg: libc::msghdr = mem::zeroed();
|
||||||
|
// msg.msg_name: NULL
|
||||||
|
// msg.msg_namelen: 0
|
||||||
|
msg.msg_iov = bufs.as_mut_ptr().cast();
|
||||||
|
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||||
|
|
||||||
|
let n = libc::recvmsg(self.0.as_raw_fd(), &mut msg, 0);
|
||||||
|
if n < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Writer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let amount = libc::write(self.0.as_raw_fd(), buf.as_ptr() as *const _, buf.len());
|
||||||
|
|
||||||
|
if amount < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(amount as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
let mut msg: libc::msghdr = mem::zeroed();
|
||||||
|
// msg.msg_name = NULL
|
||||||
|
// msg.msg_namelen = 0
|
||||||
|
msg.msg_iov = bufs.as_ptr() as *mut _;
|
||||||
|
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||||
|
|
||||||
|
let n = libc::sendmsg(self.0.as_raw_fd(), &msg, 0);
|
||||||
|
if n < 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(n as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Reader {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRawFd for Writer {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.0.as_raw_fd()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user