From 4dbe326ea18195e48938b5c6754960f897d21a2f Mon Sep 17 00:00:00 2001 From: Max Niederman Date: Mon, 22 Apr 2024 21:28:32 -0700 Subject: [PATCH] fix: reset tunnel state on reconnection Specifically, tunnels in the router configuration are now stamped with the handshake time. Applying a config with a new handshake timestamp for a tunnel wipes its state. --- packages/centipede_control/src/lib.rs | 4 + packages/centipede_router/src/config.rs | 152 +++++++++++++++++++- packages/centipede_router/src/lib.rs | 7 + packages/centipede_router/tests/udp_conn.rs | 4 +- packages/centipede_router/tests/udp_perf.rs | 4 +- 5 files changed, 163 insertions(+), 8 deletions(-) diff --git a/packages/centipede_control/src/lib.rs b/packages/centipede_control/src/lib.rs index a722bf3..d904203 100644 --- a/packages/centipede_control/src/lib.rs +++ b/packages/centipede_control/src/lib.rs @@ -461,6 +461,7 @@ impl Controller { self.router_config.recv_tunnels.insert( public_key_to_peer_id(message.sender()), centipede_router::config::RecvTunnel { + initialized_at: *handshake_timestamp, cipher: cipher.clone(), }, ); @@ -471,6 +472,7 @@ impl Controller { self.router_config.send_tunnels.insert( public_key_to_peer_id(message.sender()), centipede_router::config::SendTunnel { + initialized_at: *handshake_timestamp, cipher: cipher.clone(), links: HashSet::new(), }, @@ -529,6 +531,7 @@ impl Controller { self.router_config.recv_tunnels.insert( public_key_to_peer_id(message.sender()), centipede_router::config::RecvTunnel { + initialized_at: handshake_timestamp, cipher: cipher.clone(), }, ); @@ -545,6 +548,7 @@ impl Controller { self.router_config.send_tunnels.insert( public_key_to_peer_id(message.sender()), centipede_router::config::SendTunnel { + initialized_at: handshake_timestamp, cipher: cipher.clone(), links: local_addrs .iter() diff --git a/packages/centipede_router/src/config.rs b/packages/centipede_router/src/config.rs index af129c5..7544cb2 100644 --- a/packages/centipede_router/src/config.rs +++ b/packages/centipede_router/src/config.rs @@ -1,7 +1,5 @@ use std::{ - collections::{HashMap, HashSet}, - net::SocketAddr, - sync::{atomic::AtomicU64, Arc}, + collections::{HashMap, HashSet}, net::SocketAddr, sync::Arc, time::SystemTime }; use chacha20poly1305::ChaCha20Poly1305; @@ -27,6 +25,11 @@ pub struct Router { /// Configuration of a receiving tunnel. #[derive(Clone)] pub struct RecvTunnel { + // TODO: should this really be a time type or just an opaque identifier? + // arguably the router shouldn't care about the time + /// Timestamp at which the tunnel was initialized. Used to reset the memory. + pub initialized_at: SystemTime, + /// Cipher with which to decrypt messages. pub cipher: ChaCha20Poly1305, } @@ -34,6 +37,9 @@ pub struct RecvTunnel { /// Configuration of a sending tunndfel. #[derive(Clone)] pub struct SendTunnel { + /// Timestamp at which the tunnel was initialized. Used to reset the sequence number. + pub initialized_at: SystemTime, + /// Cipher with which to encrypt messages. pub cipher: ChaCha20Poly1305, @@ -55,11 +61,13 @@ pub(crate) fn apply(config: &Router, state: &crate::ConfiguredRouter) -> crate:: ( *id, crate::RecvTunnel { + initialized_at: tun.initialized_at, cipher: tun.cipher.clone(), memory: state .recv_tunnels .get(id) - .map(|tun| tun.memory.clone()) + .filter(|old| old.initialized_at == tun.initialized_at) + .map(|old| old.memory.clone()) .unwrap_or_default(), }, ) @@ -72,13 +80,15 @@ pub(crate) fn apply(config: &Router, state: &crate::ConfiguredRouter) -> crate:: ( *id, crate::SendTunnel { + initialized_at: tun.initialized_at, links: tun.links.iter().copied().collect(), cipher: tun.cipher.clone(), next_sequence_number: state .send_tunnels .get(id) - .map(|tun| tun.next_sequence_number.clone()) - .unwrap_or(Arc::new(AtomicU64::new(0))), + .filter(|old| old.initialized_at == tun.initialized_at) + .map(|old| old.next_sequence_number.clone()) + .unwrap_or_default() }, ) }) @@ -107,6 +117,8 @@ impl<'r> ConfiguratorHandle<'r> { #[cfg(test)] mod tests { + use std::time::Duration; + use chacha20poly1305::KeyInit; use super::*; @@ -154,6 +166,7 @@ mod tests { map.insert( [2; 8], super::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: ChaCha20Poly1305::new((&[0; 32]).into()), links: [Link { local: SocketAddr::from(([127, 0, 0, 1], 0)), @@ -199,6 +212,7 @@ mod tests { map.insert( [2; 8], super::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: ChaCha20Poly1305::new((&[0; 32]).into()), links: [Link { local: SocketAddr::from(([127, 0, 0, 1], 0)), @@ -221,6 +235,74 @@ mod tests { ); } + #[test] + fn reinitializing_send_tunnel_resets_sequence() { + let mut state = crate::ConfiguredRouter::default(); + + state = apply( + &super::Router { + local_id: [1; 8], + recv_addrs: [SocketAddr::from(([127, 0, 0, 1], 0))] + .into_iter() + .collect(), + recv_tunnels: HashMap::new(), + send_tunnels: { + let mut map = HashMap::new(); + map.insert( + [2; 8], + super::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH, + cipher: ChaCha20Poly1305::new((&[0; 32]).into()), + links: [Link { + local: SocketAddr::from(([127, 0, 0, 1], 0)), + remote: SocketAddr::from(([127, 0, 0, 1], 0)), + }] + .into_iter() + .collect(), + }, + ); + map + }, + }, + &state, + ); + let sequence = state.send_tunnels[&[2; 8]].next_sequence_number.as_ref() as *const _; + + state = apply( + &super::Router { + local_id: [1; 8], + recv_addrs: [SocketAddr::from(([127, 0, 0, 1], 0))] + .into_iter() + .collect(), + recv_tunnels: HashMap::new(), + send_tunnels: { + let mut map = HashMap::new(); + map.insert( + [2; 8], + super::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH + Duration::from_secs(1), + cipher: ChaCha20Poly1305::new((&[0; 32]).into()), + links: [Link { + local: SocketAddr::from(([127, 0, 0, 1], 0)), + remote: SocketAddr::from(([127, 0, 0, 1], 0)), + }] + .into_iter() + .collect(), + }, + ); + map + }, + }, + &state, + ); + + assert_ne!( + state.send_tunnels[&[2; 8]].next_sequence_number.as_ref() as *const _, + sequence, + "sequence number generator was preserved" + ); + } + #[test] fn updating_recv_tunnel_preserves_memory() { let mut state = crate::ConfiguredRouter::default(); @@ -236,6 +318,7 @@ mod tests { map.insert( [2; 8], super::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: ChaCha20Poly1305::new((&[0; 32]).into()), }, ); @@ -268,6 +351,7 @@ mod tests { map.insert( [2; 8], super::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: ChaCha20Poly1305::new((&[1; 32]).into()), }, ); @@ -285,4 +369,60 @@ mod tests { "packet memory was not preserved" ); } + + #[test] + fn reinitializing_recv_tunnel_resets_memory() { + let mut state = crate::ConfiguredRouter::default(); + + state = apply( + &super::Router { + local_id: [1; 8], + recv_addrs: [SocketAddr::from(([127, 0, 0, 1], 0))] + .into_iter() + .collect(), + recv_tunnels: { + let mut map = HashMap::new(); + map.insert( + [2; 8], + super::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH, + cipher: ChaCha20Poly1305::new((&[0; 32]).into()), + }, + ); + map + }, + send_tunnels: HashMap::new(), + }, + &state, + ); + let memory = state.recv_tunnels[&[2; 8]].memory.as_ref() as *const _; + + state = apply( + &super::Router { + local_id: [1; 8], + recv_addrs: [SocketAddr::from(([127, 0, 0, 1], 0))] + .into_iter() + .collect(), + recv_tunnels: { + let mut map = HashMap::new(); + map.insert( + [2; 8], + super::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH + Duration::from_secs(1), + cipher: ChaCha20Poly1305::new((&[1; 32]).into()), + }, + ); + map + }, + send_tunnels: HashMap::new(), + }, + &state, + ); + + assert_ne!( + state.recv_tunnels[&[2; 8]].memory.as_ref() as *const _, + memory, + "packet memory was preserved" + ); + } } diff --git a/packages/centipede_router/src/lib.rs b/packages/centipede_router/src/lib.rs index 8b683c8..45837d9 100644 --- a/packages/centipede_router/src/lib.rs +++ b/packages/centipede_router/src/lib.rs @@ -10,6 +10,7 @@ use std::{ atomic::{AtomicBool, AtomicU64, Ordering}, Arc, }, + time::SystemTime, }; use arc_swap::ArcSwap; @@ -52,6 +53,9 @@ struct ConfiguredRouter { /// The state of a receiving tunnel. #[derive(Clone)] struct RecvTunnel { + /// Timestamp at which the tunnel was initialized. Used to reset the memory. + initialized_at: SystemTime, + /// Cipher with which to decrypt messages. cipher: ChaCha20Poly1305, @@ -62,6 +66,9 @@ struct RecvTunnel { /// The state of a sending tunnel. #[derive(Clone)] struct SendTunnel { + /// Timestamp at which the tunnel was initialized. Used to reset the memory. + initialized_at: SystemTime, + /// Address pairs on which to send messages. links: Vec, diff --git a/packages/centipede_router/tests/udp_conn.rs b/packages/centipede_router/tests/udp_conn.rs index 3aea809..e5dc4d9 100644 --- a/packages/centipede_router/tests/udp_conn.rs +++ b/packages/centipede_router/tests/udp_conn.rs @@ -3,7 +3,7 @@ use std::{ io, net::{SocketAddr, UdpSocket}, thread, - time::Duration, + time::{Duration, SystemTime}, }; use centipede_proto::{ @@ -61,6 +61,7 @@ fn half_duplex_single_message() { map.insert( [1; 8], config::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: dummy_cipher(), links: ctx.possible_links_to([1; 8]), }, @@ -99,6 +100,7 @@ fn half_duplex_single_message() { map.insert( [0; 8], config::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: dummy_cipher(), }, ); diff --git a/packages/centipede_router/tests/udp_perf.rs b/packages/centipede_router/tests/udp_perf.rs index d1a4a24..6a0d343 100644 --- a/packages/centipede_router/tests/udp_perf.rs +++ b/packages/centipede_router/tests/udp_perf.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use std::{mem, net::UdpSocket, thread}; +use std::{mem, net::UdpSocket, thread, time::SystemTime}; use centipede_proto::PacketMessage; use centipede_router::{config, Link, Router}; @@ -45,6 +45,7 @@ fn half_duplex_iter(packet_size: usize, num_packets: usize) { recv_tunnels: [( [0; 8], config::RecvTunnel { + initialized_at: SystemTime::UNIX_EPOCH, cipher: dummy_cipher(), }, )] @@ -78,6 +79,7 @@ fn half_duplex_iter(packet_size: usize, num_packets: usize) { send_tunnels: [( [1; 8], config::SendTunnel { + initialized_at: SystemTime::UNIX_EPOCH, links: [Link { local: recv_addr, remote: recv_addr,