From 23fd3f22719416983b681f66b599595663594ce3 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Fri, 4 Aug 2023 19:24:27 -0400 Subject: [PATCH] Fix addr format ambiguity --- libs/fast-nat/Cargo.toml | 3 +- libs/fast-nat/src/cpnat.rs | 83 ++++++++++++------------------------ libs/fast-nat/src/error.rs | 10 +++-- libs/fast-nat/src/nat.rs | 22 +++++----- src/common/packet_handler.rs | 4 +- src/protomask.rs | 10 ++--- 6 files changed, 53 insertions(+), 79 deletions(-) diff --git a/libs/fast-nat/Cargo.toml b/libs/fast-nat/Cargo.toml index e6d1993..7bb1672 100644 --- a/libs/fast-nat/Cargo.toml +++ b/libs/fast-nat/Cargo.toml @@ -15,4 +15,5 @@ categories = [] [dependencies] log = "^0.4" rustc-hash = "1.1.0" -thiserror = "^1.0.44" \ No newline at end of file +thiserror = "^1.0.44" +ipnet = "^2.8.0" \ No newline at end of file diff --git a/libs/fast-nat/src/cpnat.rs b/libs/fast-nat/src/cpnat.rs index 740df12..86f5ee4 100644 --- a/libs/fast-nat/src/cpnat.rs +++ b/libs/fast-nat/src/cpnat.rs @@ -1,5 +1,9 @@ -use std::time::Duration; +use std::{ + net::{Ipv4Addr, Ipv6Addr}, + time::Duration, +}; +use ipnet::Ipv4Net; use rustc_hash::FxHashMap; use crate::{bimap::BiHashMap, error::Error, timeout::MaybeTimeout}; @@ -50,7 +54,7 @@ impl CrossProtocolNetworkAddressTable { } /// Insert a new indefinite mapping - pub fn insert_indefinite, T6: Into>(&mut self, ipv4: T4, ipv6: T6) { + pub fn insert_indefinite(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr) { self.prune(); let (ipv4, ipv6) = (ipv4.into(), ipv6.into()); self.addr_map.insert(ipv4, ipv6); @@ -58,12 +62,7 @@ impl CrossProtocolNetworkAddressTable { } /// Insert a new mapping with a finite time-to-live - pub fn insert, T6: Into>( - &mut self, - ipv4: T4, - ipv6: T6, - duration: Duration, - ) { + pub fn insert(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr, duration: Duration) { self.prune(); let (ipv4, ipv6) = (ipv4.into(), ipv6.into()); self.addr_map.insert(ipv4, ipv6); @@ -78,14 +77,18 @@ impl CrossProtocolNetworkAddressTable { /// Get the IPv6 address for a given IPv4 address #[must_use] - pub fn get_ipv6>(&self, ipv4: T) -> Option { - self.addr_map.get_right(&ipv4.into()).copied() + pub fn get_ipv6(&self, ipv4: &Ipv4Addr) -> Option { + self.addr_map + .get_right(&(*ipv4).into()) + .map(|addr| (*addr).into()) } /// Get the IPv4 address for a given IPv6 address #[must_use] - pub fn get_ipv4>(&self, ipv6: T) -> Option { - self.addr_map.get_left(&ipv6.into()).copied() + pub fn get_ipv4(&self, ipv6: &Ipv6Addr) -> Option { + self.addr_map + .get_left(&(*ipv6).into()) + .map(|addr| (*addr).into()) } /// Get the number of mappings in the table @@ -115,49 +118,25 @@ pub struct CrossProtocolNetworkAddressTableWithIpv4Pool { /// Internal table table: CrossProtocolNetworkAddressTable, /// Internal pool of IPv4 prefixes to assign new mappings from - pool: Vec<(u32, u32)>, + pool: Vec, /// The timeout to use for new entries timeout: Duration, - /// The pre-calculated maximum number of mappings that can be created - max_mappings: usize, } impl CrossProtocolNetworkAddressTableWithIpv4Pool { /// Construct a new Cross-protocol network address table with a given IPv4 pool #[must_use] - pub fn new + Clone>(pool: &[(T, T)], timeout: Duration) -> Self { + pub fn new(pool: &[Ipv4Net], timeout: Duration) -> Self { Self { table: CrossProtocolNetworkAddressTable::default(), - pool: pool - .iter() - .map(|(a, b)| (a.clone().into(), b.clone().into())) - .collect(), + pool: pool.to_vec(), timeout, - max_mappings: pool - .iter() - .map(|(_, netmask)| (*netmask).clone().into() as usize) - .map(|netmask| !netmask) - .sum(), } } - /// Check if the pool contains an address - #[must_use] - pub fn contains>(&self, addr: T) -> bool { - let addr = addr.into(); - self.pool - .iter() - .any(|(network_addr, netmask)| (addr & netmask) == *network_addr) - } - /// Insert a new static mapping - pub fn insert_static, T6: Into>( - &mut self, - ipv4: T4, - ipv6: T6, - ) -> Result<(), Error> { - let (ipv4, ipv6) = (ipv4.into(), ipv6.into()); - if !self.contains(ipv4) { + pub fn insert_static(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Result<(), Error> { + if !self.pool.iter().any(|prefix| prefix.contains(&ipv4)) { return Err(Error::InvalidIpv4Address(ipv4)); } self.table.insert_indefinite(ipv4, ipv6); @@ -165,31 +144,25 @@ impl CrossProtocolNetworkAddressTableWithIpv4Pool { } /// Gets the IPv4 address for a given IPv6 address or inserts a new mapping if one does not exist (if possible) - pub fn get_or_create_ipv4>(&mut self, ipv6: T) -> Result { - let ipv6 = ipv6.into(); - + pub fn get_or_create_ipv4(&mut self, ipv6: &Ipv6Addr) -> Result { // Return the known mapping if it exists if let Some(ipv4) = self.table.get_ipv4(ipv6) { return Ok(ipv4); } - // Otherwise, we first need to make sure there is actually room for a new mapping - if self.table.len() >= self.max_mappings { - return Err(Error::Ipv4PoolExhausted(self.max_mappings)); - } - // Find the next available IPv4 address in the pool let new_address = self .pool .iter() - .map(|(network_address, netmask)| (*network_address)..(*network_address | !netmask)) - .find_map(|mut addr_range| addr_range.find(|addr| !self.table.get_ipv6(*addr).is_some())) - .ok_or(Error::Ipv4PoolExhausted(self.max_mappings))?; + .map(|prefix| prefix.hosts()) + .flatten() + .find(|addr| !self.table.get_ipv6(addr).is_some()) + .ok_or(Error::Ipv4PoolExhausted)?; // Insert the new mapping - self.table.insert(new_address, ipv6, self.timeout); + self.table.insert(new_address, *ipv6, self.timeout); log::info!( - "New cross-protocol address mapping: {:02x} -> {:02x}", + "New cross-protocol address mapping: {} -> {}", ipv6, new_address ); @@ -200,7 +173,7 @@ impl CrossProtocolNetworkAddressTableWithIpv4Pool { /// Gets the IPv6 address for a given IPv4 address if it exists #[must_use] - pub fn get_ipv6>(&self, ipv4: T) -> Option { + pub fn get_ipv6(&self, ipv4: &Ipv4Addr) -> Option { self.table.get_ipv6(ipv4) } } diff --git a/libs/fast-nat/src/error.rs b/libs/fast-nat/src/error.rs index 0b2e276..c024630 100644 --- a/libs/fast-nat/src/error.rs +++ b/libs/fast-nat/src/error.rs @@ -1,7 +1,9 @@ +use std::net::Ipv4Addr; + #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("Ipv4 address does not belong to the NAT pool: {0:02x}")] - InvalidIpv4Address(u32), - #[error("IPv4 pool exhausted. All {0} spots filled")] - Ipv4PoolExhausted(usize), + #[error("Ipv4 address does not belong to the NAT pool: {0}")] + InvalidIpv4Address(Ipv4Addr), + #[error("IPv4 pool exhausted")] + Ipv4PoolExhausted, } diff --git a/libs/fast-nat/src/nat.rs b/libs/fast-nat/src/nat.rs index 1fc6d3b..63fb144 100644 --- a/libs/fast-nat/src/nat.rs +++ b/libs/fast-nat/src/nat.rs @@ -1,8 +1,6 @@ -use std::time::Duration; - -use rustc_hash::FxHashMap; - use crate::{bimap::BiHashMap, timeout::MaybeTimeout}; +use rustc_hash::FxHashMap; +use std::{net::Ipv4Addr, time::Duration}; /// A table of network address mappings #[derive(Debug)] @@ -50,7 +48,7 @@ impl NetworkAddressTable { } /// Insert a new indefinite mapping - pub fn insert_indefinite>(&mut self, left: T, right: T) { + pub fn insert_indefinite(&mut self, left: Ipv4Addr, right: Ipv4Addr) { self.prune(); let (left, right) = (left.into(), right.into()); self.addr_map.insert(left, right); @@ -58,7 +56,7 @@ impl NetworkAddressTable { } /// Insert a new mapping with a finite time-to-live - pub fn insert>(&mut self, left: T, right: T, duration: Duration) { + pub fn insert(&mut self, left: Ipv4Addr, right: Ipv4Addr, duration: Duration) { self.prune(); let (left, right) = (left.into(), right.into()); self.addr_map.insert(left, right); @@ -73,14 +71,18 @@ impl NetworkAddressTable { /// Get the right value for a given left value #[must_use] - pub fn get_right>(&self, left: T) -> Option { - self.addr_map.get_right(&left.into()).copied() + pub fn get_right(&self, left: &Ipv4Addr) -> Option { + self.addr_map + .get_right(&(*left).into()) + .map(|addr| (*addr).into()) } /// Get the left value for a given right value #[must_use] - pub fn get_left>(&self, right: T) -> Option { - self.addr_map.get_left(&right.into()).copied() + pub fn get_left(&self, right: &Ipv4Addr) -> Option { + self.addr_map + .get_left(&(*right).into()) + .map(|addr| (*addr).into()) } } diff --git a/src/common/packet_handler.rs b/src/common/packet_handler.rs index eeb2dfc..24366c7 100644 --- a/src/common/packet_handler.rs +++ b/src/common/packet_handler.rs @@ -90,8 +90,8 @@ where ); None } - PacketHandlingError::FastNatError(fast_nat::error::Error::Ipv4PoolExhausted(size)) => { - log::warn!("IPv4 pool exhausted with {} mappings", size); + PacketHandlingError::FastNatError(fast_nat::error::Error::Ipv4PoolExhausted) => { + log::warn!("IPv4 pool exhausted. Dropping packet."); None } PacketHandlingError::FastNatError(fast_nat::error::Error::InvalidIpv4Address(addr)) => { diff --git a/src/protomask.rs b/src/protomask.rs index f0640c5..a08fdad 100644 --- a/src/protomask.rs +++ b/src/protomask.rs @@ -140,11 +140,7 @@ pub async fn main() { // Set up the address table let mut addr_table = RefCell::new(CrossProtocolNetworkAddressTableWithIpv4Pool::new( - pool_prefixes - .iter() - .map(|prefix| (u32::from(prefix.addr()), u32::from(prefix.netmask()))) - .collect::>() - .as_slice(), + &pool_prefixes, Duration::from_secs(args.reservation_timeout), )); for (v6_addr, v4_addr) in args.get_static_reservations().unwrap() { @@ -171,7 +167,7 @@ pub async fn main() { if let Some(output) = handle_packet( &buffer[..len], // IPv4 -> IPv6 - |packet, source, dest| match addr_table.borrow().get_ipv6(*dest) { + |packet, source, dest| match addr_table.borrow().get_ipv6(dest) { Some(new_destination) => Ok(translate_ipv4_to_ipv6( packet, unsafe { embed_ipv4_addr_unchecked(*source, args.translation_prefix) }, @@ -187,7 +183,7 @@ pub async fn main() { |packet, source, dest| { Ok(translate_ipv6_to_ipv4( packet, - addr_table.borrow_mut().get_or_create_ipv4(*source)?.into(), + addr_table.borrow_mut().get_or_create_ipv4(source)?.into(), unsafe { extract_ipv4_addr_unchecked(*dest, args.translation_prefix.prefix_len()) },