From b642112b4da77460eef63997c8cb103a64368c1b Mon Sep 17 00:00:00 2001 From: core Date: Wed, 21 Dec 2022 12:26:10 -0500 Subject: [PATCH] [docs/cli][cli/pproc] add more documentation / restructure cli to put more routing stuff into the engine for crossplatformness --- quicktap-cli/Cargo.toml | 6 +- quicktap-cli/README.md | 20 ++++++ quicktap-cli/src/cli.rs | 9 +++ quicktap-cli/src/command.rs | 6 ++ quicktap-cli/src/device.rs | 28 -------- quicktap-cli/src/main.rs | 137 ++++++++++++++++++++++++++++++++++-- quicktap-cli/src/pproc.rs | 42 +++++++++++ quicktap/Cargo.toml | 6 +- quicktap/README.md | 2 +- quicktap/build.rs | 3 + quicktap/src/lib.rs | 7 +- 11 files changed, 230 insertions(+), 36 deletions(-) create mode 100644 quicktap-cli/README.md create mode 100644 quicktap-cli/src/cli.rs create mode 100644 quicktap-cli/src/command.rs delete mode 100644 quicktap-cli/src/device.rs create mode 100644 quicktap-cli/src/pproc.rs create mode 100644 quicktap/build.rs diff --git a/quicktap-cli/Cargo.toml b/quicktap-cli/Cargo.toml index 309d0b1..2053627 100644 --- a/quicktap-cli/Cargo.toml +++ b/quicktap-cli/Cargo.toml @@ -6,4 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -quicktap = { path = "../quicktap", version = "0.1.0" } \ No newline at end of file +quicktap = { path = "../quicktap", version = "0.1.0" } +clap = { version = "4.0.29", features = ["derive"]} +daemonize = "0.4.1" +log = "0.4.17" +simple_logger = "4.0.0" \ No newline at end of file diff --git a/quicktap-cli/README.md b/quicktap-cli/README.md new file mode 100644 index 0000000..1aec603 --- /dev/null +++ b/quicktap-cli/README.md @@ -0,0 +1,20 @@ +# Quicktap CLI +This is a WireGuard xplatform implementation for using [Quicktap Engine](../quicktap/README.md) with as many platforms as possible. + +Here's a support table for the CLI: + +| | IPv4 | IPv6 | +|---------|---------------|---------------| +| Linux | ✓ | ✓ | +| NetBSD | ⚠ | x | +| MacOS | x | x | +| Windows | x | x | + +Key: +- ✓ - Fully supported +- ⚠ - Partially supported +- x - Not supported +- ‡ - Support planned +- † - Unmaintained + +Help is wanted! Improve existing implementations or pick out an unmaintained or unsupported one and help the project get better. \ No newline at end of file diff --git a/quicktap-cli/src/cli.rs b/quicktap-cli/src/cli.rs new file mode 100644 index 0000000..eb4f224 --- /dev/null +++ b/quicktap-cli/src/cli.rs @@ -0,0 +1,9 @@ +use clap::{ArgAction, Parser}; + +#[derive(Parser)] +#[command(author = "The Quicktap Maintainers", version, about = "A userspace WireGuard implementation written in Rust", long_about = None)] +pub struct Cli { + #[arg(short, long, action = ArgAction::SetTrue)] + pub foreground: bool, + pub interface_name: String +} \ No newline at end of file diff --git a/quicktap-cli/src/command.rs b/quicktap-cli/src/command.rs new file mode 100644 index 0000000..894806f --- /dev/null +++ b/quicktap-cli/src/command.rs @@ -0,0 +1,6 @@ +pub enum PacketThreadCommand { + UpdateConfig(UpdateConfigCommand), + Stop +} + +pub struct UpdateConfigCommand {} \ No newline at end of file diff --git a/quicktap-cli/src/device.rs b/quicktap-cli/src/device.rs deleted file mode 100644 index b9cd9c7..0000000 --- a/quicktap-cli/src/device.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::error::Error; -use std::io; -use std::net::Ipv4Addr; - -use quicktap::cidr::{IpInet, Ipv4Inet}; -use quicktap::drivers::tun::TunDevice; -use quicktap::drivers::tungeneric::{GenericDriver, GenericInterface}; - -pub fn packet_eventloop() -> Result<(), Box> { - let mut device = TunDevice::new(&GenericInterface { - addresses: vec![IpInet::V4(Ipv4Inet::new(Ipv4Addr::new(10, 19, 2, 2), 16).unwrap())], - mtu: None, - name: "ela1".to_string(), - }).unwrap(); - loop { - let packet = match device.read() { - Ok(p) => Some(p), - Err(e) => match e.kind() { - io::ErrorKind::WouldBlock => None, - _ => return Err(e.into()) - } - }; - - if let Some(p) = packet { - println!("{:?}", p.header); - } - } -} \ No newline at end of file diff --git a/quicktap-cli/src/main.rs b/quicktap-cli/src/main.rs index 26c9483..4845594 100644 --- a/quicktap-cli/src/main.rs +++ b/quicktap-cli/src/main.rs @@ -1,13 +1,142 @@ -use std::net::Ipv4Addr; +use std::collections::HashMap; +use std::fs::File; +use std::io; +use std::io::Read; +use std::net::{Ipv4Addr, SocketAddr}; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::path::Path; +use std::sync::mpsc::channel; +use std::thread::{sleep, spawn}; +use std::time::Duration; + +use clap::Parser; +use daemonize::Daemonize; +use log::{error, info}; use quicktap::cidr::{IpInet, Ipv4Inet}; use quicktap::drivers::tun::TunDevice; use quicktap::drivers::tungeneric::{GenericDriver, GenericInterface}; -use crate::device::packet_eventloop; +use crate::cli::Cli; +use crate::command::PacketThreadCommand; +use crate::pproc::packet_thread; -pub mod device; +pub mod cli; +pub mod pproc; +pub mod command; fn main() { - packet_eventloop().unwrap(); + simple_logger::init().unwrap(); + + let args = Cli::parse(); + + info!("Hello, world! {}", quicktap::version()); + info!("Starting up"); + + if !args.foreground { + info!("Forking to background"); + + let stdout = match File::create("/var/log/e4.log") { + Ok(f) => f, + Err(e) => { + error!("Failed to open stdout file: {}", e); + std::process::exit(8); + } + }; + let stderr = match File::create("/var/log/e4.err") { + Ok(f) => f, + Err(e) => { + error!("Failed to open stderr file: {}", e); + std::process::exit(9); + } + }; + + let daemon_opts = Daemonize::new() + .stdout(stdout) + .stderr(stderr); + match daemon_opts.start() { + Ok(_) => info!("Forked to background successfully"), + Err(e) => { + error!("Failed to daemonize quicktap-cli worker process: {}", e); + std::process::exit(1); + } + } + } + + info!("Creating unconfigured device"); + + let mut device = match TunDevice::new(&GenericInterface { + addresses: vec![], + mtu: None, + name: args.interface_name.clone(), + }) { + Ok(d) => d, + Err(e) => { + error!("Failed to create interface: {}", e); + std::process::exit(2); + } + }; + + info!("Device created; opening listening socket"); + + let mut config_listener = match UnixListener::bind(format!("/var/run/{}.sock", args.interface_name)) { + Ok(s) => s, + Err(e) => { + error!("Failed to open listen socket /var/run/{}.sock: {}", args.interface_name, e); + std::process::exit(3); + } + }; + match config_listener.set_nonblocking(true) { + Ok(_) => (), + Err(e) => { + error!("Failed to set stream as nonblocking: {}", e); + std::process::exit(4); + } + }; + + info!("Spawning packet processor thread"); + + let (tx, rx) = channel::(); + + let pproc_thread = spawn(|| { + match packet_thread(device, rx) { + Ok(_) => (), + Err(e) => { + error!("Error in pproc_thread: {}", e); + } + } + }); + + sleep(Duration::from_secs(10)); + + match tx.send(PacketThreadCommand::Stop) { + Ok(_) => (), + Err(e) => { + error!("Error sending stop command to packet thread: {}", e); + std::process::exit(6); + } + } + + info!("Waiting for packet thread to stop"); + match pproc_thread.join() { + Ok(_) => (), + Err(e) => { + error!("Error joining thread: {:?}", e); + std::process::exit(5); + } + } + + info!("Cleaning up"); + + if Path::new(&format!("/var/run/{}.sock", args.interface_name)).exists() { + match std::fs::remove_file(format!("/var/run/{}.sock", args.interface_name)) { + Ok(_) => (), + Err(e) => { + error!("Failed to remove existing socket: {}", e); + std::process::exit(7); + } + } + } + + info!("Cya!"); } diff --git a/quicktap-cli/src/pproc.rs b/quicktap-cli/src/pproc.rs new file mode 100644 index 0000000..7f363c6 --- /dev/null +++ b/quicktap-cli/src/pproc.rs @@ -0,0 +1,42 @@ +use std::error::Error; +use std::io; +use std::sync::mpsc::Receiver; + +use log::{error, info}; + +use quicktap::drivers::tun::TunDevice; +use quicktap::drivers::tungeneric::GenericDriver; + +use crate::command::PacketThreadCommand; + +pub fn packet_thread(mut device: TunDevice, rx: Receiver) -> Result<(), Box> { + loop { + // Check command channel + let command = match rx.try_recv() { + Ok(d) => Some(d), + Err(e) if matches!(e, std::sync::mpsc::TryRecvError::Empty) => None, + Err(e) => return Err(e.into()) + }; + if let Some(cmd) = command { + if let PacketThreadCommand::Stop = cmd { + info!("packet thread: recieved command to stop by control process"); + return Ok(()) + } + } + + let packet = match device.read() { + Ok(p) => Some(p), + Err(e) => match e.kind() { + io::ErrorKind::WouldBlock => None, + _ => { + error!("Error while reading packets from interface: {}", e); + return Err(e.into()); + } + } + }; + + if let Some(p) = packet { + info!("recv_packet_on_tun {:?}", p.header); + } + } +} \ No newline at end of file diff --git a/quicktap/Cargo.toml b/quicktap/Cargo.toml index 0463f51..e378e67 100644 --- a/quicktap/Cargo.toml +++ b/quicktap/Cargo.toml @@ -14,10 +14,14 @@ x25519-dalek = "2.0.0-pre.1" rand = "0.8.5" hmac = "0.12.1" chacha20poly1305 = "0.10.1" +build-info = "0.0.29" [target.'cfg(unix)'.dependencies] tun = "0.5.4" [dev-dependencies] hex_lit = "0.1.1" -hex = "0.4.3" \ No newline at end of file +hex = "0.4.3" + +[build-dependencies] +build-info-build = "0.0.29" \ No newline at end of file diff --git a/quicktap/README.md b/quicktap/README.md index 6619686..e330e2f 100644 --- a/quicktap/README.md +++ b/quicktap/README.md @@ -1,4 +1,4 @@ -# Quicktap +# Quicktap Engine Quicktap is a (hopefully) standard-compliant Rust implementation of the [WireGuard protocol](https://wireguard.org). It was designed to be a simpler alternative to Cloudflare's [boringtun](https://github.com/cloudflare/boringtun). diff --git a/quicktap/build.rs b/quicktap/build.rs new file mode 100644 index 0000000..587dfb9 --- /dev/null +++ b/quicktap/build.rs @@ -0,0 +1,3 @@ +fn main() { + build_info_build::build_script(); +} \ No newline at end of file diff --git a/quicktap/src/lib.rs b/quicktap/src/lib.rs index 8e6fbb5..f2acf2a 100644 --- a/quicktap/src/lib.rs +++ b/quicktap/src/lib.rs @@ -15,4 +15,9 @@ pub use cidr; pub mod drivers; pub mod qcrypto; -pub mod noise; \ No newline at end of file +pub mod noise; + +/// Gets the compile-time versioning information for the engine build. +pub const fn version() -> &'static str { + build_info::format!("quicktap engine v{} ({}) built at {} with {}", $.crate_info.version, $.version_control.unwrap().git().unwrap().commit_short_id, $.timestamp, $.compiler) +}