From 772b70547d2854dc85b21db9f6337a3698e8f0a8 Mon Sep 17 00:00:00 2001 From: huitema <huitema@huitema.net> Date: Sat, 9 Jun 2018 21:32:44 -0700 Subject: [PATCH] Adding a stress test, and fixing issues discovered during stress. --- UnitTest1/unittest1.cpp | 7 + picoquic/frames.c | 2 - picoquic/picoquic.h | 2 + picoquic/quicctx.c | 7 + picoquic/sender.c | 12 +- picoquic_t/picoquic_t.c | 3 +- picoquictest/picoquictest.h | 1 + picoquictest/skip_frame_test.c | 3 - picoquictest/stresstest.c | 523 ++++++++++++++++++++++++++------- picoquictest/tls_api_test.c | 2 +- 10 files changed, 448 insertions(+), 114 deletions(-) diff --git a/UnitTest1/unittest1.cpp b/UnitTest1/unittest1.cpp index bb70d254..aefd5ce9 100644 --- a/UnitTest1/unittest1.cpp +++ b/UnitTest1/unittest1.cpp @@ -568,5 +568,12 @@ namespace UnitTest1 Assert::AreEqual(ret, 0); } + + TEST_METHOD(stress) + { + int ret = stress_test(); + + Assert::AreEqual(ret, 0); + } }; } diff --git a/picoquic/frames.c b/picoquic/frames.c index eaae2acf..7e8017d8 100644 --- a/picoquic/frames.c +++ b/picoquic/frames.c @@ -1478,8 +1478,6 @@ int picoquic_is_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time) if (cnx->highest_ack_sent + 2 <= cnx->first_sack_item.end_of_sack_range || cnx->highest_ack_time + cnx->ack_delay_local <= current_time) { ret = cnx->ack_needed; - } else if (cnx->ack_needed) { - ret = 0; } return ret; diff --git a/picoquic/picoquic.h b/picoquic/picoquic.h index b31f5e55..7c12ca6b 100644 --- a/picoquic/picoquic.h +++ b/picoquic/picoquic.h @@ -354,6 +354,8 @@ int picoquic_is_cnx_backlog_empty(picoquic_cnx_t* cnx); void picoquic_set_callback(picoquic_cnx_t* cnx, picoquic_stream_data_cb_fn callback_fn, void* callback_ctx); +void * picoquic_get_callback_context(picoquic_cnx_t* cnx); + /* Send extra frames */ int picoquic_queue_misc_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, size_t length); diff --git a/picoquic/quicctx.c b/picoquic/quicctx.c index a5827d9e..24fa4fad 100644 --- a/picoquic/quicctx.c +++ b/picoquic/quicctx.c @@ -832,6 +832,8 @@ int picoquic_start_client_cnx(picoquic_cnx_t * cnx) { int ret = picoquic_initialize_stream_zero(cnx); + picoquic_cnx_set_next_wake_time(cnx, picoquic_get_quic_time(cnx->quic)); + return ret; } @@ -999,6 +1001,11 @@ void picoquic_set_callback(picoquic_cnx_t* cnx, cnx->callback_ctx = callback_ctx; } +void * picoquic_get_callback_context(picoquic_cnx_t * cnx) +{ + return cnx->callback_ctx; +} + picoquic_misc_frame_header_t* picoquic_create_misc_frame(const uint8_t* bytes, size_t length) { uint8_t* misc_frame = (uint8_t*)malloc(sizeof(picoquic_misc_frame_header_t) + length); diff --git a/picoquic/sender.c b/picoquic/sender.c index 85e9c6e0..10f9ef0b 100644 --- a/picoquic/sender.c +++ b/picoquic/sender.c @@ -64,6 +64,7 @@ int picoquic_add_to_stream(picoquic_cnx_t* cnx, uint64_t stream_id, if ((stream_id & 1) != parity) { ret = PICOQUIC_ERROR_INVALID_STREAM_ID; } else { +#if 0 if ((stream_id & 2) == 0) { if (stream_id > cnx->max_stream_id_bidir_remote) { ret = PICOQUIC_ERROR_INVALID_STREAM_ID; @@ -75,6 +76,7 @@ int picoquic_add_to_stream(picoquic_cnx_t* cnx, uint64_t stream_id, ret = PICOQUIC_ERROR_INVALID_STREAM_ID; } } +#endif } if (ret == 0) { @@ -319,8 +321,8 @@ uint32_t picoquic_create_packet_header_12( /* Create a short packet -- using 32 bit sequence numbers for now */ uint8_t K = (packet_type == picoquic_packet_1rtt_protected_phi0) ? 0 : 0x40; const uint8_t C = 0x30; - uint8_t spin_vec = (cnx->spin_vec ); - uint8_t spin_bit = (uint8_t)((cnx->current_spin) << 2); + uint8_t spin_vec = (uint8_t) (cnx->spin_vec ); + uint8_t spin_bit = (uint8_t)((cnx->current_spin) << 2); if (!cnx->spin_edge) spin_vec = 0; else { @@ -676,7 +678,7 @@ static int picoquic_retransmit_needed_by_packet(picoquic_cnx_t* cnx, uint64_t retransmit_timer = (cnx->nb_retransmit == 0) ? cnx->path[0]->retransmit_timer : (1000000ull << (cnx->nb_retransmit - 1)); - if ((uint64_t)time_out <= retransmit_timer) { + if ((uint64_t)time_out < retransmit_timer) { /* Do not retransmit if the timer has not yet elapsed */ should_retransmit = 0; } else { @@ -1277,7 +1279,7 @@ int picoquic_prepare_packet_client_init(picoquic_cnx_t* cnx, picoquic_path_t * p /* document the send time & overhead */ packet->send_time = current_time; packet->checksum_overhead = checksum_overhead; - } else if (ret == 0 && is_cleartext_mode && stream == NULL && cnx->first_misc_frame == NULL) { + } else if (ret == 0 && is_cleartext_mode && stream == NULL && cnx->first_misc_frame == NULL && cnx->ack_needed == 0) { /* when in a clear text mode, only send packets if there is * actually something to send, or resend */ @@ -1567,7 +1569,7 @@ int picoquic_prepare_packet_closing(picoquic_cnx_t* cnx, picoquic_path_t * path_ if (current_time >= exit_time) { cnx->cnx_state = picoquic_state_disconnected; - } else if (current_time > cnx->next_wake_time) { + } else if (current_time >= cnx->next_wake_time) { uint64_t delta_t = path_x->rtt_min; if (delta_t * 2 < path_x->retransmit_timer) { delta_t = path_x->retransmit_timer / 2; diff --git a/picoquic_t/picoquic_t.c b/picoquic_t/picoquic_t.c index 42d1e5ca..b5ccb56e 100644 --- a/picoquic_t/picoquic_t.c +++ b/picoquic_t/picoquic_t.c @@ -110,7 +110,8 @@ static const picoquic_test_def_t test_table[] = { { "pn_vector", cleartext_pn_vector_test }, { "zero_rtt_spurious", zero_rtt_spurious_test }, { "zero_rtt_retry", zero_rtt_retry_test }, - { "parse_frames", parse_frame_test } + { "parse_frames", parse_frame_test }, + { "stress", stress_test } }; static size_t const nb_tests = sizeof(test_table) / sizeof(picoquic_test_def_t); diff --git a/picoquictest/picoquictest.h b/picoquictest/picoquictest.h index 66857b22..9efc404b 100644 --- a/picoquictest/picoquictest.h +++ b/picoquictest/picoquictest.h @@ -105,6 +105,7 @@ int cleartext_pn_vector_test(); int zero_rtt_spurious_test(); int zero_rtt_retry_test(); int parse_frame_test(); +int stress_test(); #ifdef __cplusplus } diff --git a/picoquictest/skip_frame_test.c b/picoquictest/skip_frame_test.c index eb09f75f..08ffe3da 100644 --- a/picoquictest/skip_frame_test.c +++ b/picoquictest/skip_frame_test.c @@ -332,11 +332,8 @@ int parse_frame_test() { int ret = 0; uint8_t buffer[PICOQUIC_MAX_PACKET_SIZE]; - uint8_t fuzz_buffer[PICOQUIC_MAX_PACKET_SIZE]; const uint8_t extra_bytes[4] = { 0, 0, 0, 0 }; uint64_t random_context = 0xBABED011; - int fuzz_count = 0; - int fuzz_fail = 0; uint64_t simulated_time = 0; struct sockaddr_in saddr; picoquic_quic_t * qclient = picoquic_create(8, NULL, NULL, NULL, NULL, diff --git a/picoquictest/stresstest.c b/picoquictest/stresstest.c index 47c0dbf5..63e02670 100644 --- a/picoquictest/stresstest.c +++ b/picoquictest/stresstest.c @@ -31,6 +31,60 @@ #include <string.h> #include <openssl/pem.h> +#define PICOQUIC_MAX_STRESS_CLIENTS 256 +#define PICOQUIC_STRESS_MAX_NUMBER_TRACKED_STREAMS 16 +#define PICOQUIC_STRESS_MINIMAL_QUERY_SIZE 127 +#define PICOQUIC_STRESS_DEFAULT_RESPONSE_SIZE 257 +#define PICOQUIC_STRESS_RESPONSE_LENGTH_MAX 1000000 +#define PICOQUIC_STRESS_MESSAGE_BUFFER_SIZE 0x10000 +#define PICOQUIC_STRESS_MAX_CLIENT_STREAMS 16 + +#define PICOQUIC_TEST_SNI "picoquic.test" +#define PICOQUIC_TEST_ALPN "picoquic-test" + +uint64_t picoquic_stress_test_duration = 120000000; /* Default to 2 minutes */ +size_t picoquic_stress_nb_clients = 1; /* Default to 4 clients */ +uint64_t picoquic_stress_max_bidir = 1 * 4; /* Default to 8 streams max per connection */ +size_t picoquic_stress_max_open_streams = 1; /* Default to 4 simultaneous streams max per connection */ + +typedef struct st_picoquic_stress_server_callback_ctx_t { + // picoquic_first_server_stream_ctx_t* first_stream; + uint8_t buffer[PICOQUIC_STRESS_MESSAGE_BUFFER_SIZE]; + size_t data_received_on_stream[PICOQUIC_STRESS_MAX_NUMBER_TRACKED_STREAMS]; + uint32_t data_sum_of_stream[PICOQUIC_STRESS_MAX_NUMBER_TRACKED_STREAMS]; +} picoquic_stress_server_callback_ctx_t; + +typedef struct st_picoquic_stress_client_callback_ctx_t { + uint64_t test_id; + uint64_t max_bidir; + uint64_t next_bidir; + size_t max_open_streams; + size_t nb_open_streams; + uint64_t stream_id[PICOQUIC_STRESS_MAX_CLIENT_STREAMS]; + uint32_t nb_client_streams; + uint64_t last_interaction_time; + int progress_observed; +} picoquic_stress_client_callback_ctx_t; + +typedef struct st_picoquic_stress_client_t { + picoquic_quic_t* qclient; + struct sockaddr_in client_addr; + char ticket_file_name[32]; + picoquictest_sim_link_t* c_to_s_link; + picoquictest_sim_link_t* s_to_c_link; +} picoquic_stress_client_t; + +typedef struct st_picoquic_stress_ctx_t { + picoquic_quic_t* qserver; + struct sockaddr_in server_addr; + uint64_t simulated_time; + int sum_data_received_at_server; + int sum_data_sent_at_server; + int sum_connections; + int nb_clients; + picoquic_stress_client_t * c_ctx[PICOQUIC_MAX_STRESS_CLIENTS]; +} picoquic_stress_ctx_t; + /* * Portable abort call, should work on Linux and Windows. * We deliberately do not call the ASSERT macros, becuse these @@ -55,19 +109,6 @@ static void stress_debug_break() * TODO: add debug_break on error condition. */ -#define STRESS_MAX_NUMBER_TRACKED_STREAMS 16 -#define STRESS_MINIMAL_QUERY_SIZE 127 -#define STRESS_DEFAULT_RESPONSE_SIZE 257 -#define STRESS_RESPONSE_LENGTH_MAX 1000000 -#define STRESS_MESSAGE_BUFFER_SIZE 0x10000 - -typedef struct st_picoquic_stress_server_callback_ctx_t { - // picoquic_first_server_stream_ctx_t* first_stream; - uint8_t buffer[STRESS_MESSAGE_BUFFER_SIZE]; - size_t data_received_on_stream[STRESS_MAX_NUMBER_TRACKED_STREAMS]; - uint32_t data_sum_of_stream[STRESS_MAX_NUMBER_TRACKED_STREAMS]; -} picoquic_stress_server_callback_ctx_t; - static void stress_server_callback(picoquic_cnx_t* cnx, uint64_t stream_id, uint8_t* bytes, size_t length, picoquic_call_back_event_t fin_or_event, void* callback_ctx) @@ -123,12 +164,12 @@ static void stress_server_callback(picoquic_cnx_t* cnx, size_t response_length = 0; - if (bidir_id < STRESS_MAX_NUMBER_TRACKED_STREAMS) { + if (bidir_id < PICOQUIC_STRESS_MAX_NUMBER_TRACKED_STREAMS) { size_t received = ctx->data_received_on_stream[bidir_id] + length; - if (ctx->data_received_on_stream[bidir_id] < STRESS_MINIMAL_QUERY_SIZE) { + if (ctx->data_received_on_stream[bidir_id] < PICOQUIC_STRESS_MINIMAL_QUERY_SIZE) { int processed = length; - if (received >= STRESS_MINIMAL_QUERY_SIZE) { - processed = received - STRESS_MINIMAL_QUERY_SIZE; + if (received >= PICOQUIC_STRESS_MINIMAL_QUERY_SIZE) { + processed = received - PICOQUIC_STRESS_MINIMAL_QUERY_SIZE; } for (int i = 0; i < processed; i++) { @@ -136,8 +177,8 @@ static void stress_server_callback(picoquic_cnx_t* cnx, ctx->data_sum_of_stream[bidir_id] * 101 + bytes[i]; } - if (received >= STRESS_MINIMAL_QUERY_SIZE) { - response_length = ctx->data_sum_of_stream[bidir_id] % STRESS_RESPONSE_LENGTH_MAX; + if (received >= PICOQUIC_STRESS_MINIMAL_QUERY_SIZE) { + response_length = ctx->data_sum_of_stream[bidir_id] % PICOQUIC_STRESS_RESPONSE_LENGTH_MAX; } } } @@ -145,22 +186,22 @@ static void stress_server_callback(picoquic_cnx_t* cnx, /* for all streams above the limit, or all streams with short queries,just send a fixed size answer, * after receiving all the client data */ if (fin_or_event == picoquic_callback_stream_fin && - (bidir_id >= STRESS_MAX_NUMBER_TRACKED_STREAMS || - ctx->data_received_on_stream[bidir_id] < STRESS_MINIMAL_QUERY_SIZE)) { + (bidir_id >= PICOQUIC_STRESS_MAX_NUMBER_TRACKED_STREAMS || + ctx->data_received_on_stream[bidir_id] < PICOQUIC_STRESS_MINIMAL_QUERY_SIZE)) { - response_length = STRESS_DEFAULT_RESPONSE_SIZE; + response_length = PICOQUIC_STRESS_DEFAULT_RESPONSE_SIZE; } if (response_length > 0) { /* Push data on the stream */ - while (response_length > STRESS_MESSAGE_BUFFER_SIZE) { + while (response_length > PICOQUIC_STRESS_MESSAGE_BUFFER_SIZE) { if ( (ret = picoquic_add_to_stream(cnx, stream_id, ctx->buffer, - STRESS_MESSAGE_BUFFER_SIZE, 0)) != 0) { + PICOQUIC_STRESS_MESSAGE_BUFFER_SIZE, 0)) != 0) { stress_debug_break(); } - response_length -= STRESS_MESSAGE_BUFFER_SIZE; + response_length -= PICOQUIC_STRESS_MESSAGE_BUFFER_SIZE; } if ((ret = picoquic_add_to_stream(cnx, stream_id, ctx->buffer, response_length, 1)) != 0) { @@ -189,19 +230,6 @@ static void stress_server_callback(picoquic_cnx_t* cnx, * zero share start. */ -#define STRESS_MAX_CLIENT_STREAMS 16 - -typedef struct st_picoquic_stress_client_callback_ctx_t { - uint64_t test_id; - uint64_t max_bidir; - uint64_t next_bidir; - size_t max_open_streams; - size_t nb_open_streams; - uint64_t stream_id[STRESS_MAX_CLIENT_STREAMS]; - uint32_t nb_client_streams; - uint64_t last_interaction_time; - int progress_observed; -} picoquic_stress_client_callback_ctx_t; static void stress_client_start_streams(picoquic_cnx_t* cnx, picoquic_stress_client_callback_ctx_t* ctx) @@ -305,7 +333,7 @@ static void stress_client_callback(picoquic_cnx_t* cnx, } else { /* Initialize the next bidir stream */ - stress_client_start_streams(ctx, cnx); + stress_client_start_streams(cnx, ctx); } } } @@ -314,41 +342,54 @@ static void stress_client_callback(picoquic_cnx_t* cnx, /* that's it */ } +int stress_client_set_callback(picoquic_cnx_t* cnx) +{ + static uint64_t test_id = 0; + int ret = 0; + + if (picoquic_get_callback_context(cnx) != NULL) { + /* Duplicate init call. This is a bug */ + stress_debug_break(); + ret = -1; + } + else { + picoquic_stress_client_callback_ctx_t* ctx = + (picoquic_stress_client_callback_ctx_t*)malloc(sizeof(picoquic_stress_client_callback_ctx_t)); + if (ctx == NULL) { + stress_debug_break(); + ret = -1; + } + else { + memset(ctx, 0, sizeof(picoquic_stress_client_callback_ctx_t)); + ctx->test_id = test_id++; + ctx->max_bidir = picoquic_stress_max_bidir; + ctx->max_open_streams = picoquic_stress_max_open_streams; + ctx->next_bidir = 4; /* TODO: change to zero when cream/crack gets done */ + for (size_t i = 0; i < ctx->max_open_streams; i++) { + ctx->stream_id[i] = (uint64_t)((int64_t)-1); + } + picoquic_set_callback(cnx, stress_client_callback, ctx); + + stress_client_start_streams(cnx, ctx); + } + } + + return ret; +} + /* Orchestration of the simulation: one server, N simulation * links. On each link, there may be a new client added in * the future. Links have different delays, capacity, and * different client arrival rates. */ -#define PICOQUIC_MAX_STRESS_CLIENTS 256 - -typedef struct st_picoquic_stress_client_t { - picoquic_quic_t* qclient; - struct sockaddr_in client_addr; - picoquictest_sim_link_t* c_to_s_link; - picoquictest_sim_link_t* s_to_c_link; - int sum_data_received_at_client; -} picoquic_stress_client_t; - -typedef struct st_picoquic_stress_ctx_t { - picoquic_quic_t* qserver; - uint64_t simulated_time; - size_t nb_stress_client; - int sum_data_received_at_server; - int sum_data_sent_at_server; - int nb_clients; - picoquic_stress_client_t * c_ctx[PICOQUIC_MAX_STRESS_CLIENTS]; -} picoquic_stress_ctx_t; - /* * Message loop and related functions */ -void stress_set_ip_address_from_index(struct sockaddr_in * addr, int c_index) +static void stress_set_ip_address_from_index(struct sockaddr_in * addr, int c_index) { - int ret = 0; - - memset(&addr, 0, sizeof(struct sockaddr_in)); + memset(addr, 0, sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; #ifdef _WINDOWS addr->sin_addr.S_un.S_addr = (ULONG) c_index; @@ -358,9 +399,9 @@ void stress_set_ip_address_from_index(struct sockaddr_in * addr, int c_index) addr->sin_port = 4321; } -int stress_get_index_from_ip_address(struct sockaddr_in * addr) +static int stress_get_index_from_ip_address(struct sockaddr_in * addr) { - uint32_t c_index = -1; + uint32_t c_index; #ifdef _WINDOWS c_index = (int)addr->sin_addr.S_un.S_addr; #else @@ -370,43 +411,55 @@ int stress_get_index_from_ip_address(struct sockaddr_in * addr) } -int stress_submit_sp_packets(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, int c_index) +static int stress_submit_sp_packets(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, int c_index) { int ret = 0; picoquic_stateless_packet_t* sp = NULL; picoquictest_sim_link_t* target_link = NULL; - picoquictest_sim_packet_t* packet = picoquictest_sim_link_create_packet(); - if (packet == NULL) { - ret = -1; - } - else while ((sp = picoquic_dequeue_stateless_packet(q)) != NULL) { + while ((sp = picoquic_dequeue_stateless_packet(q)) != NULL) { if (sp->length > 0) { - memcpy(&packet->addr_from, &sp->addr_local, - (sp->addr_local.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); - memcpy(&packet->addr_to, &sp->addr_to, - (sp->addr_to.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); - memcpy(packet->bytes, sp->bytes, sp->length); - packet->length = sp->length; - - if (c_index > 0) - { - target_link = ctx->c_ctx[c_index]->c_to_s_link; + picoquictest_sim_packet_t* packet = picoquictest_sim_link_create_packet(); + + if (packet == NULL) { + stress_debug_break(); + ret = -1; + break; } else { - /* find target from address */ - int d_index = stress_get_index_from_ip_address((struct sockaddr_in *) &sp->addr_to); - - if (d_index < 0 || d_index >= ctx->nb_clients) { - ret = -1; + memcpy(&packet->addr_from, &sp->addr_local, + (sp->addr_local.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); + memcpy(&packet->addr_to, &sp->addr_to, + (sp->addr_to.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); + memcpy(packet->bytes, sp->bytes, sp->length); + packet->length = sp->length; + + if (c_index >= 0) + { + target_link = ctx->c_ctx[c_index]->c_to_s_link; } else { - target_link = ctx->c_ctx[c_index]->s_to_c_link; + /* find target from address */ + int d_index = stress_get_index_from_ip_address((struct sockaddr_in *) &sp->addr_to); + + if (d_index < 0 || d_index >= ctx->nb_clients) { + stress_debug_break(); + ret = -1; + } + else { + target_link = ctx->c_ctx[d_index]->s_to_c_link; + } } - } - if (target_link != NULL) { - picoquictest_sim_link_submit(target_link, packet, ctx->simulated_time); + if (target_link != NULL) { + picoquictest_sim_link_submit(target_link, packet, ctx->simulated_time); + } + else { + free(packet); + stress_debug_break(); + ret = -1; + break; + } } } picoquic_delete_stateless_packet(sp); @@ -415,7 +468,7 @@ int stress_submit_sp_packets(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, i return ret; } -int stress_handle_packet_arrival(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, picoquictest_sim_link_t* link) +static int stress_handle_packet_arrival(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, picoquictest_sim_link_t* link) { int ret = 0; /* dequeue packet from server to client and submit */ @@ -426,12 +479,15 @@ int stress_handle_packet_arrival(picoquic_stress_ctx_t * ctx, picoquic_quic_t * (struct sockaddr*)&packet->addr_from, (struct sockaddr*)&packet->addr_to, 0, ctx->simulated_time); + if (ret != 0){ + stress_debug_break(); + } } return ret; } -int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, int c_index) +static int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_quic_t * q, int c_index) { /* prepare packet and submit */ int ret = 0; @@ -447,7 +503,7 @@ int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_quic_t * memcpy(&packet->addr_from, &cnx->path[0]->dest_addr, sizeof(struct sockaddr_in)); memcpy(&packet->addr_to, &cnx->path[0]->peer_addr, sizeof(struct sockaddr_in)); - if (c_index > 0) + if (c_index >= 0) { target_link = ctx->c_ctx[c_index]->c_to_s_link; } @@ -456,23 +512,59 @@ int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_quic_t * int d_index = stress_get_index_from_ip_address((struct sockaddr_in *) &packet->addr_to); if (d_index < 0 || d_index >= ctx->nb_clients) { + stress_debug_break(); ret = -1; } else { - target_link = ctx->c_ctx[c_index]->s_to_c_link; + target_link = ctx->c_ctx[d_index]->s_to_c_link; } } - - picoquictest_sim_link_submit(target_link, packet, ctx->simulated_time); + if (target_link != NULL) { + picoquictest_sim_link_submit(target_link, packet, ctx->simulated_time); + } } else { free(p); + p = NULL; + free(packet); + packet = NULL; + + if (ret == PICOQUIC_ERROR_DISCONNECTED) { + if (c_index >= 0) { + /* Check that the client connection was properly terminated */ + picoquic_stress_client_callback_ctx_t* c_ctx = + (picoquic_stress_client_callback_ctx_t*)picoquic_get_callback_context(cnx); + ret = 0; + if (c_ctx != NULL) { + if (c_ctx->next_bidir <= c_ctx->max_bidir || + c_ctx->nb_open_streams != 0) { + stress_debug_break(); + ret = -1; + } + free(c_ctx); + picoquic_set_callback(cnx, NULL, NULL); + } + } + else { + ret = 0; + } + picoquic_delete_cnx(cnx); + if (c_index >= 0 && q->cnx_wake_first != NULL) { + stress_debug_break(); + ret = -1; + } + } + else if (ret != 0) { + stress_debug_break(); + } } - free(packet); } else { - ret = -1; + if (cnx != NULL) { + stress_debug_break(); + ret = -1; + } if (packet != NULL) { free(packet); } @@ -485,7 +577,38 @@ int stress_handle_packet_prepare(picoquic_stress_ctx_t * ctx, picoquic_quic_t * return ret; } -int stress_loop_poll_context(picoquic_stress_ctx_t * ctx, uint64_t next_time) { +static int stress_start_client_connection(picoquic_quic_t * qclient, picoquic_stress_ctx_t * ctx) +{ + int ret = 0; + + picoquic_cnx_t * cnx = picoquic_create_cnx(qclient, + picoquic_null_connection_id, picoquic_null_connection_id, + (struct sockaddr*)&ctx->server_addr, ctx->simulated_time, + 0, PICOQUIC_TEST_SNI, PICOQUIC_TEST_ALPN, 1); + + if (cnx == NULL) { + stress_debug_break(); + ret = -1; + } + else { + ret = stress_client_set_callback(cnx); + + if (ret == 0) { + ret = picoquic_start_client_cnx(cnx); + if (ret != 0) { + stress_debug_break(); + } + } + else { + stress_debug_break(); + } + } + + return ret; +} + +static int stress_loop_poll_context(picoquic_stress_ctx_t * ctx) +{ int ret = 0; int best_index = -1; int last_index = -1; @@ -519,6 +642,10 @@ int stress_loop_poll_context(picoquic_stress_ctx_t * ctx, uint64_t next_time) { } ret = stress_submit_sp_packets(ctx, ctx->c_ctx[x]->qclient, x); + + if (ret != 0) { + stress_debug_break(); + } } if (ret == 0) { @@ -528,28 +655,220 @@ int stress_loop_poll_context(picoquic_stress_ctx_t * ctx, uint64_t next_time) { if (best_index < 0) { /* The server is ready first */ ret = stress_handle_packet_prepare(ctx, ctx->qserver, -1); + + if (ret != 0) { + stress_debug_break(); + } } else { if (ret == 0 && ctx->c_ctx[best_index]->s_to_c_link->first_packet != NULL && ctx->c_ctx[best_index]->s_to_c_link->first_packet->arrival_time <= ctx->simulated_time) { /* dequeue packet from server to client and submit */ ret = stress_handle_packet_arrival(ctx, ctx->c_ctx[best_index]->qclient, ctx->c_ctx[best_index]->s_to_c_link); + if (ret != 0) { + stress_debug_break(); + } } if (ret == 0 && ctx->c_ctx[best_index]->c_to_s_link->first_packet != NULL && ctx->c_ctx[best_index]->c_to_s_link->first_packet->arrival_time <= ctx->simulated_time) { /* dequeue packet from client to server and submit */ ret = stress_handle_packet_arrival(ctx, ctx->qserver, ctx->c_ctx[best_index]->c_to_s_link); + if (ret != 0) { + stress_debug_break(); + } + } + + if (ctx->c_ctx[best_index]->qclient->cnx_wake_first != NULL) { + /* If the connection is valid, check whether it is ready */ + if (ctx->c_ctx[best_index]->qclient->cnx_wake_first->next_wake_time <= ctx->simulated_time) { + ret = stress_handle_packet_prepare(ctx, ctx->c_ctx[best_index]->qclient, best_index); + if (ret != 0) { + stress_debug_break(); + } + } } + } + } + + return ret; +} - if (ctx->c_ctx[best_index]->qclient->cnx_wake_first != NULL && - ctx->c_ctx[best_index]->qclient->cnx_wake_first->next_wake_time <= ctx->simulated_time) { +/* Stress test management + * Parameters: + * Number of clients + * Simulated duration of stress test + * Profile of client run, i.e. max number of queries/client. + * + * Operation: + * Initialize the context: + * Loop: + * Clean terminated connections (part of simulation loop) + * Create connection for empty client contexts (part of simulation loop?) + * Run the loop, sending packets, etc. + * Clean up: + * Set termination flag for all contexts: close existing connections, do not create new ones. + * Run the loop until all contexts are freed. + * Fail if cannot clean up on a timeout. + * Report: + * Statistics on duration, volume, connections. + * + * Stress succeeds if it comes to a successful end. + */ + +static const uint8_t stress_ticket_encrypt_key[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 +}; - ret = stress_handle_packet_prepare(ctx, ctx->c_ctx[best_index]->qclient, best_index); +static int stress_create_client_context(int client_index, picoquic_stress_ctx_t * stress_ctx) +{ + int ret = 0; + picoquic_stress_client_t * ctx = (picoquic_stress_client_t *)malloc(sizeof(picoquic_stress_client_t)); + stress_ctx->c_ctx[client_index] = ctx; + + if (ctx == NULL) { + DBG_PRINTF("Cannot create the client context #%d.\n", (int)client_index); + ret = -1; + } + if (ret == 0) { + memset(ctx, 0, sizeof(picoquic_stress_client_t)); + /* Initialize client specific address */ + stress_set_ip_address_from_index(&ctx->client_addr, (int)client_index); + /* set stream ID to default value */ + + /* initialize client specific ticket file */ + memcpy(ctx->ticket_file_name, "stress_ticket_000.bin", 19); + ctx->ticket_file_name[14] = (uint8_t)('0' + client_index / 100); + ctx->ticket_file_name[15] = (uint8_t)('0' + (client_index / 10) % 10); + ctx->ticket_file_name[16] = (uint8_t)('0' + client_index % 10); + ctx->ticket_file_name[21] = 0; + if (ret == 0) { + ret = picoquic_save_tickets(NULL, stress_ctx->simulated_time, ctx->ticket_file_name); + if (ret != 0) { + DBG_PRINTF("Cannot create ticket file <%s>.\n", ctx->ticket_file_name); } } } + if (ret == 0) { + /* initialize the simulation links from client to server and back. */ + ctx->c_to_s_link = picoquictest_sim_link_create(0.01, 10000, 0, 0, 0); + ctx->s_to_c_link = picoquictest_sim_link_create(0.01, 10000, 0, 0, 0); + if (ctx->c_to_s_link == NULL || + ctx->s_to_c_link == NULL) { + DBG_PRINTF("Cannot create the sim links for client #%d.\n", (int)client_index); + ret = -1; + } + } + + if (ret == 0) { + /* Create the quic context for this client*/ + ctx->qclient = picoquic_create(8, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, stress_ctx->simulated_time, &stress_ctx->simulated_time, + ctx->ticket_file_name, NULL, 0); + if (ctx->qclient == NULL) { + DBG_PRINTF("Cannot create the quic client #%d.\n", (int)client_index); + ret = -1; + } + } return ret; } +static void stress_delete_client_context(int client_index, picoquic_stress_ctx_t * stress_ctx) +{ + picoquic_stress_client_t * ctx = stress_ctx->c_ctx[client_index]; + + if (ctx != NULL) { + if (ctx->qclient != NULL) { + picoquic_free(ctx->qclient); + ctx->qclient = NULL; + } + + if (ctx->c_to_s_link != NULL) { + picoquictest_sim_link_delete(ctx->c_to_s_link); + ctx->c_to_s_link = NULL; + } + + if (ctx->s_to_c_link != NULL) { + picoquictest_sim_link_delete(ctx->s_to_c_link); + ctx->s_to_c_link = NULL; + } + free(ctx); + + stress_ctx->c_ctx[client_index] = NULL; + } +} + +int stress_test() +{ + int ret = 0; + picoquic_stress_ctx_t stress_ctx; + + /* Initialization */ + memset(&stress_ctx, 0, sizeof(picoquic_stress_ctx_t)); + stress_set_ip_address_from_index(&stress_ctx.server_addr, -1); + if (stress_ctx.nb_clients > PICOQUIC_MAX_STRESS_CLIENTS) { + DBG_PRINTF("Number of stress clients too high (%d). Should be lower than %d\n", + stress_ctx.nb_clients, PICOQUIC_MAX_STRESS_CLIENTS); + ret = -1; + } else { + stress_ctx.nb_clients = picoquic_stress_nb_clients; + stress_ctx.qserver = picoquic_create(PICOQUIC_MAX_STRESS_CLIENTS, +#ifdef _WINDOWS +#ifdef _WINDOWS64 + "..\\..\\certs\\cert.pem", "..\\..\\certs\\key.pem", +#else + "..\\certs\\cert.pem", "..\\certs\\key.pem", +#endif +#else + "certs/cert.pem", "certs/key.pem", +#endif + PICOQUIC_TEST_ALPN, stress_server_callback, (void*)&stress_ctx, NULL, NULL, NULL, + stress_ctx.simulated_time, &stress_ctx.simulated_time, NULL, + stress_ticket_encrypt_key, sizeof(stress_ticket_encrypt_key)); + + if (stress_ctx.qserver == NULL) { + DBG_PRINTF("%s", "Cannot create the test server.\n"); + ret = -1; + } + else { + for (int i = 0; ret == 0 && i < stress_ctx.nb_clients; i++) { + ret = stress_create_client_context(i, &stress_ctx); + } + } + } + + /* Run the simulation until the specified time */ + while (ret == 0 && stress_ctx.simulated_time < picoquic_stress_test_duration) { + /* Poll for new packet transmission */ + ret = stress_loop_poll_context(&stress_ctx); + + if (ret == 0) { + /* Check whether there is a need for new connections */ + for (int i = 0; ret == 0 && i < stress_ctx.nb_clients; i++) { + if (stress_ctx.c_ctx[i]->qclient->cnx_list == NULL) { + ret = stress_start_client_connection(stress_ctx.c_ctx[i]->qclient, &stress_ctx); + if (ret != 0) { + stress_debug_break(); + } + } + } + } + else { + stress_debug_break(); + } + } + + /* Shut down everything */ + for (int i = 0; i < stress_ctx.nb_clients; i++) { + stress_delete_client_context((int)i, &stress_ctx); + } + + if (stress_ctx.qserver != NULL) { + picoquic_free(stress_ctx.qserver); + stress_ctx.qserver = NULL; + } + + return ret; +} diff --git a/picoquictest/tls_api_test.c b/picoquictest/tls_api_test.c index d94cfbd5..86a484fe 100644 --- a/picoquictest/tls_api_test.c +++ b/picoquictest/tls_api_test.c @@ -1239,7 +1239,7 @@ int tls_api_very_long_max_test() int tls_api_very_long_with_err_test() { - return tls_api_one_scenario_test(test_scenario_very_long, sizeof(test_scenario_very_long), 0x30000, 128000, 0, 0, 10000000, NULL); + return tls_api_one_scenario_test(test_scenario_very_long, sizeof(test_scenario_very_long), 0x30000, 128000, 0, 0, 11000000, NULL); } int tls_api_very_long_congestion_test() -- GitLab