wip
This commit is contained in:
parent
33a2dd685f
commit
bb9b41861b
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
37
Cargo.toml
37
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 }
|
||||
systemd-units = { enable = false }
|
||||
|
5
Makefile
5
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
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
[package]
|
||||
name = "protomask-tun"
|
||||
version = "0.1.0"
|
||||
authors = ["Evan Pratten <ewpratten@gmail.com>"]
|
||||
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"
|
@ -1,3 +0,0 @@
|
||||
# protomask-tun
|
||||
|
||||
An async interface to Linux TUN devices. Support library for `protomask`.
|
@ -1,5 +0,0 @@
|
||||
mod error;
|
||||
mod tun;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use tun::TunDevice;
|
5
src/cli/mod.rs
Normal file
5
src/cli/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod cli;
|
||||
pub mod config;
|
||||
pub mod logging;
|
||||
|
||||
pub use cli::Args;
|
12
src/lib.rs
12
src/lib.rs
@ -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;
|
@ -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
|
@ -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)]
|
||||
|
@ -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);
|
||||
|
@ -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<Ipv4Addr, TableError> {
|
||||
// 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<Ipv6Addr, TableError> {
|
||||
// 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
|
||||
|
@ -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<RawBytes>,
|
||||
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<RawBytes>,
|
||||
new_source: Ipv4Addr,
|
||||
|
@ -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,
|
||||
|
@ -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<Vec<u8>>,
|
||||
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<Vec<u8>>,
|
||||
new_source: Ipv4Addr,
|
||||
|
@ -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<RawBytes>,
|
||||
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<RawBytes>,
|
||||
new_source_addr: Ipv4Addr,
|
||||
|
@ -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<RawBytes>,
|
||||
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<RawBytes>,
|
||||
new_source_addr: Ipv4Addr,
|
||||
|
@ -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<Self> {
|
||||
pub async fn new(name: &str) -> Result<Self, TunError> {
|
||||
// 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
|
@ -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<T> = std::result::Result<T, Error>;
|
5
src/tun/mod.rs
Normal file
5
src/tun/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod device;
|
||||
mod error;
|
||||
|
||||
pub use device::TunDevice;
|
||||
pub use error::TunError;
|
Loading…
x
Reference in New Issue
Block a user