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)
+}