diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..16f1e73 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 37458a9..b67d979 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,20 +12,26 @@ license = "GPL-3.0" keywords = [] categories = [] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +profiling = ["profiling/profile-with-puffin", "puffin_http", "puffin"] + [dependencies] -protomask-tun = { path = "protomask-tun", version = "0.1.0" } tokio = { version = "1.29.1", features = [ "macros", "rt-multi-thread", - # "process", - "sync" + "sync", + "tracing", ] } clap = { version = "4.3.11", features = ["derive"] } serde = { version = "1.0.171", features = ["derive"] } ipnet = { version = "2.8.0", features = ["serde"] } hyper = { version = "0.14.27", features = ["server", "http1", "tcp"] } owo-colors = { version = "3.5.0", features = ["supports-colors"] } +puffin_http = { version = "0.13.0", optional = true } +puffin = { version = "0.16.0", optional = true } +profiling = "1.0.8" +tracing = "0.1.37" toml = "0.7.6" log = "0.4.19" fern = "0.6.2" @@ -41,16 +47,29 @@ lazy_static = "1.4.0" [[bin]] name = "protomask" -path = "src/cli/main.rs" +path = "src/main.rs" + [package.metadata.deb] section = "network" assets = [ - ["target/release/protomask", "/usr/local/bin/protomask", "755"], - ["./protomask.toml", "/etc/protomask.toml", "644"], - ["README.md", "usr/share/doc/protomask/README.md", "644"] + [ + "target/release/protomask", + "/usr/local/bin/protomask", + "755", + ], + [ + "./protomask.toml", + "/etc/protomask.toml", + "644", + ], + [ + "README.md", + "usr/share/doc/protomask/README.md", + "644", + ], ] conf-files = ["/etc/protomask.toml"] depends = [] maintainer-scripts = "./debian/" -systemd-units = { enable = false } \ No newline at end of file +systemd-units = { enable = false } diff --git a/Makefile b/Makefile index f2b9397..25c0625 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # All sources used to build the protomask binary -SRC = Cargo.toml $(shell find src/ -type f -name '*.rs') $(shell find protomask-tun/src/ -type f -name '*.rs') +SRC = Cargo.toml $(shell find src/ -type f -name '*.rs') # Used to auto-version things CRATE_VERSION = $(shell sed -n -r "s/^version = \"([0-9\.]+)\"/\1/p" Cargo.toml) @@ -7,6 +7,9 @@ CRATE_VERSION = $(shell sed -n -r "s/^version = \"([0-9\.]+)\"/\1/p" Cargo.toml) target/x86_64-unknown-linux-musl/release/protomask: $(SRC) cross build --target x86_64-unknown-linux-musl --release +target/x86_64-unknown-linux-musl/debug/protomask: $(SRC) + cross build --target x86_64-unknown-linux-musl --features profiling + target/aarch64-unknown-linux-musl/release/protomask: $(SRC) cross build --target aarch64-unknown-linux-musl --release diff --git a/protomask-tun/Cargo.toml b/protomask-tun/Cargo.toml deleted file mode 100644 index a2a24aa..0000000 --- a/protomask-tun/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "protomask-tun" -version = "0.1.0" -authors = ["Evan Pratten "] -edition = "2021" -description = "An async interface to Linux TUN devices" -readme = "README.md" -homepage = "https://github.com/ewpratten/protomask/protomask-tun" -documentation = "https://docs.rs/protomask-tun" -repository = "https://github.com/ewpratten/protomask" -license = "GPL-3.0" -keywords = [] -categories = [] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -tokio = { version = "1.29.1", features = ["sync", "rt"] } -log = "0.4.19" -thiserror = "1.0.43" -tun-tap = "0.1.3" -rtnetlink = "0.13.0" -futures = "0.3.28" -ipnet = "^2.8.0" diff --git a/protomask-tun/README.md b/protomask-tun/README.md deleted file mode 100644 index 2f77949..0000000 --- a/protomask-tun/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# protomask-tun - -An async interface to Linux TUN devices. Support library for `protomask`. diff --git a/protomask-tun/src/lib.rs b/protomask-tun/src/lib.rs deleted file mode 100644 index b75b6df..0000000 --- a/protomask-tun/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod error; -mod tun; - -pub use error::{Error, Result}; -pub use tun::TunDevice; diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..bdb0e4e --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,5 @@ +mod cli; +pub mod config; +pub mod logging; + +pub use cli::Args; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 14f189f..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! # Protomask library -//! -//! *Note: There is a fair chance you are looking for `src/cli/main.rs` instead of this file.* - -#![deny(clippy::pedantic)] -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::missing_panics_doc)] - -pub mod metrics; -pub mod nat; -mod packet; diff --git a/src/cli/main.rs b/src/main.rs similarity index 61% rename from src/cli/main.rs rename to src/main.rs index f2f2355..d2e6ecf 100644 --- a/src/cli/main.rs +++ b/src/main.rs @@ -1,16 +1,29 @@ -//! This is the entrypoint for `protomask` from the command line. +//! The `protomask` application entrypoint + +#![deny(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::missing_panics_doc)] use clap::Parser; -use config::Config; -use logging::enable_logger; -use protomask::nat::Nat64; +use cli::{config::Config, logging::enable_logger}; +use nat::Nat64; mod cli; -mod config; -mod logging; +mod metrics; +mod nat; +mod packet; +mod tun; #[tokio::main] pub async fn main() { + // Enable profiling server if we are building with `--features profiling` + #[cfg(feature = "profiling")] + let _puffin_server = + puffin_http::Server::new(&format!("[::]:{}", puffin_http::DEFAULT_PORT)).unwrap(); + #[cfg(feature = "profiling")] + puffin::set_scopes_on(true); + // Parse CLI args let args = cli::Args::parse(); @@ -44,7 +57,7 @@ pub async fn main() { // Handle metrics requests if let Some(bind_addr) = config.prom_bind_addr { log::info!("Enabling metrics server on {}", bind_addr); - tokio::spawn(protomask::metrics::serve_metrics(bind_addr)); + tokio::spawn(metrics::serve_metrics(bind_addr)); } // Handle packets diff --git a/src/nat/error.rs b/src/nat/error.rs index d90751a..d1b8d53 100644 --- a/src/nat/error.rs +++ b/src/nat/error.rs @@ -3,7 +3,7 @@ pub enum Nat64Error { #[error(transparent)] Table(#[from] super::table::TableError), #[error(transparent)] - Tun(#[from] protomask_tun::Error), + Tun(#[from] crate::tun::TunError), #[error(transparent)] Io(#[from] std::io::Error), #[error(transparent)] diff --git a/src/nat/mod.rs b/src/nat/mod.rs index 930b363..e6a9aaf 100644 --- a/src/nat/mod.rs +++ b/src/nat/mod.rs @@ -12,7 +12,7 @@ use self::{ utils::{embed_address, extract_address, unwrap_log}, }; use ipnet::{Ipv4Net, Ipv6Net}; -use protomask_tun::TunDevice; +use crate::tun::TunDevice; use std::{ net::{IpAddr, Ipv4Addr, Ipv6Addr}, time::Duration, @@ -68,6 +68,8 @@ impl Nat64 { // Process packets in a loop loop { + profiling::finish_frame!(); + // Try to read a packet match rx.recv().await { Ok(packet) => { @@ -98,7 +100,7 @@ impl Nat64 { .inc(); // Spawn a task to process the packet - tokio::spawn(async move { + // tokio::spawn(async move { if let Some(output) = unwrap_log(translate_ipv4_to_ipv6( packet, new_source, @@ -107,7 +109,7 @@ impl Nat64 { tx.send(output.into()).await.unwrap(); PACKET_COUNTER.with_label_values(&["ipv6", "sent"]).inc(); } - }); + // }); } 6 => { // Parse the packet @@ -147,7 +149,7 @@ impl Nat64 { .inc(); // Spawn a task to process the packet - tokio::spawn(async move { + // tokio::spawn(async move { if let Some(output) = unwrap_log(translate_ipv6_to_ipv4( &packet, new_source, @@ -156,7 +158,7 @@ impl Nat64 { tx.send(output.into()).await.unwrap(); PACKET_COUNTER.with_label_values(&["ipv4", "sent"]).inc(); } - }); + // }); } n => { log::warn!("Unknown IP version: {}", n); diff --git a/src/nat/table.rs b/src/nat/table.rs index e7e3693..d0dc11b 100644 --- a/src/nat/table.rs +++ b/src/nat/table.rs @@ -53,6 +53,7 @@ impl Nat64Table { } /// Make a reservation for an IP address pair for eternity + #[profiling::function] pub fn add_infinite_reservation( &mut self, ipv6: Ipv6Addr, @@ -75,6 +76,7 @@ impl Nat64Table { } /// Check if a given address exists in the table + #[profiling::function] pub fn contains(&self, address: &IpAddr) -> bool { match address { IpAddr::V4(ipv4) => self.reservations.contains_right(ipv4), @@ -83,6 +85,7 @@ impl Nat64Table { } /// Get or assign an IPv4 address for the given IPv6 address + #[profiling::function] pub fn get_or_assign_ipv4(&mut self, ipv6: Ipv6Addr) -> Result { // Prune old reservations self.prune(); @@ -118,6 +121,7 @@ impl Nat64Table { } /// Try to find an IPv6 address for the given IPv4 address + #[profiling::function] pub fn get_reverse(&mut self, ipv4: Ipv4Addr) -> Result { // Prune old reservations self.prune(); @@ -140,6 +144,7 @@ impl Nat64Table { impl Nat64Table { /// Prune old reservations + #[profiling::function] fn prune(&mut self) { let now = Instant::now(); @@ -162,6 +167,7 @@ impl Nat64Table { }); } + #[profiling::function] fn track_utilization(&self) { // Count static and dynamic in a single pass let (total_dynamic_reservations, total_static_reservations) = self diff --git a/src/packet/xlat/icmp/mod.rs b/src/packet/xlat/icmp/mod.rs index f0479b0..b45382b 100644 --- a/src/packet/xlat/icmp/mod.rs +++ b/src/packet/xlat/icmp/mod.rs @@ -17,6 +17,7 @@ use super::ip::{translate_ipv4_to_ipv6, translate_ipv6_to_ipv4}; mod type_code; /// Translates an ICMP packet to an ICMPv6 packet +#[profiling::function] pub fn translate_icmp_to_icmpv6( input: IcmpPacket, new_source: Ipv6Addr, @@ -68,6 +69,7 @@ pub fn translate_icmp_to_icmpv6( } /// Translates an ICMPv6 packet to an ICMP packet +#[profiling::function] pub fn translate_icmpv6_to_icmp( input: Icmpv6Packet, new_source: Ipv4Addr, diff --git a/src/packet/xlat/icmp/type_code.rs b/src/packet/xlat/icmp/type_code.rs index 8aead6c..1e2c17e 100644 --- a/src/packet/xlat/icmp/type_code.rs +++ b/src/packet/xlat/icmp/type_code.rs @@ -11,6 +11,7 @@ use crate::packet::error::PacketError; /// Best effort translation from an ICMP type and code to an ICMPv6 type and code #[allow(clippy::deprecated_cfg_attr)] +#[profiling::function] pub fn translate_type_and_code_4_to_6( icmp_type: IcmpType, icmp_code: IcmpCode, @@ -60,6 +61,7 @@ pub fn translate_type_and_code_4_to_6( /// Best effort translation from an ICMPv6 type and code to an ICMP type and code #[allow(clippy::deprecated_cfg_attr)] +#[profiling::function] pub fn translate_type_and_code_6_to_4( icmp_type: Icmpv6Type, icmp_code: Icmpv6Code, diff --git a/src/packet/xlat/ip.rs b/src/packet/xlat/ip.rs index ae9804f..2bb2641 100644 --- a/src/packet/xlat/ip.rs +++ b/src/packet/xlat/ip.rs @@ -17,6 +17,7 @@ use super::{ }; /// Translates an IPv4 packet to an IPv6 packet +#[tracing::instrument(name="xlat_ipv4_ipv6")] pub fn translate_ipv4_to_ipv6( input: Ipv4Packet>, new_source: Ipv6Addr, @@ -69,6 +70,7 @@ pub fn translate_ipv4_to_ipv6( } /// Translates an IPv6 packet to an IPv4 packet +#[tracing::instrument(name="xlat_ipv6_ipv4")] pub fn translate_ipv6_to_ipv4( input: &Ipv6Packet>, new_source: Ipv4Addr, diff --git a/src/packet/xlat/tcp.rs b/src/packet/xlat/tcp.rs index 2b456c3..15352cb 100644 --- a/src/packet/xlat/tcp.rs +++ b/src/packet/xlat/tcp.rs @@ -6,6 +6,7 @@ use crate::packet::{ }; /// Translates an IPv4 TCP packet to an IPv6 TCP packet +#[profiling::function] pub fn translate_tcp4_to_tcp6( input: TcpPacket, new_source_addr: Ipv6Addr, @@ -26,6 +27,7 @@ pub fn translate_tcp4_to_tcp6( } /// Translates an IPv6 TCP packet to an IPv4 TCP packet +#[profiling::function] pub fn translate_tcp6_to_tcp4( input: TcpPacket, new_source_addr: Ipv4Addr, diff --git a/src/packet/xlat/udp.rs b/src/packet/xlat/udp.rs index df2a74b..897cc8c 100644 --- a/src/packet/xlat/udp.rs +++ b/src/packet/xlat/udp.rs @@ -6,6 +6,7 @@ use crate::packet::{ }; /// Translates an IPv4 UDP packet to an IPv6 UDP packet +#[profiling::function] pub fn translate_udp4_to_udp6( input: UdpPacket, new_source_addr: Ipv6Addr, @@ -20,6 +21,7 @@ pub fn translate_udp4_to_udp6( } /// Translates an IPv6 UDP packet to an IPv4 UDP packet +#[profiling::function] pub fn translate_udp6_to_udp4( input: UdpPacket, new_source_addr: Ipv4Addr, diff --git a/protomask-tun/src/tun.rs b/src/tun/device.rs similarity index 95% rename from protomask-tun/src/tun.rs rename to src/tun/device.rs index 79511b5..146683e 100644 --- a/protomask-tun/src/tun.rs +++ b/src/tun/device.rs @@ -12,7 +12,7 @@ use tokio::{ }; use tun_tap::Mode; -use crate::Result; +use super::TunError; #[derive(Debug)] pub struct TunDevice { @@ -29,7 +29,7 @@ impl TunDevice { /// /// The name field can be any string. If `%d` is present in the string, /// it will be replaced with a unique number. - pub async fn new(name: &str) -> Result { + pub async fn new(name: &str) -> Result { // Bring up an rtnetlink connection let (rt_connection, rt_handle, _) = rtnetlink::new_connection().map_err(|err| { log::error!("Failed to open rtnetlink connection"); @@ -85,7 +85,11 @@ impl TunDevice { } /// Add an IP address to this device - pub async fn add_address(&mut self, ip_address: IpAddr, prefix_len: u8) -> Result<()> { + pub async fn add_address( + &mut self, + ip_address: IpAddr, + prefix_len: u8, + ) -> Result<(), TunError> { self.rt_handle .address() .add(self.link_index, ip_address, prefix_len) @@ -101,7 +105,11 @@ impl TunDevice { } /// Remove an IP address from this device - pub async fn remove_address(&mut self, ip_address: IpAddr, prefix_len: u8) -> Result<()> { + pub async fn remove_address( + &mut self, + ip_address: IpAddr, + prefix_len: u8, + ) -> Result<(), TunError> { // Find the address message that matches the given address if let Some(address_message) = self .rt_handle @@ -136,7 +144,7 @@ impl TunDevice { } /// Add a route to this device - pub async fn add_route(&mut self, destination: IpNet) -> Result<()> { + pub async fn add_route(&mut self, destination: IpNet) -> Result<(), TunError> { match destination { IpNet::V4(destination) => { self.rt_handle diff --git a/protomask-tun/src/error.rs b/src/tun/error.rs similarity index 71% rename from protomask-tun/src/error.rs rename to src/tun/error.rs index ed62a8e..4894d76 100644 --- a/protomask-tun/src/error.rs +++ b/src/tun/error.rs @@ -1,10 +1,8 @@ #[derive(Debug, thiserror::Error)] -pub enum Error { +pub enum TunError { #[error(transparent)] IoError(#[from] std::io::Error), #[error(transparent)] NetlinkError(#[from] rtnetlink::Error), } - -pub type Result = std::result::Result; \ No newline at end of file diff --git a/src/tun/mod.rs b/src/tun/mod.rs new file mode 100644 index 0000000..0882283 --- /dev/null +++ b/src/tun/mod.rs @@ -0,0 +1,5 @@ +mod device; +mod error; + +pub use device::TunDevice; +pub use error::TunError;