diff --git a/rust/connlib/snownet/src/allocation.rs b/rust/connlib/snownet/src/allocation.rs index 15342a065..e36018d01 100644 --- a/rust/connlib/snownet/src/allocation.rs +++ b/rust/connlib/snownet/src/allocation.rs @@ -851,6 +851,8 @@ impl Allocation { return; }; + tracing::debug!(number = %channel, "Binding new channel"); + self.authenticate_and_queue( make_channel_bind_request(peer, channel, self.software.clone()), None, @@ -867,10 +869,15 @@ impl Allocation { let active_socket = self.active_socket?.addr; let payload_length = buffer.len() - 4; - let channel_number = match self.channel_bindings.connected_channel_to_peer(peer, now) { + let connected_channel_to_peer = self.channel_bindings.connected_channel_to_peer(peer, now); + let inflight_channel_to_peer = self.channel_bindings.inflight_channel_to_peer(peer, now); + + // We use connected and in-flight channels in order to optimistically send data. + // Chances are, by the time the channel data message arrives, the channel will have been bound already. + // Whether or not we drop the packet here or on the relay if happened to not be bound does not matter. + let channel_number = match connected_channel_to_peer.or(inflight_channel_to_peer) { Some(cn) => cn, None => { - tracing::debug!(%peer, %active_socket, "No channel to peer, binding new one"); self.bind_channel(peer, now); return None; @@ -1498,6 +1505,13 @@ impl ChannelBindings { .map(|(n, _)| *n) } + fn inflight_channel_to_peer(&self, peer: SocketAddr, now: Instant) -> Option { + self.inner + .iter() + .find(|(_, c)| c.inflight_to_peer(peer, now)) + .map(|(n, _)| *n) + } + fn bound_channel_to_peer(&self, peer: SocketAddr, now: Instant) -> Option { self.inner .iter() @@ -1553,6 +1567,13 @@ impl Channel { self.peer == peer && self.age(now) < Self::CHANNEL_LIFETIME && self.bound } + /// Check if this channel is to-be-bound to the given peer. + /// + /// In case the channel is older than its lifetime (10 minutes), this returns false because the relay will have de-allocated the channel. + fn inflight_to_peer(&self, peer: SocketAddr, now: Instant) -> bool { + self.peer == peer && self.age(now) < Self::CHANNEL_LIFETIME + } + /// Check if this channel is bound to the given peer. fn bound_to_peer(&self, peer: SocketAddr, now: Instant) -> bool { self.peer == peer @@ -1934,12 +1955,25 @@ mod tests { } #[test] - fn does_not_relay_to_with_unbound_channel() { + fn relays_to_inflight_channel() { let mut allocation = Allocation::for_test_ip4(Instant::now()) .with_binding_response(PEER1, Instant::now()) .with_allocate_response(&[RELAY_ADDR_IP4], Instant::now()); allocation.bind_channel(PEER2_IP4, Instant::now()); + let mut buffer = channel_data_packet_buffer(b"foobar"); + let encode_ok = + allocation.encode_channel_data_header(PEER2_IP4, &mut buffer, Instant::now()); + + assert!(encode_ok.is_some()) + } + + #[test] + fn does_not_relay_to_with_unbound_channel() { + let mut allocation = Allocation::for_test_ip4(Instant::now()) + .with_binding_response(PEER1, Instant::now()) + .with_allocate_response(&[RELAY_ADDR_IP4], Instant::now()); + let mut buffer = channel_data_packet_buffer(b"foobar"); let encode_ok = allocation.encode_channel_data_header(PEER2_IP4, &mut buffer, Instant::now());