use core::fmt;
use core::net::{Ipv4Addr, SocketAddrV4};
pub const MAX_PRIVATE_RANGES: usize = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrivateRange {
network: Ipv4Addr,
prefix: u8,
}
impl PrivateRange {
pub const fn new(network: Ipv4Addr, prefix: u8) -> Result<Self, NatError> {
if prefix > 32 {
return Err(NatError::Prefix);
}
let bits = network.to_bits() & mask_bits(prefix);
Ok(Self {
network: Ipv4Addr::from_bits(bits),
prefix,
})
}
#[must_use]
pub const fn network(&self) -> Ipv4Addr {
self.network
}
#[must_use]
pub const fn prefix(&self) -> u8 {
self.prefix
}
#[must_use]
pub const fn contains(&self, ip: Ipv4Addr) -> bool {
(ip.to_bits() ^ self.network.to_bits()) & mask_bits(self.prefix) == 0
}
}
impl fmt::Display for PrivateRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.network, self.prefix)
}
}
const fn mask_bits(prefix: u8) -> u32 {
match prefix {
0 => 0,
_ => u32::MAX << (32 - prefix as u32),
}
}
#[must_use]
pub const fn is_rfc1918(ip: Ipv4Addr) -> bool {
let [a, b, ..] = ip.octets();
a == 10 || (a == 172 && b & 0xF0 == 16) || (a == 192 && b == 168)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum NatError {
Prefix,
TooManyRanges,
}
impl fmt::Display for NatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Prefix => f.write_str("an IPv4 prefix is at most 32 bits"),
Self::TooManyRanges => write!(
f,
"at most {MAX_PRIVATE_RANGES} private ranges besides RFC 1918"
),
}
}
}
impl core::error::Error for NatError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct Nat {
pub global: SocketAddrV4,
ranges: [PrivateRange; MAX_PRIVATE_RANGES],
len: u8,
}
const UNUSED_RANGE: PrivateRange = PrivateRange {
network: Ipv4Addr::UNSPECIFIED,
prefix: 32,
};
impl Nat {
#[must_use]
pub const fn new(global: SocketAddrV4) -> Self {
Self {
global,
ranges: [UNUSED_RANGE; MAX_PRIVATE_RANGES],
len: 0,
}
}
pub fn add_private_range(&mut self, range: PrivateRange) -> Result<(), NatError> {
if self.private_ranges().contains(&range) {
return Ok(());
}
let slot = self
.ranges
.get_mut(usize::from(self.len))
.ok_or(NatError::TooManyRanges)?;
*slot = range;
self.len += 1;
Ok(())
}
#[must_use]
pub fn private_ranges(&self) -> &[PrivateRange] {
&self.ranges[..usize::from(self.len)]
}
#[must_use]
pub fn is_private(&self, ip: Ipv4Addr) -> bool {
is_rfc1918(ip) || self.private_ranges().iter().any(|range| range.contains(ip))
}
}