mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-07 13:03:45 +02:00
added comments and reduced path requirements
This commit is contained in:
parent
adcf553f18
commit
1925d0f98e
2 changed files with 11 additions and 7 deletions
|
@ -71,7 +71,7 @@ pub trait ApplicationLayer: Sized {
|
||||||
///
|
///
|
||||||
/// A physical path could be an IP address or IP plus device in the case of UDP, a socket in the
|
/// A physical path could be an IP address or IP plus device in the case of UDP, a socket in the
|
||||||
/// case of TCP, etc.
|
/// case of TCP, etc.
|
||||||
type PhysicalPath: PartialEq + Eq + Hash + Clone;
|
type PhysicalPath: Hash;
|
||||||
|
|
||||||
/// Get a reference to this host's static public key blob.
|
/// Get a reference to this host's static public key blob.
|
||||||
///
|
///
|
||||||
|
|
|
@ -40,7 +40,7 @@ const GCM_CIPHER_POOL_SIZE: usize = 4;
|
||||||
/// defragment incoming packets that are not yet associated with a session.
|
/// defragment incoming packets that are not yet associated with a session.
|
||||||
pub struct Context<Application: ApplicationLayer> {
|
pub struct Context<Application: ApplicationLayer> {
|
||||||
default_physical_mtu: AtomicUsize,
|
default_physical_mtu: AtomicUsize,
|
||||||
defrag_hasher: RandomState,
|
defrag_salt: RandomState,
|
||||||
defrag: [Mutex<Fragged<Application::IncomingPacketBuffer, MAX_NOISE_HANDSHAKE_FRAGMENTS>>; MAX_INCOMPLETE_SESSION_QUEUE_SIZE],
|
defrag: [Mutex<Fragged<Application::IncomingPacketBuffer, MAX_NOISE_HANDSHAKE_FRAGMENTS>>; MAX_INCOMPLETE_SESSION_QUEUE_SIZE],
|
||||||
sessions: RwLock<SessionsById<Application>>,
|
sessions: RwLock<SessionsById<Application>>,
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
pub fn new(default_physical_mtu: usize) -> Self {
|
pub fn new(default_physical_mtu: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
default_physical_mtu: AtomicUsize::new(default_physical_mtu),
|
default_physical_mtu: AtomicUsize::new(default_physical_mtu),
|
||||||
defrag_hasher: RandomState::new(),
|
defrag_salt: RandomState::new(),
|
||||||
defrag: std::array::from_fn(|_| Mutex::new(Fragged::new())),
|
defrag: std::array::from_fn(|_| Mutex::new(Fragged::new())),
|
||||||
sessions: RwLock::new(SessionsById {
|
sessions: RwLock::new(SessionsById {
|
||||||
active: HashMap::with_capacity(64),
|
active: HashMap::with_capacity(64),
|
||||||
|
@ -527,7 +527,9 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
|
|
||||||
let assembled;
|
let assembled;
|
||||||
let incoming_packet = if fragment_count > 1 {
|
let incoming_packet = if fragment_count > 1 {
|
||||||
let mut hasher = self.defrag_hasher.build_hasher();
|
// Using just incoming_counter unhashed would be good DOS resistant,
|
||||||
|
// but why not make it harder by mixing in a random value and the physical path in as well.
|
||||||
|
let mut hasher = self.defrag_salt.build_hasher();
|
||||||
source.hash(&mut hasher);
|
source.hash(&mut hasher);
|
||||||
hasher.write_u64(incoming_counter);
|
hasher.write_u64(incoming_counter);
|
||||||
let hashed_counter = hasher.finish();
|
let hashed_counter = hasher.finish();
|
||||||
|
@ -535,10 +537,12 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
let idx1 = (hashed_counter as usize)/MAX_INCOMPLETE_SESSION_QUEUE_SIZE%MAX_INCOMPLETE_SESSION_QUEUE_SIZE;
|
let idx1 = (hashed_counter as usize)/MAX_INCOMPLETE_SESSION_QUEUE_SIZE%MAX_INCOMPLETE_SESSION_QUEUE_SIZE;
|
||||||
|
|
||||||
// Open hash lookup of just 2 slots.
|
// Open hash lookup of just 2 slots.
|
||||||
// By only checking 2 slots we avoid an expensive hash table lookup while also minimizing the chance that 2 offers collide.
|
// By only checking 2 slots we avoid a full table lookup while also minimizing the chance that 2 offers collide.
|
||||||
// To DOS, an adversary would either need to volumetrically spam the defrag table to keep all slots full
|
// To DOS, an adversary would either need to volumetrically spam the defrag table to keep all slots full
|
||||||
// or replay Alice's packet header from a spoofed physical path before Alice's packet is fully assembled.
|
// or replay Alice's packet header from a spoofed physical path before Alice's packet is fully assembled.
|
||||||
// Since Alice's packet header has a randomly generated counter value replaying it in time is very hard.
|
// Volumetric spam is quite difficult since without the `defrag_salt: RandomState` value an adversary
|
||||||
|
// cannot control which slots their fragments index to. And since Alice's packet header has a randomly
|
||||||
|
// generated counter value replaying it in time requires extreme amounts of network control.
|
||||||
let mut slot0 = self.defrag[idx0].lock().unwrap();
|
let mut slot0 = self.defrag[idx0].lock().unwrap();
|
||||||
if slot0.counter() == hashed_counter {
|
if slot0.counter() == hashed_counter {
|
||||||
assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
||||||
|
@ -547,7 +551,7 @@ impl<Application: ApplicationLayer> Context<Application> {
|
||||||
if slot1.counter() == hashed_counter || slot1.counter() == 0 {
|
if slot1.counter() == hashed_counter || slot1.counter() == 0 {
|
||||||
assembled = slot1.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
assembled = slot1.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
||||||
} else {
|
} else {
|
||||||
// Both slots are full so kick out the unassembled packet in slot0 to make more room.
|
// slot1 is full so kick out whatever is in slot0 to make more room.
|
||||||
assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
assembled = slot0.assemble(hashed_counter, incoming_physical_packet_buf, fragment_no, fragment_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue