From 87547f59b625e89781a0c4d435acaaa2cb8658ce Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Wed, 2 Aug 2023 16:53:41 -0400 Subject: [PATCH] Starting to build binary entrypoints --- Cargo.toml | 94 +++++++++++++++++++++++----------- src/cli/cli.rs | 16 ------ src/cli/config.rs | 94 ---------------------------------- src/cli/main.rs | 52 ------------------- src/{cli => common}/logging.rs | 0 src/common/mod.rs | 3 ++ src/lib.rs | 12 ----- src/metrics/http.rs | 45 ---------------- src/metrics/metrics.rs | 34 ------------ src/metrics/mod.rs | 6 --- src/protomask-6over4.rs | 5 ++ src/protomask-clat.rs | 51 ++++++++++++++++++ src/protomask.rs | 5 ++ 13 files changed, 129 insertions(+), 288 deletions(-) delete mode 100644 src/cli/cli.rs delete mode 100644 src/cli/config.rs delete mode 100644 src/cli/main.rs rename src/{cli => common}/logging.rs (100%) create mode 100644 src/common/mod.rs delete mode 100644 src/lib.rs delete mode 100644 src/metrics/http.rs delete mode 100644 src/metrics/metrics.rs delete mode 100644 src/metrics/mod.rs create mode 100644 src/protomask-6over4.rs create mode 100644 src/protomask-clat.rs create mode 100644 src/protomask.rs diff --git a/Cargo.toml b/Cargo.toml index 348c03e..7f59c58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,17 @@ -# [package] -# name = "protomask" -# version = "0.2.0" -# authors = ["Evan Pratten "] -# edition = "2021" -# description = "A user space NAT64 implementation" -# readme = "README.md" -# homepage = "https://github.com/ewpratten/protomask" -# documentation = "https://docs.rs/protomask" -# repository = "https://github.com/ewpratten/protomask" -# license = "GPL-3.0" -# keywords = [] -# categories = [] +[package] +name = "protomask" +version = "0.2.0" +authors = ["Evan Pratten "] +edition = "2021" +description = "A user space NAT64 implementation" +readme = "README.md" +homepage = "https://github.com/ewpratten/protomask" +documentation = "https://docs.rs/protomask" +repository = "https://github.com/ewpratten/protomask" +license = "GPL-3.0" +keywords = [] +categories = [] +exclude = ["/.github/", "/.vscode/"] # # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # [dependencies] @@ -39,21 +40,56 @@ # prometheus = "0.13.3" # lazy_static = "1.4.0" -# [[bin]] -# name = "protomask" -# path = "src/cli/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"] -# ] -# conf-files = ["/etc/protomask.toml"] -# depends = [] -# maintainer-scripts = "./debian/" -# systemd-units = { enable = false } - [workspace] members = ["libs/easy-tun", "libs/fast-nat", "libs/interproto"] + +[[bin]] +name = "protomask" +path = "src/protomask.rs" + +[[bin]] +name = "protomask-clat" +path = "src/protomask-clat.rs" + +[[bin]] +name = "protomask-6over4" +path = "src/protomask-6over4.rs" + +[dependencies] +# Internal dependencies +easy-tun = { path = "libs/easy-tun" } +fast-nat = { path = "libs/fast-nat" } +interproto = { path = "libs/interproto" } + +# External Dependencies +tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } +owo-colors = { version = "3.5.0", features = ["supports-colors"] } +clap = { version = "4.3.11", features = ["derive"] } +log = "0.4.19" +fern = "0.6.2" +ipnet = "2.8.0" +nix = "0.26.2" + +[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", + ], +] +conf-files = ["/etc/protomask.toml"] +depends = [] +maintainer-scripts = "./debian/" +systemd-units = { enable = false } diff --git a/src/cli/cli.rs b/src/cli/cli.rs deleted file mode 100644 index 8ccd700..0000000 --- a/src/cli/cli.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Command line argument definitions - -use std::path::PathBuf; - -use clap::Parser; - -#[derive(Parser, Debug)] -#[command(author, version, about, long_about = None)] -pub struct Args { - /// Path to the config file - pub config_file: PathBuf, - - /// Enable verbose logging - #[clap(short, long)] - pub verbose: bool, -} diff --git a/src/cli/config.rs b/src/cli/config.rs deleted file mode 100644 index 50e46f6..0000000 --- a/src/cli/config.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Serde definitions for the config file - -use std::{ - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - path::Path, - time::Duration, -}; - -use ipnet::{Ipv4Net, Ipv6Net}; - -/// A static mapping rule -#[derive(Debug, serde::Deserialize)] -pub struct AddressMappingRule { - /// IPv4 address - pub v4: Ipv4Addr, - /// IPv6 address - pub v6: Ipv6Addr, -} - -/// Used to generate the default reservation duration -fn default_reservation_duration() -> u64 { - 7200 -} - -/// Rules config -#[derive(Debug, serde::Deserialize)] -pub struct PoolConfig { - /// Pool prefixes - #[serde(rename = "Prefixes")] - pub prefixes: Vec, - /// Static mapping rules - #[serde(rename = "Static", default = "Vec::new")] - pub static_map: Vec, - /// How long to hold a dynamic mapping for - #[serde(rename = "MaxIdleDuration", default = "default_reservation_duration")] - reservation_duration: u64, -} - -impl PoolConfig { - /// Get the reservation duration - pub fn reservation_duration(&self) -> Duration { - Duration::from_secs(self.reservation_duration) - } -} - -/// Representation of the `protomask.toml` config file -#[derive(Debug, serde::Deserialize)] -pub struct Config { - /// The NAT64 prefix - #[serde(rename = "Nat64Prefix")] - pub nat64_prefix: Ipv6Net, - /// Address to bind to for prometheus support - #[serde(rename = "Prometheus")] - pub prom_bind_addr: Option, - /// Pool configuration - #[serde(rename = "Pool")] - pub pool: PoolConfig, -} - -impl Config { - /// Load the config from a file - pub fn load>(path: P) -> Result { - // Load the file - let file_contents = std::fs::read_to_string(path)?; - - // Build the deserializer - let deserializer = toml::Deserializer::new(&file_contents); - - // Parse - match serde_path_to_error::deserialize(deserializer) { - Ok(config) => Ok(config), - // If there is a parsing error, display a reasonable error message - Err(e) => { - eprintln!( - "Failed to parse config file due to:\n {}\n at {}", - e.inner().message(), - e.path() - ); - std::process::exit(1); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Test that fails if the example file is not valid - #[test] - fn ensure_example_is_valid() { - let _ = Config::load("protomask.toml").unwrap(); - } -} diff --git a/src/cli/main.rs b/src/cli/main.rs deleted file mode 100644 index f2f2355..0000000 --- a/src/cli/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! This is the entrypoint for `protomask` from the command line. - -use clap::Parser; -use config::Config; -use logging::enable_logger; -use protomask::nat::Nat64; - -mod cli; -mod config; -mod logging; - -#[tokio::main] -pub async fn main() { - // Parse CLI args - let args = cli::Args::parse(); - - // Set up logging - enable_logger(args.verbose); - - // Parse the config file - let config = Config::load(args.config_file).unwrap(); - - // Currently, only a /96 is supported - if config.nat64_prefix.prefix_len() != 96 { - log::error!("Only a /96 prefix is supported for the NAT64 prefix"); - std::process::exit(1); - } - - // Create the NAT64 instance - let mut nat64 = Nat64::new( - config.nat64_prefix, - config.pool.prefixes.clone(), - config - .pool - .static_map - .iter() - .map(|rule| (rule.v6, rule.v4)) - .collect(), - config.pool.reservation_duration(), - ) - .await - .unwrap(); - - // 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)); - } - - // Handle packets - nat64.run().await.unwrap(); -} diff --git a/src/cli/logging.rs b/src/common/logging.rs similarity index 100% rename from src/cli/logging.rs rename to src/common/logging.rs diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..e568b1f --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1,3 @@ +//! Common code used across all protomask binaries + +pub mod logging; 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/metrics/http.rs b/src/metrics/http.rs deleted file mode 100644 index 5f88e1c..0000000 --- a/src/metrics/http.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::{convert::Infallible, net::SocketAddr}; - -use hyper::{ - service::{make_service_fn, service_fn}, - Body, Method, Request, Response, Server, -}; -use prometheus::{Encoder, TextEncoder}; - -/// Handle an HTTP request -#[allow(clippy::unused_async)] -async fn handle_request(request: Request) -> Result, Infallible> { - // If the request is targeting the metrics endpoint - if request.method() == Method::GET && request.uri().path() == "/metrics" { - // Gather metrics - let metric_families = prometheus::gather(); - let body = { - let mut buffer = Vec::new(); - let encoder = TextEncoder::new(); - encoder.encode(&metric_families, &mut buffer).unwrap(); - String::from_utf8(buffer).unwrap() - }; - - // Return the response - return Ok(Response::new(Body::from(body))); - } - - // Otherwise, just return a 404 - Ok(Response::builder() - .status(404) - .body(Body::from("Not found")) - .unwrap()) -} - -/// Bring up an HTTP server that listens for metrics requests -pub async fn serve_metrics(bind_addr: SocketAddr) { - // Set up the server - let make_service = - make_service_fn(|_| async { Ok::<_, Infallible>(service_fn(handle_request)) }); - let server = Server::bind(&bind_addr).serve(make_service); - - // Run the server - if let Err(e) = server.await { - eprintln!("Metrics server error: {e}"); - } -} diff --git a/src/metrics/metrics.rs b/src/metrics/metrics.rs deleted file mode 100644 index ba32849..0000000 --- a/src/metrics/metrics.rs +++ /dev/null @@ -1,34 +0,0 @@ -use lazy_static::lazy_static; -use prometheus::{ - register_int_counter_vec, register_int_gauge, register_int_gauge_vec, IntCounterVec, IntGauge, - IntGaugeVec, -}; - -lazy_static! { - /// Counter for the number of packets processes - pub static ref PACKET_COUNTER: IntCounterVec = register_int_counter_vec!( - "packets", - "Number of packets processed", - &["protocol", "status"] - ).unwrap(); - - /// Counter for ICMP packet types - pub static ref ICMP_COUNTER: IntCounterVec = register_int_counter_vec!( - "icmp", - "Number of ICMP packets processed", - &["protocol", "type", "code"] - ).unwrap(); - - /// Gauge for the number of addresses in the IPv4 pool - pub static ref IPV4_POOL_SIZE: IntGauge = register_int_gauge!( - "ipv4_pool_size", - "Number of IPv4 addresses in the pool" - ).unwrap(); - - /// Gauge for the number of addresses currently reserved in the IPv4 pool - pub static ref IPV4_POOL_RESERVED: IntGaugeVec = register_int_gauge_vec!( - "ipv4_pool_reserved", - "Number of IPv4 addresses currently reserved", - &["static"] - ).unwrap(); -} diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs deleted file mode 100644 index e79cd4c..0000000 --- a/src/metrics/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod http; -#[allow(clippy::module_inception)] -mod metrics; - -pub use http::serve_metrics; -pub(crate) use metrics::*; diff --git a/src/protomask-6over4.rs b/src/protomask-6over4.rs new file mode 100644 index 0000000..9d38cbe --- /dev/null +++ b/src/protomask-6over4.rs @@ -0,0 +1,5 @@ + +#[tokio::main] +pub async fn main(){ + +} \ No newline at end of file diff --git a/src/protomask-clat.rs b/src/protomask-clat.rs new file mode 100644 index 0000000..150b650 --- /dev/null +++ b/src/protomask-clat.rs @@ -0,0 +1,51 @@ +//! Entrypoint for the `protomask-clat` binary. +//! +//! This binary is a Customer-side transLATor (CLAT) that translates all native +//! IPv4 traffic to IPv6 traffic for transmission over an IPv6-only ISP network. + +use clap::Parser; +use common::logging::enable_logger; +use easy_tun::Tun; +use ipnet::Ipv6Net; +use nix::unistd::Uid; + +mod common; + +#[derive(Debug, Parser)] +#[clap(author, version, about="IPv4 to IPv6 Customer-side transLATor (CLAT)", long_about = None)] +struct Args { + /// IPv6 prefix to embed IPv4 addresses in + #[clap(long="via", default_value_t = ("64:ff9b::/96").parse().unwrap())] + embed_prefix: Ipv6Net, + + /// Explicitly set the interface name to use + #[clap(short, long, default_value_t = ("clat%d").to_string())] + interface: String, + + /// Enable verbose logging + #[clap(short, long)] + verbose: bool, +} + +#[tokio::main] +pub async fn main() { + // Parse CLI args + let args = Args::parse(); + + // Initialize logging + enable_logger(args.verbose); + + // We must be root to continue program execution + if !Uid::effective().is_root() { + log::error!("This program must be run as root"); + std::process::exit(1); + } + + // Bring up a TUN interface + let mut tun = Tun::new(&args.interface).unwrap(); + + log::info!("Translating packets on {}", tun.name()); + loop { + + } +} diff --git a/src/protomask.rs b/src/protomask.rs new file mode 100644 index 0000000..9d38cbe --- /dev/null +++ b/src/protomask.rs @@ -0,0 +1,5 @@ + +#[tokio::main] +pub async fn main(){ + +} \ No newline at end of file