use std::collections::HashMap;
use std::net::{IpAddr, SocketAddr};
use std::time::{Duration, Instant};
pub mod constants {
use std::time::Duration;
pub const MIN_MIGRATION_INTERVAL: Duration = Duration::from_secs(1);
pub const AMPLIFICATION_FACTOR: usize = 3;
}
#[derive(Debug, Clone)]
struct AddressState {
first_seen: Instant,
bytes_received: usize,
bytes_sent: usize,
validated: bool,
}
impl AddressState {
fn new() -> Self {
Self {
first_seen: Instant::now(),
bytes_received: 0,
bytes_sent: 0,
validated: false,
}
}
}
fn subnet_key(addr: &IpAddr) -> Vec<u8> {
match addr {
IpAddr::V4(v4) => {
let octets = v4.octets();
vec![octets[0], octets[1], octets[2]] }
IpAddr::V6(v6) => {
let segments = v6.segments();
let mut key = Vec::with_capacity(6);
for seg in &segments[0..3] {
key.extend_from_slice(&seg.to_be_bytes());
}
key
}
}
}
#[derive(Debug)]
pub struct MigrationState {
current_address: SocketAddr,
addresses: HashMap<SocketAddr, AddressState>,
subnet_last_migration: HashMap<Vec<u8>, Instant>,
}
impl MigrationState {
pub fn new(initial_address: SocketAddr) -> Self {
let mut addresses = HashMap::new();
let mut state = AddressState::new();
state.validated = true; addresses.insert(initial_address, state);
Self {
current_address: initial_address,
addresses,
subnet_last_migration: HashMap::new(),
}
}
pub fn current_address(&self) -> SocketAddr {
self.current_address
}
pub fn on_receive(&mut self, from: SocketAddr, bytes: usize) {
let state = self.addresses.entry(from).or_insert_with(AddressState::new);
state.bytes_received = state.bytes_received.saturating_add(bytes);
}
pub fn on_send(&mut self, to: SocketAddr, bytes: usize) {
if let Some(state) = self.addresses.get_mut(&to) {
state.bytes_sent = state.bytes_sent.saturating_add(bytes);
}
}
pub fn can_send(&self, to: SocketAddr, bytes: usize) -> bool {
if let Some(state) = self.addresses.get(&to) {
if state.validated {
return true;
}
let allowed = state.bytes_received.saturating_mul(constants::AMPLIFICATION_FACTOR);
state.bytes_sent.saturating_add(bytes) <= allowed
} else {
false
}
}
pub fn validate_address(&mut self, addr: SocketAddr) -> bool {
if addr != self.current_address {
let current_subnet = subnet_key(&self.current_address.ip());
let new_subnet = subnet_key(&addr.ip());
if current_subnet != new_subnet {
let now = Instant::now();
if let Some(&last) = self.subnet_last_migration.get(&new_subnet)
&& now.duration_since(last) < constants::MIN_MIGRATION_INTERVAL
{
return false; }
self.subnet_last_migration.insert(new_subnet, now);
}
}
let state = self.addresses.entry(addr).or_insert_with(AddressState::new);
state.validated = true;
self.current_address = addr;
true
}
pub fn is_validated(&self, addr: SocketAddr) -> bool {
self.addresses
.get(&addr)
.is_some_and(|state| state.validated)
}
pub fn cleanup(&mut self, max_age: Duration) {
let now = Instant::now();
self.addresses.retain(|addr, state| {
*addr == self.current_address || now.duration_since(state.first_seen) < max_age
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn addr_v4(a: u8, b: u8, c: u8, d: u8, port: u16) -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(a, b, c, d)), port)
}
#[allow(dead_code)]
fn addr_v6(segments: [u16; 8], port: u16) -> SocketAddr {
SocketAddr::new(
IpAddr::V6(Ipv6Addr::new(
segments[0],
segments[1],
segments[2],
segments[3],
segments[4],
segments[5],
segments[6],
segments[7],
)),
port,
)
}
#[test]
fn test_migration_state_initial() {
let addr = addr_v4(192, 168, 1, 100, 8080);
let state = MigrationState::new(addr);
assert_eq!(state.current_address(), addr);
assert!(state.is_validated(addr));
}
#[test]
fn test_anti_amplification() {
let initial = addr_v4(192, 168, 1, 100, 8080);
let mut state = MigrationState::new(initial);
let new_addr = addr_v4(10, 0, 0, 50, 9090);
assert!(!state.can_send(new_addr, 100));
state.on_receive(new_addr, 100);
assert!(state.can_send(new_addr, 300));
assert!(!state.can_send(new_addr, 301));
state.validate_address(new_addr);
assert!(state.can_send(new_addr, 10000));
}
#[test]
fn test_migration_same_subnet() {
let initial = addr_v4(192, 168, 1, 100, 8080);
let mut state = MigrationState::new(initial);
let new_addr = addr_v4(192, 168, 1, 200, 9090);
assert!(state.validate_address(new_addr));
assert_eq!(state.current_address(), new_addr);
}
#[test]
fn test_migration_different_subnet_rate_limit() {
let initial = addr_v4(192, 168, 1, 100, 8080);
let mut state = MigrationState::new(initial);
let new_addr = addr_v4(10, 0, 0, 50, 9090);
assert!(state.validate_address(new_addr));
let another_addr = addr_v4(172, 16, 0, 1, 7070);
assert!(state.validate_address(another_addr));
}
#[test]
fn test_subnet_key_v4() {
let ip = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100));
let key = subnet_key(&ip);
assert_eq!(key, vec![192, 168, 1]);
}
#[test]
fn test_subnet_key_v6() {
let ip = IpAddr::V6(Ipv6Addr::new(0x2001, 0x0db8, 0x85a3, 0, 0, 0, 0, 1));
let key = subnet_key(&ip);
assert_eq!(key.len(), 6);
assert_eq!(&key[0..2], &[0x20, 0x01]);
assert_eq!(&key[2..4], &[0x0d, 0xb8]);
assert_eq!(&key[4..6], &[0x85, 0xa3]);
}
#[test]
fn test_cleanup() {
let initial = addr_v4(192, 168, 1, 100, 8080);
let mut state = MigrationState::new(initial);
let other = addr_v4(10, 0, 0, 50, 9090);
state.on_receive(other, 100);
state.cleanup(Duration::from_nanos(1));
assert!(state.is_validated(initial));
}
}