Fix addr format ambiguity
This commit is contained in:
parent
79a861acbf
commit
23fd3f2271
@ -16,3 +16,4 @@ categories = []
|
|||||||
log = "^0.4"
|
log = "^0.4"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
thiserror = "^1.0.44"
|
thiserror = "^1.0.44"
|
||||||
|
ipnet = "^2.8.0"
|
@ -1,5 +1,9 @@
|
|||||||
use std::time::Duration;
|
use std::{
|
||||||
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{bimap::BiHashMap, error::Error, timeout::MaybeTimeout};
|
use crate::{bimap::BiHashMap, error::Error, timeout::MaybeTimeout};
|
||||||
@ -50,7 +54,7 @@ impl CrossProtocolNetworkAddressTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new indefinite mapping
|
/// Insert a new indefinite mapping
|
||||||
pub fn insert_indefinite<T4: Into<u32>, T6: Into<u128>>(&mut self, ipv4: T4, ipv6: T6) {
|
pub fn insert_indefinite(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr) {
|
||||||
self.prune();
|
self.prune();
|
||||||
let (ipv4, ipv6) = (ipv4.into(), ipv6.into());
|
let (ipv4, ipv6) = (ipv4.into(), ipv6.into());
|
||||||
self.addr_map.insert(ipv4, ipv6);
|
self.addr_map.insert(ipv4, ipv6);
|
||||||
@ -58,12 +62,7 @@ impl CrossProtocolNetworkAddressTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new mapping with a finite time-to-live
|
/// Insert a new mapping with a finite time-to-live
|
||||||
pub fn insert<T4: Into<u32>, T6: Into<u128>>(
|
pub fn insert(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr, duration: Duration) {
|
||||||
&mut self,
|
|
||||||
ipv4: T4,
|
|
||||||
ipv6: T6,
|
|
||||||
duration: Duration,
|
|
||||||
) {
|
|
||||||
self.prune();
|
self.prune();
|
||||||
let (ipv4, ipv6) = (ipv4.into(), ipv6.into());
|
let (ipv4, ipv6) = (ipv4.into(), ipv6.into());
|
||||||
self.addr_map.insert(ipv4, ipv6);
|
self.addr_map.insert(ipv4, ipv6);
|
||||||
@ -78,14 +77,18 @@ impl CrossProtocolNetworkAddressTable {
|
|||||||
|
|
||||||
/// Get the IPv6 address for a given IPv4 address
|
/// Get the IPv6 address for a given IPv4 address
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_ipv6<T: Into<u32>>(&self, ipv4: T) -> Option<u128> {
|
pub fn get_ipv6(&self, ipv4: &Ipv4Addr) -> Option<Ipv6Addr> {
|
||||||
self.addr_map.get_right(&ipv4.into()).copied()
|
self.addr_map
|
||||||
|
.get_right(&(*ipv4).into())
|
||||||
|
.map(|addr| (*addr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the IPv4 address for a given IPv6 address
|
/// Get the IPv4 address for a given IPv6 address
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_ipv4<T: Into<u128>>(&self, ipv6: T) -> Option<u32> {
|
pub fn get_ipv4(&self, ipv6: &Ipv6Addr) -> Option<Ipv4Addr> {
|
||||||
self.addr_map.get_left(&ipv6.into()).copied()
|
self.addr_map
|
||||||
|
.get_left(&(*ipv6).into())
|
||||||
|
.map(|addr| (*addr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of mappings in the table
|
/// Get the number of mappings in the table
|
||||||
@ -115,49 +118,25 @@ pub struct CrossProtocolNetworkAddressTableWithIpv4Pool {
|
|||||||
/// Internal table
|
/// Internal table
|
||||||
table: CrossProtocolNetworkAddressTable,
|
table: CrossProtocolNetworkAddressTable,
|
||||||
/// Internal pool of IPv4 prefixes to assign new mappings from
|
/// Internal pool of IPv4 prefixes to assign new mappings from
|
||||||
pool: Vec<(u32, u32)>,
|
pool: Vec<Ipv4Net>,
|
||||||
/// The timeout to use for new entries
|
/// The timeout to use for new entries
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
/// The pre-calculated maximum number of mappings that can be created
|
|
||||||
max_mappings: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CrossProtocolNetworkAddressTableWithIpv4Pool {
|
impl CrossProtocolNetworkAddressTableWithIpv4Pool {
|
||||||
/// Construct a new Cross-protocol network address table with a given IPv4 pool
|
/// Construct a new Cross-protocol network address table with a given IPv4 pool
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new<T: Into<u32> + Clone>(pool: &[(T, T)], timeout: Duration) -> Self {
|
pub fn new(pool: &[Ipv4Net], timeout: Duration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
table: CrossProtocolNetworkAddressTable::default(),
|
table: CrossProtocolNetworkAddressTable::default(),
|
||||||
pool: pool
|
pool: pool.to_vec(),
|
||||||
.iter()
|
|
||||||
.map(|(a, b)| (a.clone().into(), b.clone().into()))
|
|
||||||
.collect(),
|
|
||||||
timeout,
|
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<T: Into<u32>>(&self, addr: T) -> bool {
|
|
||||||
let addr = addr.into();
|
|
||||||
self.pool
|
|
||||||
.iter()
|
|
||||||
.any(|(network_addr, netmask)| (addr & netmask) == *network_addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Insert a new static mapping
|
/// Insert a new static mapping
|
||||||
pub fn insert_static<T4: Into<u32>, T6: Into<u128>>(
|
pub fn insert_static(&mut self, ipv4: Ipv4Addr, ipv6: Ipv6Addr) -> Result<(), Error> {
|
||||||
&mut self,
|
if !self.pool.iter().any(|prefix| prefix.contains(&ipv4)) {
|
||||||
ipv4: T4,
|
|
||||||
ipv6: T6,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let (ipv4, ipv6) = (ipv4.into(), ipv6.into());
|
|
||||||
if !self.contains(ipv4) {
|
|
||||||
return Err(Error::InvalidIpv4Address(ipv4));
|
return Err(Error::InvalidIpv4Address(ipv4));
|
||||||
}
|
}
|
||||||
self.table.insert_indefinite(ipv4, ipv6);
|
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)
|
/// 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<T: Into<u128>>(&mut self, ipv6: T) -> Result<u32, Error> {
|
pub fn get_or_create_ipv4(&mut self, ipv6: &Ipv6Addr) -> Result<Ipv4Addr, Error> {
|
||||||
let ipv6 = ipv6.into();
|
|
||||||
|
|
||||||
// Return the known mapping if it exists
|
// Return the known mapping if it exists
|
||||||
if let Some(ipv4) = self.table.get_ipv4(ipv6) {
|
if let Some(ipv4) = self.table.get_ipv4(ipv6) {
|
||||||
return Ok(ipv4);
|
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
|
// Find the next available IPv4 address in the pool
|
||||||
let new_address = self
|
let new_address = self
|
||||||
.pool
|
.pool
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(network_address, netmask)| (*network_address)..(*network_address | !netmask))
|
.map(|prefix| prefix.hosts())
|
||||||
.find_map(|mut addr_range| addr_range.find(|addr| !self.table.get_ipv6(*addr).is_some()))
|
.flatten()
|
||||||
.ok_or(Error::Ipv4PoolExhausted(self.max_mappings))?;
|
.find(|addr| !self.table.get_ipv6(addr).is_some())
|
||||||
|
.ok_or(Error::Ipv4PoolExhausted)?;
|
||||||
|
|
||||||
// Insert the new mapping
|
// Insert the new mapping
|
||||||
self.table.insert(new_address, ipv6, self.timeout);
|
self.table.insert(new_address, *ipv6, self.timeout);
|
||||||
log::info!(
|
log::info!(
|
||||||
"New cross-protocol address mapping: {:02x} -> {:02x}",
|
"New cross-protocol address mapping: {} -> {}",
|
||||||
ipv6,
|
ipv6,
|
||||||
new_address
|
new_address
|
||||||
);
|
);
|
||||||
@ -200,7 +173,7 @@ impl CrossProtocolNetworkAddressTableWithIpv4Pool {
|
|||||||
|
|
||||||
/// Gets the IPv6 address for a given IPv4 address if it exists
|
/// Gets the IPv6 address for a given IPv4 address if it exists
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_ipv6<T: Into<u32>>(&self, ipv4: T) -> Option<u128> {
|
pub fn get_ipv6(&self, ipv4: &Ipv4Addr) -> Option<Ipv6Addr> {
|
||||||
self.table.get_ipv6(ipv4)
|
self.table.get_ipv6(ipv4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Ipv4 address does not belong to the NAT pool: {0:02x}")]
|
#[error("Ipv4 address does not belong to the NAT pool: {0}")]
|
||||||
InvalidIpv4Address(u32),
|
InvalidIpv4Address(Ipv4Addr),
|
||||||
#[error("IPv4 pool exhausted. All {0} spots filled")]
|
#[error("IPv4 pool exhausted")]
|
||||||
Ipv4PoolExhausted(usize),
|
Ipv4PoolExhausted,
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
|
|
||||||
use crate::{bimap::BiHashMap, timeout::MaybeTimeout};
|
use crate::{bimap::BiHashMap, timeout::MaybeTimeout};
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::{net::Ipv4Addr, time::Duration};
|
||||||
|
|
||||||
/// A table of network address mappings
|
/// A table of network address mappings
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -50,7 +48,7 @@ impl NetworkAddressTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new indefinite mapping
|
/// Insert a new indefinite mapping
|
||||||
pub fn insert_indefinite<T: Into<u32>>(&mut self, left: T, right: T) {
|
pub fn insert_indefinite(&mut self, left: Ipv4Addr, right: Ipv4Addr) {
|
||||||
self.prune();
|
self.prune();
|
||||||
let (left, right) = (left.into(), right.into());
|
let (left, right) = (left.into(), right.into());
|
||||||
self.addr_map.insert(left, right);
|
self.addr_map.insert(left, right);
|
||||||
@ -58,7 +56,7 @@ impl NetworkAddressTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a new mapping with a finite time-to-live
|
/// Insert a new mapping with a finite time-to-live
|
||||||
pub fn insert<T: Into<u32>>(&mut self, left: T, right: T, duration: Duration) {
|
pub fn insert(&mut self, left: Ipv4Addr, right: Ipv4Addr, duration: Duration) {
|
||||||
self.prune();
|
self.prune();
|
||||||
let (left, right) = (left.into(), right.into());
|
let (left, right) = (left.into(), right.into());
|
||||||
self.addr_map.insert(left, right);
|
self.addr_map.insert(left, right);
|
||||||
@ -73,14 +71,18 @@ impl NetworkAddressTable {
|
|||||||
|
|
||||||
/// Get the right value for a given left value
|
/// Get the right value for a given left value
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_right<T: Into<u32>>(&self, left: T) -> Option<u32> {
|
pub fn get_right(&self, left: &Ipv4Addr) -> Option<Ipv4Addr> {
|
||||||
self.addr_map.get_right(&left.into()).copied()
|
self.addr_map
|
||||||
|
.get_right(&(*left).into())
|
||||||
|
.map(|addr| (*addr).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the left value for a given right value
|
/// Get the left value for a given right value
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_left<T: Into<u32>>(&self, right: T) -> Option<u32> {
|
pub fn get_left(&self, right: &Ipv4Addr) -> Option<Ipv4Addr> {
|
||||||
self.addr_map.get_left(&right.into()).copied()
|
self.addr_map
|
||||||
|
.get_left(&(*right).into())
|
||||||
|
.map(|addr| (*addr).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +90,8 @@ where
|
|||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
PacketHandlingError::FastNatError(fast_nat::error::Error::Ipv4PoolExhausted(size)) => {
|
PacketHandlingError::FastNatError(fast_nat::error::Error::Ipv4PoolExhausted) => {
|
||||||
log::warn!("IPv4 pool exhausted with {} mappings", size);
|
log::warn!("IPv4 pool exhausted. Dropping packet.");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
PacketHandlingError::FastNatError(fast_nat::error::Error::InvalidIpv4Address(addr)) => {
|
PacketHandlingError::FastNatError(fast_nat::error::Error::InvalidIpv4Address(addr)) => {
|
||||||
|
@ -140,11 +140,7 @@ pub async fn main() {
|
|||||||
|
|
||||||
// Set up the address table
|
// Set up the address table
|
||||||
let mut addr_table = RefCell::new(CrossProtocolNetworkAddressTableWithIpv4Pool::new(
|
let mut addr_table = RefCell::new(CrossProtocolNetworkAddressTableWithIpv4Pool::new(
|
||||||
pool_prefixes
|
&pool_prefixes,
|
||||||
.iter()
|
|
||||||
.map(|prefix| (u32::from(prefix.addr()), u32::from(prefix.netmask())))
|
|
||||||
.collect::<Vec<(u32, u32)>>()
|
|
||||||
.as_slice(),
|
|
||||||
Duration::from_secs(args.reservation_timeout),
|
Duration::from_secs(args.reservation_timeout),
|
||||||
));
|
));
|
||||||
for (v6_addr, v4_addr) in args.get_static_reservations().unwrap() {
|
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(
|
if let Some(output) = handle_packet(
|
||||||
&buffer[..len],
|
&buffer[..len],
|
||||||
// IPv4 -> IPv6
|
// 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(
|
Some(new_destination) => Ok(translate_ipv4_to_ipv6(
|
||||||
packet,
|
packet,
|
||||||
unsafe { embed_ipv4_addr_unchecked(*source, args.translation_prefix) },
|
unsafe { embed_ipv4_addr_unchecked(*source, args.translation_prefix) },
|
||||||
@ -187,7 +183,7 @@ pub async fn main() {
|
|||||||
|packet, source, dest| {
|
|packet, source, dest| {
|
||||||
Ok(translate_ipv6_to_ipv4(
|
Ok(translate_ipv6_to_ipv4(
|
||||||
packet,
|
packet,
|
||||||
addr_table.borrow_mut().get_or_create_ipv4(*source)?.into(),
|
addr_table.borrow_mut().get_or_create_ipv4(source)?.into(),
|
||||||
unsafe {
|
unsafe {
|
||||||
extract_ipv4_addr_unchecked(*dest, args.translation_prefix.prefix_len())
|
extract_ipv4_addr_unchecked(*dest, args.translation_prefix.prefix_len())
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user