From 6e5f5636a3d34d3edd5c901c8b67f6444a116ad5 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 14 Jan 2024 19:06:28 +0100 Subject: Add TUN functionality --- Cargo.lock | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 +++ src/main.rs | 85 +++++++++++++++++++++++++++++++++++++++++++ src/ui.rs | 2 +- 4 files changed, 207 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 6b9c4c2..1ae0ba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -345,6 +345,26 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "c2rust-bitfields" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b43c3f07ab0ef604fa6f595aa46ec2f8a22172c975e186f6f5bf9829a3b72c41" +dependencies = [ + "c2rust-bitfields-derive", +] + +[[package]] +name = "c2rust-bitfields-derive" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3cbc102e2597c9744c8bd8c15915d554300601c91a079430d309816b0912545" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "cats-radio-node" version = "0.1.0" @@ -354,6 +374,8 @@ dependencies = [ "askama_axum", "async-stream", "axum 0.7.4", + "futures", + "futures-core", "half", "ham-cats", "log", @@ -367,6 +389,7 @@ dependencies = [ "toml", "tonic", "tower-http", + "tun", ] [[package]] @@ -636,6 +659,21 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -680,6 +718,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -698,8 +747,10 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1057,6 +1108,12 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "ioctl-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd11f3a29434026f5ff98c730b668ba74b1033637b8817940b54d040696133c" + [[package]] name = "itertools" version = "0.12.0" @@ -1093,6 +1150,16 @@ version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "libm" version = "0.2.8" @@ -2457,6 +2524,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tun" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0adb9992bbd5ca76f3847ed579ad4ee8defb2ec2eea918cceef17ccc66fa4fd4" +dependencies = [ + "byteorder", + "bytes", + "futures-core", + "ioctl-sys", + "libc", + "log", + "thiserror", + "tokio", + "tokio-util", + "wintun", +] + [[package]] name = "typenum" version = "1.17.0" @@ -2573,6 +2658,25 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2714,6 +2818,19 @@ dependencies = [ "memchr", ] +[[package]] +name = "wintun" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b83b0eca06dd125dbcd48a45327c708a6da8aada3d95a3f06db0ce4b17e0d4" +dependencies = [ + "c2rust-bitfields", + "libloading", + "log", + "thiserror", + "windows", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 7706d87..23ccf35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "sqlite"]} tokio = { version = "1", features = ["full"] } tower-http = { version = "0.5.0", features = ["fs"] } +futures-core = "0.3" +futures= "0.3" +tun = { version = "0.6", features = ["async"] } + ham-cats = { git = "https://gitlab.scd31.com/cats/ham-cats", rev = "d22f541c9a7e1c3a6c6e9449d87212b060f5edfb" } half = { version = "2" } rf4463 = { git = "https://gitlab.scd31.com/stephen/rf4463-lib", rev = "79c8def87540f8ab2663bfa3c9fb13db344ef84e" } diff --git a/src/main.rs b/src/main.rs index 42044be..fd4cc03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use anyhow::{anyhow, Context}; use log::{debug, info, warn, error}; use std::sync::{Arc, Mutex}; use tokio::net::UdpSocket; @@ -17,12 +18,41 @@ struct AppState { type SharedState = Arc>; +const TUN_MTU : usize = 255; + #[tokio::main] async fn main() -> std::io::Result<()> { simple_logger::SimpleLogger::new().env().init().unwrap(); let conf = config::Config::load().expect("Could not load config"); + let (mut tun_sink, tun_source) = if conf.tunnel.enabled { + let tunnelconf = conf.tunnel.clone(); + let mut tunconfig = tun::Configuration::default(); + + tunconfig + .address(tunnelconf.local_ip) + .netmask(tunnelconf.netmask) + // TODO MTU could be increased to something a bit smaller than MAX_PACKET_LEN, but Arbitrary only have 255 byte + // payloads. Maybe propose a Tunnel whisker if it makes sense. + .mtu(TUN_MTU.try_into().unwrap()) + // TODO is .destination() needed? + .up(); + + #[cfg(target_os = "linux")] + tunconfig.platform(|tunconfig| { + tunconfig.packet_information(true); + }); + + let dev = tun::create_as_async(&tunconfig).unwrap(); + use futures::stream::StreamExt; + let (tun_sink, tun_source) = dev.into_framed().split(); + (Some(tun_sink), Some(tun_source)) + } + else { + (None, None) + }; + let (radio_rx_queue, mut packet_receive) = mpsc::channel(16); let (packet_send, mut radio_tx_queue) = mpsc::channel::>(16); @@ -91,6 +121,15 @@ async fn main() -> std::io::Result<()> { if let Err(e) = db.store_packet(&packet_data).await { warn!("Failed to write to sqlite: {}", e); } + + if let Some(sink) = &mut tun_sink { + for arb in packet.arbitrary_iter() { + use futures::SinkExt; + if let Err(e) = sink.send(tun::TunPacket::new(arb.0.to_vec())).await { + warn!("Failed to send to TUN: {}", e); + } + } + } } Err(e) => { warn!("Failed to decode packet: {}", e); @@ -102,6 +141,52 @@ async fn main() -> std::io::Result<()> { warn!("Packet receive task stopping"); }); + let shared_state_tunnel = shared_state.clone(); + if let Some(mut source) = tun_source { + tokio::task::spawn(async move { + use futures::stream::StreamExt; + while let Some(packet_from_tun) = source.next().await { + match packet_from_tun { + Ok(ip_packet) if ip_packet.get_bytes().len() <= TUN_MTU => { + println!("RX: {} bytes", ip_packet.get_bytes().len()); + + let config = shared_state_tunnel.lock().unwrap().conf.clone(); + + fn build_tun_packet(config: config::Config, ip_packet: &[u8]) -> anyhow::Result> { + let mut buf = [0; MAX_PACKET_LEN]; + let mut pkt = ham_cats::packet::Packet::new(&mut buf); + pkt.add_identification( + ham_cats::whisker::Identification::new(&config.callsign, config.ssid, config.icon) + .context("Invalid identification")? + ).map_err(|e| anyhow!("Could not add identification to packet: {e}"))?; + + pkt.add_arbitrary(ham_cats::whisker::Arbitrary::new(ip_packet).unwrap()) + .map_err(|e| anyhow!("Could not add data to packet: {e}"))?; + + let mut buf2 = [0; MAX_PACKET_LEN]; + let mut data = ham_cats::buffer::Buffer::new_empty(&mut buf2); + pkt.fully_encode(&mut data) + .map_err(|e| anyhow!("Could not encode packet: {e}"))?; + Ok(data.to_vec()) + } + + match build_tun_packet(config, ip_packet.get_bytes()) { + Ok(data) => if let Err(e) = packet_send.send(data).await { + warn!("Failed to send TUN packet: {e}"); + }, + Err(e) => warn!("Failed to prepare TUN packet: {e}"), + } + + }, + Ok(ip_packet) => { + println!("RX: too large packet: {} bytes", ip_packet.get_bytes().len()); + }, + Err(err) => panic!("Error: {:?}", err), + } + } + }); + } + let port = 3000; info!("Setting up listener on port {port}"); ui::serve(port, shared_state).await; diff --git a/src/ui.rs b/src/ui.rs index f17d1a8..2226a43 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -290,7 +290,7 @@ async fn post_settings(State(state): State, Form(input): Form -

Configuration updated

+

Configuration updated. If you enabled or disabled tunnel, please restart the cats-radio-node process.

To dashboard

"#.to_owned())) } -- cgit v1.2.3