aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2024-01-14 19:06:28 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2024-01-14 19:06:28 +0100
commit6e5f5636a3d34d3edd5c901c8b67f6444a116ad5 (patch)
tree1ec1b2205a9b3b18c948c4fe4d14ce8438ec009f
parente8deeb539f8afc01f4936aa0d7eb1a29c4469da8 (diff)
downloadcats-radio-node-6e5f5636a3d34d3edd5c901c8b67f6444a116ad5.tar.gz
cats-radio-node-6e5f5636a3d34d3edd5c901c8b67f6444a116ad5.tar.bz2
cats-radio-node-6e5f5636a3d34d3edd5c901c8b67f6444a116ad5.zip
Add TUN functionality
-rw-r--r--Cargo.lock117
-rw-r--r--Cargo.toml4
-rw-r--r--src/main.rs85
-rw-r--r--src/ui.rs2
4 files changed, 207 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6b9c4c2..1ae0ba4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -346,6 +346,26 @@ 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"
dependencies = [
@@ -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]]
@@ -637,6 +660,21 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -681,6 +719,17 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
@@ -1058,6 +1109,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1094,6 +1151,16 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2458,6 +2525,24 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2574,6 +2659,25 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2715,6 +2819,19 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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<Mutex<AppState>>;
+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::<Vec<u8>>(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<Vec<u8>> {
+ 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<SharedState>, Form(input): Form<FormC
(StatusCode::OK, Html(
r#"<!doctype html>
<html><head></head><body>
- <p>Configuration updated</p>
+ <p>Configuration updated. If you enabled or disabled tunnel, please restart the cats-radio-node process.</p>
<p>To <a href="/">dashboard</a></p>
</body></html>"#.to_owned()))
}