Skip to content

Commit

Permalink
Merge pull request #1739 from private-octopus/test_initial_pto
Browse files Browse the repository at this point in the history
Add test of initial PTO for retransmissions.
  • Loading branch information
huitema authored Aug 24, 2024
2 parents 9b9a8a4 + bb1143e commit 858ffd7
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 4 deletions.
7 changes: 7 additions & 0 deletions UnitTest1/unittest1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,13 @@ namespace UnitTest1
Assert::AreEqual(ret, 0);
}

TEST_METHOD(initial_pto)
{
int ret = initial_pto_test();

Assert::AreEqual(ret, 0);
}

TEST_METHOD(ready_to_send)
{
int ret = ready_to_send_test();
Expand Down
2 changes: 2 additions & 0 deletions picoquic/picoquic_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,8 @@ size_t picoquic_get_checksum_length(picoquic_cnx_t* cnx, picoquic_epoch_enum is_

void picoquic_protect_packet_header(uint8_t* send_buffer, size_t pn_offset, uint8_t first_mask, void* pn_enc);

size_t picoquic_protect_packet(picoquic_cnx_t* cnx, picoquic_packet_type_enum ptype, uint8_t* bytes, uint64_t sequence_number, size_t length, size_t header_length, uint8_t* send_buffer, size_t send_buffer_max, void* aead_context, void* pn_enc, picoquic_path_t* path_x, uint64_t current_time);

uint64_t picoquic_get_packet_number64(uint64_t highest, uint64_t mask, uint32_t pn);

void picoquic_log_pn_dec_trial(picoquic_cnx_t* cnx); /* For debugging potential PN_ENC corruption */
Expand Down
3 changes: 2 additions & 1 deletion picoquic/sender.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "picoquic_internal.h"
/*
* Author: Christian Huitema
* Copyright (c) 2017, Private Octopus, Inc.
Expand Down Expand Up @@ -828,7 +829,7 @@ void picoquic_protect_packet_header(uint8_t * send_buffer, size_t pn_offset, uin
}
}

static size_t picoquic_protect_packet(picoquic_cnx_t* cnx,
size_t picoquic_protect_packet(picoquic_cnx_t* cnx,
picoquic_packet_type_enum ptype,
uint8_t * bytes,
uint64_t sequence_number,
Expand Down
1 change: 1 addition & 0 deletions picoquic_t/picoquic_t.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ static const picoquic_test_def_t test_table[] = {
{ "reset_need_max", reset_need_max_test },
{ "reset_need_reset", reset_need_reset_test },
{ "reset_need_stop", reset_need_stop_test },
{ "initial_pto", initial_pto_test },
{ "ready_to_send", ready_to_send_test },
{ "ready_to_skip", ready_to_skip_test },
{ "ready_to_zfin", ready_to_zfin_test },
Expand Down
165 changes: 165 additions & 0 deletions picoquictest/edge_cases.c
Original file line number Diff line number Diff line change
Expand Up @@ -1231,4 +1231,169 @@ int reset_need_reset_test()
int reset_need_stop_test()
{
return reset_repeat_test_one(reset_need_stop_sending);
}

/*
* Initial PTO test:
* Test the scenario in which:
* - the client sends an initial message
* - the server sends an ACK
* - no further packets from the server, either because they are lost
* or because the server is hitting the amplification limit.
* Verify that the client sends a message after a PTO commensurate
* with the RTT.
*/

int initial_pto_prepare(picoquic_test_tls_api_ctx_t* test_ctx, uint64_t* p_simulated_time,
size_t *length)
{
int ret = 0;
uint8_t buf[PICOQUIC_MAX_PACKET_SIZE];
struct sockaddr_storage addr_to;
struct sockaddr_storage addr_from;

ret = picoquic_prepare_packet(test_ctx->cnx_client, *p_simulated_time,
buf, PICOQUIC_MAX_PACKET_SIZE, length,
&addr_to, &addr_from, NULL);

if (ret == 0) {
/* Submit initial packet to server context, so we get the
* crypto context created */
ret = picoquic_incoming_packet_ex(test_ctx->qserver, buf, *length,
(struct sockaddr*)&addr_from, (struct sockaddr*)&addr_to, 0, 0,
&test_ctx->cnx_server, *p_simulated_time);
if (ret == 0 && test_ctx->cnx_server == NULL) {
ret = -1;
}
}
return ret;
}

int initial_pto_wait(picoquic_test_tls_api_ctx_t* test_ctx, uint64_t* p_simulated_time,
uint64_t max_wait, size_t* length_sent)
{
int ret = 0;
size_t length = 0;
*length_sent = 0;
while (ret == 0 && *p_simulated_time < max_wait) {
uint64_t client_departure = test_ctx->cnx_client->next_wake_time;

if (client_departure >= max_wait) {
*p_simulated_time = max_wait;
break;
}
else {
if (*p_simulated_time < client_departure) {
*p_simulated_time = client_departure;
}
ret = initial_pto_prepare(test_ctx, p_simulated_time, &length);
if (length > 0 || test_ctx->cnx_client->cnx_state >= picoquic_state_handshake_failure) {
*length_sent = length;
break;
}
}
}
return ret;
}

int initial_pto_ack(picoquic_test_tls_api_ctx_t* test_ctx, uint64_t* p_simulated_time)
{
int ret = 0;
uint8_t buf[PICOQUIC_INITIAL_MTU_IPV6];
uint8_t send_buffer[PICOQUIC_MAX_PACKET_SIZE];
picoquic_epoch_enum epoch = picoquic_epoch_initial;
picoquic_packet_type_enum packet_type = picoquic_packet_initial;
picoquic_packet_context_enum pc = picoquic_packet_context_initial;
size_t checksum_overhead = 16;
uint8_t * bytes_max = buf + PICOQUIC_INITIAL_MTU_IPV6 - checksum_overhead;
uint8_t* bytes_next = buf;
uint64_t sequence_number = 0;
size_t length = 0;
size_t header_length = 0;
int more_data = 0;
size_t send_length = 0;
/* Format the header */
header_length = picoquic_predict_packet_header_length(test_ctx->cnx_server, packet_type,
&test_ctx->cnx_server->pkt_ctx[pc]);
bytes_next += header_length;
/* add ack, function of client sequence number */
bytes_next = picoquic_format_ack_frame(test_ctx->cnx_server, bytes_next, bytes_max, &more_data,
*p_simulated_time, pc, 0);
/* add padding to minimum length */
length = picoquic_pad_to_target_length(buf, bytes_next - buf,
PICOQUIC_INITIAL_MTU_IPV6 - checksum_overhead);

/* Finalize */
send_length = picoquic_protect_packet(test_ctx->cnx_server, packet_type, buf, 0,
length, header_length,
send_buffer, PICOQUIC_MAX_PACKET_SIZE,
test_ctx->cnx_server->crypto_context[picoquic_epoch_initial].aead_encrypt,
test_ctx->cnx_server->crypto_context[picoquic_epoch_initial].pn_enc,
test_ctx->cnx_server->path[0], *p_simulated_time);
/* Submit to client. */
if (send_length == 0) {
ret = -1;
}
else {
picoquic_cnx_t* last_cnx = NULL;
ret = picoquic_incoming_packet_ex(test_ctx->qclient, send_buffer, send_length,
(struct sockaddr*)&test_ctx->server_addr, (struct sockaddr*)&test_ctx->client_addr, 0, 0,
&last_cnx, *p_simulated_time);
}
return ret;
}

int initial_pto_test()
{
int ret = 0;
picoquic_test_tls_api_ctx_t *test_ctx = NULL;
size_t length = 0;
uint64_t simulated_time = 0;
uint64_t simulated_rtt = 20000;
uint64_t simulated_pto = 4*simulated_rtt;
picoquic_connection_id_t initial_cid = { { 0x94, 0x01, 0x41, 0, 0, 0, 0, 0}, 8 };

/* Create a client. */
ret = tls_api_init_ctx_ex(&test_ctx, PICOQUIC_INTERNAL_TEST_VERSION_1,
PICOQUIC_TEST_SNI, PICOQUIC_TEST_ALPN, &simulated_time, NULL, NULL, 0, 1, 0, &initial_cid);
if (ret != 0) {
DBG_PRINTF("Cannot initialize context, ret = 0x%x", ret);
}
else {
/* Set the binlog */
picoquic_set_binlog(test_ctx->qclient, ".");
/* start the client connection */
ret = picoquic_start_client_cnx(test_ctx->cnx_client);
}
/* Send the initial packet */
if (ret == 0) {
ret = initial_pto_prepare(test_ctx, &simulated_time, &length);
if (ret == 0 && length < 1200) {
length = -1;
}
}
/* get the initial message, wait until next client time >= time of ACK */
if (ret == 0 && simulated_time < 20000) {
ret = initial_pto_wait(test_ctx, &simulated_time, simulated_rtt, &length);
}
/* format an ACK packet, apply initial protection, submit ACK to client */
if (ret == 0) {
ret = initial_pto_ack(test_ctx, &simulated_time);
}
/* Wait until next client time >= expected response, or
* client is ready to send and does send. */
if (ret == 0 && simulated_time < simulated_pto) {
ret = initial_pto_wait(test_ctx, &simulated_time, simulated_pto, &length);
if (ret == 0 && length < 1200) {
/* Did not send the PTO */
ret = -1;
}
}
/* Clean up */
if (test_ctx != NULL) {
tls_api_delete_ctx(test_ctx);
test_ctx = NULL;
}

return ret;
}
1 change: 1 addition & 0 deletions picoquictest/picoquictest.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ int reset_extra_stop_test();
int reset_need_max_test();
int reset_need_reset_test();
int reset_need_stop_test();
int initial_pto_test();
int ready_to_send_test();
int ready_to_skip_test();
int ready_to_zero_test();
Expand Down
8 changes: 5 additions & 3 deletions picoquictest/tls_api_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -10165,9 +10165,11 @@ int ddos_amplification_test_one(int use_0rtt, int do_8k)

if (ret == 0) {
/* Prepare a first packet from the client to the server */
ret = picoquic_prepare_packet(test_ctx->cnx_client, simulated_time,
packet->bytes, PICOQUIC_MAX_PACKET_SIZE, &packet->length,
&packet->addr_to, &packet->addr_from, NULL);
{
ret = picoquic_prepare_packet(test_ctx->cnx_client, simulated_time,
packet->bytes, PICOQUIC_MAX_PACKET_SIZE, &packet->length,
&packet->addr_to, &packet->addr_from, NULL);
}

if (packet->length == 0) {
ret = PICOQUIC_ERROR_UNEXPECTED_ERROR;
Expand Down

0 comments on commit 858ffd7

Please sign in to comment.