Skip to content

Commit

Permalink
Fixes & tests
Browse files Browse the repository at this point in the history
  • Loading branch information
larseggert committed Sep 16, 2024
1 parent 272c816 commit 502a04a
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 3 deletions.
2 changes: 1 addition & 1 deletion neqo-transport/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ impl Connection {
|| Duration::from_millis(0),
|p| {
let rtt = p.borrow().rtt().estimate();
if p.borrow().rtt().first_sample_time().is_none() {
if self.stats.borrow().frame_rx.ack == 0 {
// When we have no actual RTT sample, do not encode a guestimated RTT larger
// than the default initial RTT. (The guess can be very large under lossy
// conditions.)
Expand Down
124 changes: 122 additions & 2 deletions neqo-transport/src/connection/tests/resumption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@

use std::{cell::RefCell, mem, rc::Rc, time::Duration};

use test_fixture::{assertions, now};
use neqo_common::{Datagram, Decoder, Role};
use neqo_crypto::AuthenticationStatus;
use test_fixture::{
assertions,
header_protection::{
apply_header_protection, decode_initial_header, initial_aead_and_hp,
remove_header_protection,
},
now, split_datagram,
};

use super::{
connect, connect_with_rtt, default_client, default_server, exchange_ticket, get_tokens,
new_client, resumed_server, send_something, AT_LEAST_PTO,
};
use crate::{
addr_valid::{AddressValidation, ValidateAddress},
ConnectionParameters, Error, Version,
frame::FRAME_TYPE_PADDING,
rtt::INITIAL_RTT,
ConnectionParameters, Error, State, Version, MIN_INITIAL_PACKET_SIZE,
};

#[test]
Expand Down Expand Up @@ -75,6 +86,115 @@ fn remember_smoothed_rtt() {
);
}

fn ticket_rtt(rtt: Duration) -> Duration {
// A simple ACK_ECN frame for a single packet with packet number 0 with a single ECT(0) mark.
const ACK_FRAME_1: &[u8] = &[0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00];

let mut client = new_client(
ConnectionParameters::default().versions(Version::Version1, vec![Version::Version1]),
);
let mut server = default_server();
let mut now = now();

let client_initial = client.process_output(now);
let (_, client_dcid, _, _) =
decode_initial_header(client_initial.as_dgram_ref().unwrap(), Role::Client).unwrap();
let client_dcid = client_dcid.to_owned();

now += rtt / 2;
let server_packet = server.process(client_initial.as_dgram_ref(), now).dgram();
let (server_initial, server_hs) = split_datagram(server_packet.as_ref().unwrap());
let (protected_header, _, _, payload) =
decode_initial_header(&server_initial, Role::Server).unwrap();

// Now decrypt the packet.
let (aead, hp) = initial_aead_and_hp(&client_dcid, Role::Server);
let (header, pn) = remove_header_protection(&hp, protected_header, payload);
assert_eq!(pn, 0);
let pn_len = header.len() - protected_header.len();
let mut buf = vec![0; payload.len()];
let mut plaintext = aead
.decrypt(pn, &header, &payload[pn_len..], &mut buf)
.unwrap()
.to_owned();

// Now we need to find the frames. Make some really strong assumptions.
let mut dec = Decoder::new(&plaintext[..]);
assert_eq!(dec.decode(ACK_FRAME_1.len()), Some(ACK_FRAME_1));
assert_eq!(dec.decode_varint(), Some(0x06)); // CRYPTO
assert_eq!(dec.decode_varint(), Some(0x00)); // offset
dec.skip_vvec(); // Skip over the payload.

// Replace the ACK frame with PADDING.
plaintext[..ACK_FRAME_1.len()].fill(FRAME_TYPE_PADDING.try_into().unwrap());

// And rebuild a packet.
let mut packet = header.clone();
packet.resize(MIN_INITIAL_PACKET_SIZE, 0);
aead.encrypt(pn, &header, &plaintext, &mut packet[header.len()..])
.unwrap();
apply_header_protection(&hp, &mut packet, protected_header.len()..header.len());
let si = Datagram::new(
server_initial.source(),
server_initial.destination(),
server_initial.tos(),
packet,
);

// Now a connection can be made successfully.
now += rtt / 2;
client.process_input(&si, now);
client.process_input(&server_hs.unwrap(), now);
client.authenticated(AuthenticationStatus::Ok, now);
let finished = client.process_output(now);

assert_eq!(*client.state(), State::Connected);

now += rtt / 2;
_ = server.process(finished.as_dgram_ref(), now);
assert_eq!(*server.state(), State::Confirmed);

// Don't deliver the server's handshake finished, it has ACKs.
// Now get a ticket.
let validation = AddressValidation::new(now, ValidateAddress::NoToken).unwrap();
let validation = Rc::new(RefCell::new(validation));
server.set_validation(&validation);
send_something(&mut server, now);
server.send_ticket(now, &[]).expect("can send ticket");
let ticket = server.process_output(now).dgram();
assert!(ticket.is_some());
now += rtt / 2;
client.process_input(&ticket.unwrap(), now);
let token = get_tokens(&mut client).pop().unwrap();

// And connect again.
let mut client = default_client();
client.enable_resumption(now, token).unwrap();
let ticket_rtt = client.paths.rtt();
let mut server = resumed_server(&client);

connect_with_rtt(&mut client, &mut server, now, Duration::from_millis(50));
assert_eq!(
client.paths.rtt(),
Duration::from_millis(50),
"previous RTT should be completely erased"
);
ticket_rtt
}

#[test]
fn ticket_rtt_less_than_default() {
assert_eq!(
ticket_rtt(Duration::from_millis(10)),
Duration::from_millis(10)
);
}

#[test]
fn ticket_rtt_larger_than_default() {
assert_eq!(ticket_rtt(Duration::from_millis(500)), INITIAL_RTT);
}

/// Check that a resumed connection uses a token on Initial packets.
#[test]
fn address_validation_token_resume() {
Expand Down

0 comments on commit 502a04a

Please sign in to comment.