diff --git a/include/he.h b/include/he.h index 002465e1..6b4b7c68 100644 --- a/include/he.h +++ b/include/he.h @@ -270,6 +270,43 @@ typedef enum he_connection_protocol { HE_CONNECTION_PROTOCOL_DTLS_1_3 = 3 } he_connection_protocol_t; +/** + * @brief Lightway Path MTU Discovery states. + * @see RFC 8899 and RFC 4821 + */ +typedef enum he_pmtud_state { + // The DISABLED state is the initial state before probing has started. + // It is also entered from any other state, when the PL indicates loss of + // connectivity. This state is left once the PL indicates connectivity to the + // remote PL. When transitioning to the BASE state, a probe packet of size + // BASE_PLPMTU can be sent immediately + HE_PMTUD_STATE_DISABLED = 0, + + // The BASE state is used to confirm that the BASE_PLPMTU size is supported by + // the network path and is designed to allow an application to continue working + // when there are transient reductions in the actual PMTU. It also seeks to avoid + // long periods when a sender searching for a larger PLPMTU is unaware that + // packets are not being delivered due to a packet or ICMP black hole. + HE_PMTUD_STATE_BASE = 1, + + // The SEARCHING state is the main probing state. This state is entered when + // probing for the BASE_PLPMTU completes. + HE_PMTUD_STATE_SEARCHING = 2, + + // The SEARCH_COMPLETE state indicates that a search has completed. This is the + // normal maintenance state, where the PL is not probing to update the PLPMTU. + // DPLPMTUD remains in this state until either the PMTU_RAISE_TIMER expires or a + // black hole is detected. + HE_PMTUD_STATE_SEARCH_COMPLETE = 3, + + // The ERROR state represents the case where either the network path is not known + // to support a PLPMTU of at least the BASE_PLPMTU size or when there is + // contradictory information about the network path that would otherwise result + // in excessive variation in the MPS signaled to the higher layer. The state + // implements a method to mitigate oscillation in the state-event engine. + HE_PMTUD_STATE_ERROR = 4, +} he_pmtud_state_t; + typedef struct he_ssl_ctx he_ssl_ctx_t; typedef struct he_conn he_conn_t; typedef struct he_plugin_chain he_plugin_chain_t; @@ -277,7 +314,6 @@ typedef struct he_network_config_ipv4 he_network_config_ipv4_t; /** * @brief Data structure to hold all the state needed as a Helium client - * */ typedef struct he_client { he_ssl_ctx_t *ssl_ctx; @@ -468,6 +504,37 @@ typedef he_return_code_t (*he_populate_network_config_ipv4_cb_t)(he_conn_t *conn he_network_config_ipv4_t *config, void *context); +/** + * @brief The prototype for the Path MTU Discovery (PMTUD) time callback function + * @param conn A pointer to the connection that triggered this callback + * @param timeout The number of milliseconds to wait before calling the he_conn_pmtud_timeout + * function. If the timeout value is 0, the host application should cancel the timer. + * @param context A pointer to the user defined context + * @see he_conn_set_context Sets the value of the context pointer + * + * Lightway Path MTU Discovery needs to be able to resend probe messages if they are not received in + * time. As Lightway Core does not have its own threads or timers, it is up to the host application + * to tell Lightway Core when a certain amount of time has passed. + * + * The host application must register this callback to enable Path MTU discovery. + * + * @note Any pending timers should be reset with the value provided in the callback and there should + * only ever be one timer per connection context. + */ +typedef he_return_code_t (*he_pmtud_time_cb_t)(he_conn_t *conn, int timeout, void *context); + +/** + * @brief The prototype for Lightway PMTUD state callback function + * @param conn A pointer to the connection that triggered this callback + * @param state The state that Lightway PMTUD has just entered + * @param context A pointer to the user defined context + * @see he_conn_set_context Sets the value of the context pointer + * + * Whenever Lightway PMTUD changes state, this function will be called. + */ +typedef he_return_code_t (*he_pmtud_state_change_cb_t)(he_conn_t *conn, he_pmtud_state_t state, + void *context); + typedef struct he_network_config_ipv4 { char local_ip[HE_MAX_IPV4_STRING_LENGTH]; char peer_ip[HE_MAX_IPV4_STRING_LENGTH]; diff --git a/public/he.h b/public/he.h index 53217ab2..61f6dfee 100644 --- a/public/he.h +++ b/public/he.h @@ -270,6 +270,43 @@ typedef enum he_connection_protocol { HE_CONNECTION_PROTOCOL_DTLS_1_3 = 3 } he_connection_protocol_t; +/** + * @brief Lightway Path MTU Discovery states. + * @see RFC 8899 and RFC 4821 + */ +typedef enum he_pmtud_state { + // The DISABLED state is the initial state before probing has started. + // It is also entered from any other state, when the PL indicates loss of + // connectivity. This state is left once the PL indicates connectivity to the + // remote PL. When transitioning to the BASE state, a probe packet of size + // BASE_PLPMTU can be sent immediately + HE_PMTUD_STATE_DISABLED = 0, + + // The BASE state is used to confirm that the BASE_PLPMTU size is supported by + // the network path and is designed to allow an application to continue working + // when there are transient reductions in the actual PMTU. It also seeks to avoid + // long periods when a sender searching for a larger PLPMTU is unaware that + // packets are not being delivered due to a packet or ICMP black hole. + HE_PMTUD_STATE_BASE = 1, + + // The SEARCHING state is the main probing state. This state is entered when + // probing for the BASE_PLPMTU completes. + HE_PMTUD_STATE_SEARCHING = 2, + + // The SEARCH_COMPLETE state indicates that a search has completed. This is the + // normal maintenance state, where the PL is not probing to update the PLPMTU. + // DPLPMTUD remains in this state until either the PMTU_RAISE_TIMER expires or a + // black hole is detected. + HE_PMTUD_STATE_SEARCH_COMPLETE = 3, + + // The ERROR state represents the case where either the network path is not known + // to support a PLPMTU of at least the BASE_PLPMTU size or when there is + // contradictory information about the network path that would otherwise result + // in excessive variation in the MPS signaled to the higher layer. The state + // implements a method to mitigate oscillation in the state-event engine. + HE_PMTUD_STATE_ERROR = 4, +} he_pmtud_state_t; + typedef struct he_ssl_ctx he_ssl_ctx_t; typedef struct he_conn he_conn_t; typedef struct he_plugin_chain he_plugin_chain_t; @@ -277,7 +314,6 @@ typedef struct he_network_config_ipv4 he_network_config_ipv4_t; /** * @brief Data structure to hold all the state needed as a Helium client - * */ typedef struct he_client { he_ssl_ctx_t *ssl_ctx; @@ -468,6 +504,37 @@ typedef he_return_code_t (*he_populate_network_config_ipv4_cb_t)(he_conn_t *conn he_network_config_ipv4_t *config, void *context); +/** + * @brief The prototype for the Path MTU Discovery (PMTUD) time callback function + * @param conn A pointer to the connection that triggered this callback + * @param timeout The number of milliseconds to wait before calling the he_conn_pmtud_timeout + * function. If the timeout value is 0, the host application should cancel the timer. + * @param context A pointer to the user defined context + * @see he_conn_set_context Sets the value of the context pointer + * + * Lightway Path MTU Discovery needs to be able to resend probe messages if they are not received in + * time. As Lightway Core does not have its own threads or timers, it is up to the host application + * to tell Lightway Core when a certain amount of time has passed. + * + * The host application must register this callback to enable Path MTU discovery. + * + * @note Any pending timers should be reset with the value provided in the callback and there should + * only ever be one timer per connection context. + */ +typedef he_return_code_t (*he_pmtud_time_cb_t)(he_conn_t *conn, int timeout, void *context); + +/** + * @brief The prototype for Lightway PMTUD state callback function + * @param conn A pointer to the connection that triggered this callback + * @param state The state that Lightway PMTUD has just entered + * @param context A pointer to the user defined context + * @see he_conn_set_context Sets the value of the context pointer + * + * Whenever Lightway PMTUD changes state, this function will be called. + */ +typedef he_return_code_t (*he_pmtud_state_change_cb_t)(he_conn_t *conn, he_pmtud_state_t state, + void *context); + typedef struct he_network_config_ipv4 { char local_ip[HE_MAX_IPV4_STRING_LENGTH]; char peer_ip[HE_MAX_IPV4_STRING_LENGTH]; @@ -981,6 +1048,21 @@ bool he_ssl_ctx_is_auth_cb_set(he_ssl_ctx_t *ctx); void he_ssl_ctx_set_populate_network_config_ipv4_cb( he_ssl_ctx_t *ctx, he_populate_network_config_ipv4_cb_t pop_network_cb); +/** + * @brief Sets te function that will be called when Lightway PMTUD changes state + * @param ctx A pointer to a valid SSL context + * @param pmtud_state_change_cb The function to be called when Lightway PMTUD changes state + */ +void he_ssl_ctx_set_pmtud_state_change_cb(he_ssl_ctx_t *ctx, + he_pmtud_state_change_cb_t pmtud_state_change_cb); + +/** + * @brief Sets te function that will be called when Lightway PMTUD needs to start the timer + * @param ctx A pointer to a valid SSL context + * @param pmtud_time_cb The function to be called when Lightway PMTUD needs to start the time + */ +void he_ssl_ctx_set_pmtud_time_cb(he_ssl_ctx_t *ctx, he_pmtud_time_cb_t pmtud_time_cb); + /** * @brief Disables session roaming and removes the session ID from the packet header * @return HE_SUCCESS diff --git a/src/he/conn.c b/src/he/conn.c index a43cf3c6..b49b7aee 100644 --- a/src/he/conn.c +++ b/src/he/conn.c @@ -178,6 +178,8 @@ he_return_code_t he_internal_conn_configure(he_conn_t *conn, he_ssl_ctx_t *ctx) conn->auth_buf_cb = ctx->auth_buf_cb; conn->auth_token_cb = ctx->auth_token_cb; conn->populate_network_config_ipv4_cb = ctx->populate_network_config_ipv4_cb; + conn->pmtud_time_cb = ctx->pmtud_time_cb; + conn->pmtud_state_change_cb = ctx->pmtud_state_change_cb; // Copy the RNG to allow for generation of session IDs conn->wolf_rng = ctx->wolf_rng; diff --git a/src/he/conn.h b/src/he/conn.h index c1fff008..09b525d8 100644 --- a/src/he/conn.h +++ b/src/he/conn.h @@ -1,4 +1,4 @@ -/* * +/** * Lightway Core * Copyright (C) 2021 Express VPN International Ltd. * diff --git a/src/he/he_internal.h b/src/he/he_internal.h index d5ff5681..474a8963 100644 --- a/src/he/he_internal.h +++ b/src/he/he_internal.h @@ -103,6 +103,9 @@ struct he_ssl_ctx { he_auth_token_cb_t auth_token_cb; // Callback for populating the network config (server-only) he_populate_network_config_ipv4_cb_t populate_network_config_ipv4_cb; + // Callback for pmtud + he_pmtud_time_cb_t pmtud_time_cb; + he_pmtud_state_change_cb_t pmtud_state_change_cb; /// Don't send session ID in packet header bool disable_roaming_connections; /// Which padding type to use @@ -215,6 +218,9 @@ struct he_conn { he_auth_buf_cb_t auth_buf_cb; // Callback for populating the network config (server-only) he_populate_network_config_ipv4_cb_t populate_network_config_ipv4_cb; + // Callback for pmtud + he_pmtud_time_cb_t pmtud_time_cb; + he_pmtud_state_change_cb_t pmtud_state_change_cb; /// Connection version -- set on client side, accepted on server side he_version_info_t protocol_version; diff --git a/src/he/pmtud.h b/src/he/pmtud.h index 89ef9738..49a0a4ce 100644 --- a/src/he/pmtud.h +++ b/src/he/pmtud.h @@ -38,39 +38,6 @@ (HE_MAX_WIRE_MTU - HE_IPV4_HEADER_SIZE - HE_UDP_HEADER_SIZE - sizeof(he_wire_hdr_t) - \ HE_WOLF_MAX_HEADER_SIZE - sizeof(he_msg_data_t)) -typedef enum he_pmtud_state { - // The DISABLED state is the initial state before probing has started. - // It is also entered from any other state, when the PL indicates loss of - // connectivity. This state is left once the PL indicates connectivity to the - // remote PL. When transitioning to the BASE state, a probe packet of size - // BASE_PLPMTU can be sent immediately - HE_PMTUD_STATE_DISABLED = 0, - - // The BASE state is used to confirm that the BASE_PLPMTU size is supported by - // the network path and is designed to allow an application to continue working - // when there are transient reductions in the actual PMTU. It also seeks to avoid - // long periods when a sender searching for a larger PLPMTU is unaware that - // packets are not being delivered due to a packet or ICMP black hole. - HE_PMTUD_STATE_BASE = 1, - - // The SEARCHING state is the main probing state. This state is entered when - // probing for the BASE_PLPMTU completes. - HE_PMTUD_STATE_SEARCHING = 2, - - // The SEARCH_COMPLETE state indicates that a search has completed. This is the - // normal maintenance state, where the PL is not probing to update the PLPMTU. - // DPLPMTUD remains in this state until either the PMTU_RAISE_TIMER expires or a - // black hole is detected. - HE_PMTUD_STATE_SEARCH_COMPLETE = 3, - - // The ERROR state represents the case where either the network path is not known - // to support a PLPMTU of at least the BASE_PLPMTU size or when there is - // contradictory information about the network path that would otherwise result - // in excessive variation in the MPS signaled to the higher layer. The state - // implements a method to mitigate oscillation in the state-event engine. - HE_PMTUD_STATE_ERROR = 4, -} he_pmtud_state_t; - // Internal functions for PMTUD /** diff --git a/src/he/ssl_ctx.c b/src/he/ssl_ctx.c index daa794d5..a74f5d33 100644 --- a/src/he/ssl_ctx.c +++ b/src/he/ssl_ctx.c @@ -667,6 +667,25 @@ void he_ssl_ctx_set_populate_network_config_ipv4_cb( ctx->populate_network_config_ipv4_cb = pop_network_cb; } +void he_ssl_ctx_set_pmtud_time_cb(he_ssl_ctx_t *ctx, he_pmtud_time_cb_t pmtud_time_cb) { + // Return if ctx is null + if(!ctx) { + return; + } + + ctx->pmtud_time_cb = pmtud_time_cb; +} + +void he_ssl_ctx_set_pmtud_state_change_cb(he_ssl_ctx_t *ctx, + he_pmtud_state_change_cb_t pmtud_state_change_cb) { + // Return if ctx is null + if(!ctx) { + return; + } + + ctx->pmtud_state_change_cb = pmtud_state_change_cb; +} + he_return_code_t he_ssl_ctx_set_disable_roaming(he_ssl_ctx_t *ctx) { // Return if ctx is null if(!ctx) { diff --git a/src/he/ssl_ctx.h b/src/he/ssl_ctx.h index 46e3c483..d93b326c 100644 --- a/src/he/ssl_ctx.h +++ b/src/he/ssl_ctx.h @@ -1,4 +1,4 @@ -/* * +/** * Lightway Core * Copyright (C) 2021 Express VPN International Ltd. * @@ -470,6 +470,20 @@ bool he_ssl_ctx_is_auth_cb_set(he_ssl_ctx_t *ctx); void he_ssl_ctx_set_populate_network_config_ipv4_cb( he_ssl_ctx_t *ctx, he_populate_network_config_ipv4_cb_t pop_network_cb); +/** + * @brief Sets te function that will be called when Lightway PMTUD changes state + * @param ctx A pointer to a valid SSL context + * @param pmtud_state_change_cb The function to be called when Lightway PMTUD changes state + */ +void he_ssl_ctx_set_pmtud_state_change_cb(he_ssl_ctx_t *ctx, + he_pmtud_state_change_cb_t pmtud_state_change_cb); +/** + * @brief Sets te function that will be called when Lightway PMTUD needs to start the timer + * @param ctx A pointer to a valid SSL context + * @param pmtud_time_cb The function to be called when Lightway PMTUD needs to start the time + */ +void he_ssl_ctx_set_pmtud_time_cb(he_ssl_ctx_t *ctx, he_pmtud_time_cb_t pmtud_time_cb); + /** * @brief Disables session roaming and removes the session ID from the packet header * @return HE_SUCCESS diff --git a/test/he/test_conn.c b/test/he/test_conn.c index 815ed1d2..74c91e8f 100644 --- a/test/he/test_conn.c +++ b/test/he/test_conn.c @@ -1484,6 +1484,8 @@ void test_he_internal_conn_configure_no_version(void) { ssl_ctx.outside_write_cb = (he_outside_write_cb_t)0x7; ssl_ctx.network_config_ipv4_cb = (he_network_config_ipv4_cb_t)0x8; ssl_ctx.populate_network_config_ipv4_cb = (he_populate_network_config_ipv4_cb_t)0x9; + ssl_ctx.pmtud_time_cb = (he_pmtud_time_cb_t)0x10; + ssl_ctx.pmtud_state_change_cb = (he_pmtud_state_change_cb_t)0x11; memset(&ssl_ctx.wolf_rng, 1, sizeof(WC_RNG)); @@ -1511,6 +1513,8 @@ void test_he_internal_conn_configure_no_version(void) { TEST_ASSERT_EQUAL(conn.outside_write_cb, ssl_ctx.outside_write_cb); TEST_ASSERT_EQUAL(conn.network_config_ipv4_cb, ssl_ctx.network_config_ipv4_cb); TEST_ASSERT_EQUAL(conn.populate_network_config_ipv4_cb, ssl_ctx.populate_network_config_ipv4_cb); + TEST_ASSERT_EQUAL(conn.pmtud_time_cb, ssl_ctx.pmtud_time_cb); + TEST_ASSERT_EQUAL(conn.pmtud_state_change_cb, ssl_ctx.pmtud_state_change_cb); TEST_ASSERT_EQUAL(0, memcmp(&conn.wolf_rng, &ssl_ctx.wolf_rng, sizeof(WC_RNG))); } diff --git a/test/he/test_ssl_ctx.c b/test/he/test_ssl_ctx.c index 6d6106d8..4da94114 100644 --- a/test/he/test_ssl_ctx.c +++ b/test/he/test_ssl_ctx.c @@ -852,6 +852,16 @@ void test_set_populate_network_config_cb(void) { TEST_ASSERT_EQUAL(pop_network_config_cb, ctx->populate_network_config_ipv4_cb); } +void test_set_pmtud_time_cb(void) { + he_ssl_ctx_set_pmtud_time_cb(ctx, pmtud_time_cb); + TEST_ASSERT_EQUAL(pmtud_time_cb, ctx->pmtud_time_cb); +} + +void test_set_pmtud_state_change_cb(void) { + he_ssl_ctx_set_pmtud_state_change_cb(ctx, pmtud_state_change_cb); + TEST_ASSERT_EQUAL(pmtud_state_change_cb, ctx->pmtud_state_change_cb); +} + void test_set_server_cert_nulls(void) { int res = he_ssl_ctx_set_server_cert_key_files(ctx, good_username, NULL); TEST_ASSERT_EQUAL(HE_ERR_NULL_POINTER, res); diff --git a/test/support/test_defs.h b/test/support/test_defs.h index 87fce59b..92cc9b4b 100644 --- a/test/support/test_defs.h +++ b/test/support/test_defs.h @@ -86,6 +86,16 @@ he_return_code_t pop_network_config_cb(he_conn_t *conn, he_network_config_ipv4_t return HE_SUCCESS; } +he_return_code_t pmtud_time_cb(he_conn_t *conn, int timeout, void *context) { + call_counter++; + return HE_SUCCESS; +} + +he_return_code_t pmtud_state_change_cb(he_conn_t *conn, he_pmtud_state_t state, void *context) { + call_counter++; + return HE_SUCCESS; +} + he_return_code_t stub_overflow_plugin(he_plugin_chain_t *chain, uint8_t *packet, size_t *length, size_t capacity, int numCalls) { *length = capacity + 1;