From 4baae22c25225cca6887c4275233bd22912bb820 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Fri, 14 Jul 2023 12:21:40 -0400 Subject: [PATCH] Config loading --- Cargo.toml | 22 +++++++++++------ README.md | 12 ++++----- protomask.toml | 22 +++++++++++++++++ src/cli.rs | 15 +++++++++++ src/config.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 -- src/main.rs | 32 +++++++++++++++++++++--- 7 files changed, 153 insertions(+), 19 deletions(-) create mode 100644 protomask.toml create mode 100644 src/cli.rs create mode 100644 src/config.rs delete mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index ad467d2..0be7e42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,29 @@ [package] -name = "" +name = "protomask" version = "0.1.0" authors = ["Evan Pratten "] edition = "2021" description = "" -documentation = "https://docs.rs/" readme = "README.md" -homepage = "https://github.com/ewpratten/" -repository = "https://github.com/ewpratten/" +homepage = "https://github.com/ewpratten/protomask" +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 = ["macros", "rt-multi-thread"] } +clap = { version = "4.3.11", features = ["derive"] } +serde = { version = "1.0.171", features = ["derive"] } +ipnet = { version = "2.8.0", features = ["serde"] } +toml = "0.7.6" +log = "0.4.19" +fern = "0.6.2" +serde_path_to_error = "0.1.13" +thiserror = "1.0.43" +colored = "2.0.4" [[bin]] -name = "" +name = "protomask" path = "src/main.rs" - diff --git a/README.md b/README.md index 3deb931..bf54dcf 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# -[![Crates.io](https://img.shields.io/crates/v/)](https://crates.io/crates/) -[![Docs.rs](https://docs.rs//badge.svg)](https://docs.rs/) -[![Build](https://github.com/Ewpratten//actions/workflows/build.yml/badge.svg)](https://github.com/Ewpratten//actions/workflows/build.yml) -[![Clippy](https://github.com/Ewpratten//actions/workflows/clippy.yml/badge.svg)](https://github.com/Ewpratten//actions/workflows/clippy.yml) +# protomask +[![Crates.io](https://img.shields.io/crates/v/protomask)](https://crates.io/crates/protomask) +[![Docs.rs](https://docs.rs/protomask/badge.svg)](https://docs.rs/protomask) +[![Build](https://github.com/Ewpratten/protomask/actions/workflows/build.yml/badge.svg)](https://github.com/Ewpratten/protomask/actions/workflows/build.yml) +[![Clippy](https://github.com/Ewpratten/protomask/actions/workflows/clippy.yml/badge.svg)](https://github.com/Ewpratten/protomask/actions/workflows/clippy.yml) repo description @@ -12,5 +12,5 @@ repo description This crate can be installed via `cargo` with: ```sh -cargo install +cargo install protomask ``` diff --git a/protomask.toml b/protomask.toml new file mode 100644 index 0000000..d2a93a5 --- /dev/null +++ b/protomask.toml @@ -0,0 +1,22 @@ +# Example configuration file for protomask + +# Options for configuring the NAT64 interface +[interface] +# A list of IPv4 prefixes to use as the NAT64 pool +# The first address of the pool will be assigned to protomask itself (for ICMP messaging) +pool = ["192.0.2.0/24"] + +# The IPv6 prefix to use for the NAT64 interface. +prefix = "64:ff9b::/96" + +# A unique IPv6 address to use for ICMP messaging +# This will also serve DNS64 +icmpv6_address = "2001:db8:1::1" + +# Rules for controlling address mapping +[rules] +# A static mapping of IPv4 and IPv6 addresses +# These addresses will be exclusively reserved, and not used in the general pool +static_map = [ + { v4 = "192.0.2.2", v6 = "2001:db8:1::2" } +] diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..227f9ba --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,15 @@ +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/config.rs b/src/config.rs new file mode 100644 index 0000000..fb47300 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,67 @@ +use std::{ + net::{Ipv4Addr, Ipv6Addr}, + path::Path, +}; + +use colored::Colorize; +use ipnet::{Ipv4Net, Ipv6Net}; + +/// Interface config +#[derive(Debug, serde::Deserialize)] +pub struct InterfaceConfig { + /// Ipv4 pool + pub pool: Vec, + /// IPv6 prefix + pub prefix: Ipv6Net, + /// IPv6 router addr + pub icmpv6_address: Ipv6Addr, +} + +/// A static mapping rule +#[derive(Debug, serde::Deserialize)] +pub struct AddressMappingRule { + /// IPv4 address + pub v4: Ipv4Addr, + /// IPv6 address + pub v6: Ipv6Addr, +} + +/// Rules config +#[derive(Debug, serde::Deserialize)] +pub struct RulesConfig { + /// Static mapping rules + pub static_map: Vec, +} + +/// Representation of the `protomask.toml` config file +#[derive(Debug, serde::Deserialize)] +pub struct Config { + /// Interface config + pub interface: InterfaceConfig, + /// Rules config + pub rules: RulesConfig, +} + +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), + Err(e) => { + eprintln!( + "Failed to parse config file due to:\n {}\n at {}", + e.inner().message().bright_red(), + e.path().to_string().bright_cyan() + ); + std::process::exit(1); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 9de9b9e..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![doc = include_str!("../README.md")] -#![deny(unsafe_code)] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ba41686..190b76e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,30 @@ +use clap::Parser; +use config::Config; -pub fn main(){ - -} \ No newline at end of file +mod config; +mod cli; + +#[tokio::main] +pub async fn main() { + // Parse CLI args + let args = cli::Args::parse(); + + // Set up logging + fern::Dispatch::new() + .format(|out, message, record| out.finish(format_args!("{}: {}", record.level(), message))) + .level(match args.verbose { + true => log::LevelFilter::Debug, + false => log::LevelFilter::Info, + }) + .chain(std::io::stdout()) + .apply() + .unwrap(); + if args.verbose { + log::debug!("Verbose logging enabled"); + } + + // Parse the config file + let config = Config::load(args.config_file).unwrap(); + + +}