Much cleaner packet handling system in progress
This commit is contained in:
parent
240348303d
commit
b2b99a8e11
@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
net::{Ipv4Addr, Ipv6Addr},
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
@ -32,12 +33,20 @@ pub struct AddressMappingRule {
|
|||||||
pub v6: Ipv6Addr,
|
pub v6: Ipv6Addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to generate the default reservation duration
|
||||||
|
fn default_reservation_duration() -> Duration {
|
||||||
|
Duration::from_secs(7200)
|
||||||
|
}
|
||||||
|
|
||||||
/// Rules config
|
/// Rules config
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
pub struct RulesConfig {
|
pub struct RulesConfig {
|
||||||
/// Static mapping rules
|
/// Static mapping rules
|
||||||
#[serde(rename = "MapStatic")]
|
#[serde(rename = "MapStatic")]
|
||||||
pub static_map: Vec<AddressMappingRule>,
|
pub static_map: Vec<AddressMappingRule>,
|
||||||
|
/// How long to hold a dynamic mapping for
|
||||||
|
#[serde(rename = "ReservationDuration", default="default_reservation_duration")]
|
||||||
|
pub reservation_duration: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of the `protomask.toml` config file
|
/// Representation of the `protomask.toml` config file
|
||||||
|
43
src/main.rs
43
src/main.rs
@ -1,6 +1,7 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
use nat::Nat64;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod config;
|
mod config;
|
||||||
@ -13,10 +14,13 @@ pub async fn main() {
|
|||||||
let args = cli::Args::parse();
|
let args = cli::Args::parse();
|
||||||
|
|
||||||
// Set up logging
|
// Set up logging
|
||||||
|
let log_verbose = args.verbose;
|
||||||
fern::Dispatch::new()
|
fern::Dispatch::new()
|
||||||
.format(|out, message, record| {
|
.format(move |out, message, record| {
|
||||||
out.finish(format_args!(
|
out.finish(format_args!(
|
||||||
"{}: {}",
|
"{}: {}",
|
||||||
|
format!(
|
||||||
|
"{}{}",
|
||||||
match record.level() {
|
match record.level() {
|
||||||
log::Level::Error => "ERROR".red().bold().to_string(),
|
log::Level::Error => "ERROR".red().bold().to_string(),
|
||||||
log::Level::Warn => "WARN ".yellow().bold().to_string(),
|
log::Level::Warn => "WARN ".yellow().bold().to_string(),
|
||||||
@ -24,6 +28,12 @@ pub async fn main() {
|
|||||||
log::Level::Debug => "DEBUG".bright_blue().bold().to_string(),
|
log::Level::Debug => "DEBUG".bright_blue().bold().to_string(),
|
||||||
log::Level::Trace => "TRACE".bright_white().bold().to_string(),
|
log::Level::Trace => "TRACE".bright_white().bold().to_string(),
|
||||||
},
|
},
|
||||||
|
match log_verbose {
|
||||||
|
true => format!(" [{:13}]", record.target().split("::").nth(0).unwrap()),
|
||||||
|
false => String::new(),
|
||||||
|
}
|
||||||
|
.bright_black()
|
||||||
|
),
|
||||||
message
|
message
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
@ -41,20 +51,21 @@ pub async fn main() {
|
|||||||
// Parse the config file
|
// Parse the config file
|
||||||
let config = Config::load(args.config_file).unwrap();
|
let config = Config::load(args.config_file).unwrap();
|
||||||
|
|
||||||
// // Create the NAT64 instance
|
// Create the NAT64 instance
|
||||||
// let mut nat64 = Nat64::new(
|
let mut nat64 = Nat64::new(
|
||||||
// config.interface.pool,
|
config.interface.prefix,
|
||||||
// config.interface.prefix,
|
config.interface.pool,
|
||||||
// config
|
config
|
||||||
// .rules
|
.rules
|
||||||
// .static_map
|
.static_map
|
||||||
// .iter()
|
.iter()
|
||||||
// .map(|rule| (rule.v4, rule.v6))
|
.map(|rule| (rule.v6, rule.v4))
|
||||||
// .collect(),
|
.collect(),
|
||||||
// )
|
config.rules.reservation_duration,
|
||||||
// .await
|
)
|
||||||
// .unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// // Handle packets
|
// Handle packets
|
||||||
// nat64.run().await.unwrap();
|
nat64.run().await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,13 @@ pub enum InterfaceError {
|
|||||||
pub struct Nat64Interface {
|
pub struct Nat64Interface {
|
||||||
/// Underlying TUN interface
|
/// Underlying TUN interface
|
||||||
interface: Iface,
|
interface: Iface,
|
||||||
|
/// Interface MTU
|
||||||
|
mtu: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Nat64Interface {
|
impl Nat64Interface {
|
||||||
/// Create a new NAT64 interface
|
/// Create a new NAT64 interface
|
||||||
pub async fn new(v6_prefix: Ipv6Net, v4_pool: Vec<Ipv4Net>) -> Result<Self, InterfaceError> {
|
pub async fn new(v6_prefix: Ipv6Net, v4_pool: &Vec<Ipv4Net>) -> Result<Self, InterfaceError> {
|
||||||
// Bring up an rtnetlink connection
|
// Bring up an rtnetlink connection
|
||||||
let (rt_connection, rt_handle, _) = rtnetlink::new_connection()?;
|
let (rt_connection, rt_handle, _) = rtnetlink::new_connection()?;
|
||||||
tokio::spawn(rt_connection);
|
tokio::spawn(rt_connection);
|
||||||
@ -52,8 +54,13 @@ impl Nat64Interface {
|
|||||||
.add()
|
.add()
|
||||||
.v6()
|
.v6()
|
||||||
.destination_prefix(v6_prefix.addr(), v6_prefix.prefix_len())
|
.destination_prefix(v6_prefix.addr(), v6_prefix.prefix_len())
|
||||||
|
.output_interface(interface_link.header.index)
|
||||||
.execute()
|
.execute()
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
log::error!("Failed to add route for {}: {}", v6_prefix, error);
|
||||||
|
error
|
||||||
|
})?;
|
||||||
log::info!("Added route: {} via {}", v6_prefix, interface.name());
|
log::info!("Added route: {} via {}", v6_prefix, interface.name());
|
||||||
|
|
||||||
// Add every prefix in the v4 pool as a route
|
// Add every prefix in the v4 pool as a route
|
||||||
@ -63,12 +70,26 @@ impl Nat64Interface {
|
|||||||
.add()
|
.add()
|
||||||
.v4()
|
.v4()
|
||||||
.destination_prefix(prefix.addr(), prefix.prefix_len())
|
.destination_prefix(prefix.addr(), prefix.prefix_len())
|
||||||
|
.output_interface(interface_link.header.index)
|
||||||
.execute()
|
.execute()
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
log::error!("Failed to add route for {}: {}", prefix, error);
|
||||||
|
error
|
||||||
|
})?;
|
||||||
log::info!("Added route: {} via {}", prefix, interface.name());
|
log::info!("Added route: {} via {}", prefix, interface.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { interface })
|
// Read the interface MTU
|
||||||
|
let mtu: usize =
|
||||||
|
std::fs::read_to_string(format!("/sys/class/net/{}/mtu", interface.name()))
|
||||||
|
.expect("Failed to read interface MTU")
|
||||||
|
.strip_suffix("\n")
|
||||||
|
.unwrap()
|
||||||
|
.parse()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(Self { interface, mtu })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the interface mode
|
/// Get the interface mode
|
||||||
@ -81,6 +102,11 @@ impl Nat64Interface {
|
|||||||
self.interface.name()
|
self.interface.name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the interface MTU
|
||||||
|
pub fn mtu(&self) -> usize {
|
||||||
|
self.mtu
|
||||||
|
}
|
||||||
|
|
||||||
/// Receive a packet from the interface
|
/// Receive a packet from the interface
|
||||||
pub fn recv(&self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
pub fn recv(&self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
|
||||||
self.interface.recv(buf)
|
self.interface.recv(buf)
|
||||||
|
@ -1,4 +1,92 @@
|
|||||||
pub mod xlat;
|
use std::{
|
||||||
pub mod packet;
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
pub mod table;
|
time::Duration,
|
||||||
pub mod interface;
|
};
|
||||||
|
|
||||||
|
use ipnet::{Ipv4Net, Ipv6Net};
|
||||||
|
|
||||||
|
use self::{interface::Nat64Interface, packet::IpPacket, table::Nat64Table};
|
||||||
|
|
||||||
|
mod interface;
|
||||||
|
mod packet;
|
||||||
|
mod table;
|
||||||
|
mod xlat;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Nat64Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
TableError(#[from] table::TableError),
|
||||||
|
#[error(transparent)]
|
||||||
|
InterfaceError(#[from] interface::InterfaceError),
|
||||||
|
#[error(transparent)]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Nat64 {
|
||||||
|
table: Nat64Table,
|
||||||
|
interface: Nat64Interface,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nat64 {
|
||||||
|
/// Construct a new NAT64 instance
|
||||||
|
pub async fn new(
|
||||||
|
v6_prefix: Ipv6Net,
|
||||||
|
v4_pool: Vec<Ipv4Net>,
|
||||||
|
static_reservations: Vec<(Ipv6Addr, Ipv4Addr)>,
|
||||||
|
reservation_duration: Duration,
|
||||||
|
) -> Result<Self, Nat64Error> {
|
||||||
|
// Bring up the interface
|
||||||
|
let interface = Nat64Interface::new(v6_prefix, &v4_pool).await?;
|
||||||
|
|
||||||
|
// Build the table and insert any static reservations
|
||||||
|
let mut table = Nat64Table::new(v4_pool, reservation_duration);
|
||||||
|
for (v6, v4) in static_reservations {
|
||||||
|
table.add_infinite_reservation(v6, v4)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { table, interface })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block and process all packets
|
||||||
|
pub async fn run(&mut self) -> Result<(), Nat64Error> {
|
||||||
|
// Allocate a buffer for incoming packets
|
||||||
|
let mut buffer = vec![0u8; self.interface.mtu()];
|
||||||
|
|
||||||
|
// Loop forever
|
||||||
|
loop {
|
||||||
|
// Read a packet from the interface
|
||||||
|
match self.interface.recv(&mut buffer) {
|
||||||
|
Ok(packet_len) => {
|
||||||
|
// Parse in to a more friendly format
|
||||||
|
match IpPacket::new(&buffer[..packet_len]) {
|
||||||
|
// Try to process the packet
|
||||||
|
Ok(inbound_packet) => match self.process_packet(inbound_packet).await? {
|
||||||
|
// If data is returned, send it back out the interface
|
||||||
|
Some(outbound_packet) => {
|
||||||
|
let packet_bytes = outbound_packet.to_bytes();
|
||||||
|
self.interface.send(&packet_bytes)?;
|
||||||
|
}
|
||||||
|
// Otherwise, we can assume that the packet was dealt with, and can move on
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("Failed to parse packet: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("Failed to read packet: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Nat64 {
|
||||||
|
async fn process_packet(
|
||||||
|
&mut self,
|
||||||
|
packet: IpPacket<'_>,
|
||||||
|
) -> Result<Option<IpPacket>, Nat64Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,14 @@ use std::net::IpAddr;
|
|||||||
|
|
||||||
use pnet_packet::{ip::IpNextHeaderProtocol, ipv4::Ipv4Packet, ipv6::Ipv6Packet, Packet};
|
use pnet_packet::{ip::IpNextHeaderProtocol, ipv4::Ipv4Packet, ipv6::Ipv6Packet, Packet};
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum PacketError {
|
||||||
|
#[error("Packed too small (len: {0})")]
|
||||||
|
PacketTooSmall(usize),
|
||||||
|
#[error("Unknown Internet Protocol version: {0}")]
|
||||||
|
UnknownVersion(u8),
|
||||||
|
}
|
||||||
|
|
||||||
/// A protocol-agnostic packet type
|
/// A protocol-agnostic packet type
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum IpPacket<'a> {
|
pub enum IpPacket<'a> {
|
||||||
@ -15,11 +23,16 @@ pub enum IpPacket<'a> {
|
|||||||
|
|
||||||
impl IpPacket<'_> {
|
impl IpPacket<'_> {
|
||||||
/// Creates a new packet from a byte slice
|
/// Creates a new packet from a byte slice
|
||||||
pub fn new<'a>(bytes: &'a [u8]) -> Option<IpPacket<'a>> {
|
pub fn new<'a>(bytes: &'a [u8]) -> Result<IpPacket<'a>, PacketError> {
|
||||||
|
// Parse the packet. If there is an error, cast None to the error type
|
||||||
match bytes[0] >> 4 {
|
match bytes[0] >> 4 {
|
||||||
4 => Some(IpPacket::V4(Ipv4Packet::new(bytes)?)),
|
4 => Ok(IpPacket::V4(
|
||||||
6 => Some(IpPacket::V6(Ipv6Packet::new(bytes)?)),
|
Ipv4Packet::new(bytes).ok_or_else(|| PacketError::PacketTooSmall(bytes.len()))?,
|
||||||
_ => None,
|
)),
|
||||||
|
6 => Ok(IpPacket::V6(
|
||||||
|
Ipv6Packet::new(bytes).ok_or_else(|| PacketError::PacketTooSmall(bytes.len()))?,
|
||||||
|
)),
|
||||||
|
n => Err(PacketError::UnknownVersion(n)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use ipnet::Ipv4Net;
|
|||||||
|
|
||||||
/// Possible errors thrown in the address reservation process
|
/// Possible errors thrown in the address reservation process
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum TableError {
|
||||||
#[error("Address already reserved: {0}")]
|
#[error("Address already reserved: {0}")]
|
||||||
AddressAlreadyReserved(IpAddr),
|
AddressAlreadyReserved(IpAddr),
|
||||||
#[error("Address pool depleted")]
|
#[error("Address pool depleted")]
|
||||||
@ -49,13 +49,13 @@ impl Nat64Table {
|
|||||||
&mut self,
|
&mut self,
|
||||||
ipv6: Ipv6Addr,
|
ipv6: Ipv6Addr,
|
||||||
ipv4: Ipv4Addr,
|
ipv4: Ipv4Addr,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), TableError> {
|
||||||
// Check if either address is already reserved
|
// Check if either address is already reserved
|
||||||
self.prune();
|
self.prune();
|
||||||
if self.reservations.contains_left(&ipv6) {
|
if self.reservations.contains_left(&ipv6) {
|
||||||
return Err(Error::AddressAlreadyReserved(ipv6.into()));
|
return Err(TableError::AddressAlreadyReserved(ipv6.into()));
|
||||||
} else if self.reservations.contains_right(&ipv4) {
|
} else if self.reservations.contains_right(&ipv4) {
|
||||||
return Err(Error::AddressAlreadyReserved(ipv4.into()));
|
return Err(TableError::AddressAlreadyReserved(ipv4.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the reservation
|
// Add the reservation
|
||||||
@ -65,7 +65,7 @@ impl Nat64Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get or assign an IPv4 address for the given IPv6 address
|
/// Get or assign an IPv4 address for the given IPv6 address
|
||||||
pub fn get_or_assign_ipv4(&mut self, ipv6: Ipv6Addr) -> Result<Ipv4Addr, Error> {
|
pub fn get_or_assign_ipv4(&mut self, ipv6: Ipv6Addr) -> Result<Ipv4Addr, TableError> {
|
||||||
// Prune old reservations
|
// Prune old reservations
|
||||||
self.prune();
|
self.prune();
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ impl Nat64Table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, we failed to find an available address
|
// If we get here, we failed to find an available address
|
||||||
Err(Error::AddressPoolDepleted)
|
Err(TableError::AddressPoolDepleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to find an IPv6 address for the given IPv4 address
|
/// Try to find an IPv6 address for the given IPv4 address
|
||||||
|
Loading…
x
Reference in New Issue
Block a user