use std::{
collections::BTreeMap,
net::{IpAddr, Ipv6Addr, SocketAddr},
sync::{
Arc,
atomic::{AtomicU64, Ordering},
},
};
use snafu::Snafu;
#[derive(Debug, Snafu)]
#[snafu(display("Failed to convert"))]
pub struct IpMappedAddrError;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub(crate) struct IpMappedAddr(Ipv6Addr);
static IP_ADDR_COUNTER: AtomicU64 = AtomicU64::new(1);
impl IpMappedAddr {
const ADDR_PREFIXL: u8 = 0xfd;
const ADDR_GLOBAL_ID: [u8; 5] = [21, 7, 10, 81, 11];
const ADDR_SUBNET: [u8; 2] = [0, 1];
const MAPPED_ADDR_PORT: u16 = 12345;
pub(super) fn generate() -> Self {
let mut addr = [0u8; 16];
addr[0] = Self::ADDR_PREFIXL;
addr[1..6].copy_from_slice(&Self::ADDR_GLOBAL_ID);
addr[6..8].copy_from_slice(&Self::ADDR_SUBNET);
let counter = IP_ADDR_COUNTER.fetch_add(1, Ordering::Relaxed);
addr[8..16].copy_from_slice(&counter.to_be_bytes());
Self(Ipv6Addr::from(addr))
}
pub(crate) fn private_socket_addr(&self) -> SocketAddr {
SocketAddr::new(IpAddr::from(self.0), Self::MAPPED_ADDR_PORT)
}
}
impl TryFrom<Ipv6Addr> for IpMappedAddr {
type Error = IpMappedAddrError;
fn try_from(value: Ipv6Addr) -> std::result::Result<Self, Self::Error> {
let octets = value.octets();
if octets[0] == Self::ADDR_PREFIXL
&& octets[1..6] == Self::ADDR_GLOBAL_ID
&& octets[6..8] == Self::ADDR_SUBNET
{
return Ok(Self(value));
}
Err(IpMappedAddrError)
}
}
impl std::fmt::Display for IpMappedAddr {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "IpMappedAddr({})", self.0)
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct IpMappedAddresses(Arc<std::sync::Mutex<Inner>>);
#[derive(Debug, Default)]
pub(super) struct Inner {
by_mapped_addr: BTreeMap<IpMappedAddr, SocketAddr>,
by_ip_port: BTreeMap<(IpAddr, u16), IpMappedAddr>,
}
impl IpMappedAddresses {
pub(super) fn get_or_register(&self, socket_addr: SocketAddr) -> IpMappedAddr {
let ip_port = (socket_addr.ip(), socket_addr.port());
let mut inner = self.0.lock().expect("poisoned");
if let Some(mapped_addr) = inner.by_ip_port.get(&ip_port) {
return *mapped_addr;
}
let ip_mapped_addr = IpMappedAddr::generate();
inner.by_mapped_addr.insert(ip_mapped_addr, socket_addr);
inner.by_ip_port.insert(ip_port, ip_mapped_addr);
ip_mapped_addr
}
pub(crate) fn get_mapped_addr(&self, socket_addr: &SocketAddr) -> Option<IpMappedAddr> {
let ip_port = (socket_addr.ip(), socket_addr.port());
let inner = self.0.lock().expect("poisoned");
inner.by_ip_port.get(&ip_port).copied()
}
pub(crate) fn get_ip_addr(&self, mapped_addr: &IpMappedAddr) -> Option<SocketAddr> {
let inner = self.0.lock().expect("poisoned");
inner.by_mapped_addr.get(mapped_addr).copied()
}
}