QUIC PORT: Allow errors to be tracked at port level

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22674)
This commit is contained in:
Hugo Landau 2023-11-09 10:27:14 +00:00
parent f12ea1f1e0
commit 4df4add22d
4 changed files with 57 additions and 12 deletions

View File

@ -133,6 +133,12 @@ void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit);
/* Returns 1 if the port is running/healthy, 0 if it has failed. */ /* Returns 1 if the port is running/healthy, 0 if it has failed. */
int ossl_quic_port_is_running(const QUIC_PORT *port); int ossl_quic_port_is_running(const QUIC_PORT *port);
/*
* Restores port-level error to the error stack. To be called only if
* the port is no longer running.
*/
void ossl_quic_port_restore_err_state(const QUIC_PORT *port);
/* /*
* Events * Events
* ====== * ======
@ -140,9 +146,11 @@ int ossl_quic_port_is_running(const QUIC_PORT *port);
/* /*
* Called if a permanent network error occurs. Terminates all channels * Called if a permanent network error occurs. Terminates all channels
* immediately. * immediately. triggering_ch is an optional argument designating
* a channel which encountered the network error.
*/ */
void ossl_quic_port_raise_net_error(QUIC_PORT *port); void ossl_quic_port_raise_net_error(QUIC_PORT *port,
QUIC_CHANNEL *triggering_ch);
# endif # endif

View File

@ -2310,7 +2310,7 @@ static int ch_tx(QUIC_CHANNEL *ch)
case QTX_FLUSH_NET_RES_PERMANENT_FAIL: case QTX_FLUSH_NET_RES_PERMANENT_FAIL:
default: default:
/* Permanent underlying network BIO, start terminating. */ /* Permanent underlying network BIO, start terminating. */
ossl_quic_port_raise_net_error(ch->port); ossl_quic_port_raise_net_error(ch->port, ch);
break; break;
} }
@ -2927,13 +2927,14 @@ void ossl_quic_channel_raise_net_error(QUIC_CHANNEL *ch)
{ {
QUIC_TERMINATE_CAUSE tcause = {0}; QUIC_TERMINATE_CAUSE tcause = {0};
if (ch->net_error)
return;
ch->net_error = 1; ch->net_error = 1;
ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR,
"connection terminated due to network error");
ch_save_err_state(ch);
tcause.error_code = QUIC_ERR_INTERNAL_ERROR; tcause.error_code = QUIC_ERR_INTERNAL_ERROR;
tcause.reason = "network BIO I/O error";
tcause.reason_len = strlen(tcause.reason);
/* /*
* Skip Terminating state and go directly to Terminated, no point trying to * Skip Terminating state and go directly to Terminated, no point trying to
@ -2952,7 +2953,10 @@ void ossl_quic_channel_restore_err_state(QUIC_CHANNEL *ch)
if (ch == NULL) if (ch == NULL)
return; return;
OSSL_ERR_STATE_restore(ch->err_state); if (!ossl_quic_port_is_running(ch->port))
ossl_quic_port_restore_err_state(ch->port);
else
OSSL_ERR_STATE_restore(ch->err_state);
} }
void ossl_quic_channel_raise_protocol_error_loc(QUIC_CHANNEL *ch, void ossl_quic_channel_raise_protocol_error_loc(QUIC_CHANNEL *ch,

View File

@ -70,6 +70,9 @@ static int port_init(QUIC_PORT *port)
if (port->channel_ctx == NULL) if (port->channel_ctx == NULL)
goto err; goto err;
if ((port->err_state = OSSL_ERR_STATE_new()) == NULL)
goto err;
if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL, if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL,
/*Short CID Len=*/rx_short_dcid_len, /*Short CID Len=*/rx_short_dcid_len,
get_time, port)) == NULL) get_time, port)) == NULL)
@ -108,6 +111,9 @@ static void port_cleanup(QUIC_PORT *port)
ossl_quic_lcidm_free(port->lcidm); ossl_quic_lcidm_free(port->lcidm);
port->lcidm = NULL; port->lcidm = NULL;
OSSL_ERR_STATE_free(port->err_state);
port->err_state = NULL;
} }
static void port_transition_failed(QUIC_PORT *port) static void port_transition_failed(QUIC_PORT *port)
@ -341,7 +347,8 @@ static void port_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags)
if (!port->inhibit_tick) { if (!port->inhibit_tick) {
/* Handle any incoming data from network. */ /* Handle any incoming data from network. */
port_rx_pre(port); if (ossl_quic_port_is_running(port))
port_rx_pre(port);
/* Iterate through all channels and service them. */ /* Iterate through all channels and service them. */
LIST_FOREACH(ch, ch, &port->channel_list) { LIST_FOREACH(ch, ch, &port->channel_list) {
@ -370,7 +377,7 @@ static void port_rx_pre(QUIC_PORT *port)
* Terminated state as there is no point trying to send CONNECTION_CLOSE * Terminated state as there is no point trying to send CONNECTION_CLOSE
* frames if the network BIO is not operating correctly. * frames if the network BIO is not operating correctly.
*/ */
ossl_quic_port_raise_net_error(port); ossl_quic_port_raise_net_error(port, NULL);
} }
/* /*
@ -538,12 +545,35 @@ void ossl_quic_port_set_inhibit_tick(QUIC_PORT *port, int inhibit)
port->inhibit_tick = (inhibit != 0); port->inhibit_tick = (inhibit != 0);
} }
void ossl_quic_port_raise_net_error(QUIC_PORT *port) void ossl_quic_port_raise_net_error(QUIC_PORT *port,
QUIC_CHANNEL *triggering_ch)
{ {
QUIC_CHANNEL *ch; QUIC_CHANNEL *ch;
if (!ossl_quic_port_is_running(port))
return;
/*
* Immediately capture any triggering error on the error stack, with a
* cover error.
*/
ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR,
"port failed due to network BIO I/O error");
OSSL_ERR_STATE_save(port->err_state);
port_transition_failed(port); port_transition_failed(port);
/* Give the triggering channel (if any) the first notification. */
if (triggering_ch != NULL)
ossl_quic_channel_raise_net_error(triggering_ch);
LIST_FOREACH(ch, ch, &port->channel_list) LIST_FOREACH(ch, ch, &port->channel_list)
ossl_quic_channel_raise_net_error(ch); if (ch != triggering_ch)
ossl_quic_channel_raise_net_error(ch);
}
void ossl_quic_port_restore_err_state(const QUIC_PORT *port)
{
ERR_clear_error();
OSSL_ERR_STATE_restore(port->err_state);
} }

View File

@ -71,6 +71,9 @@ struct quic_port_st {
/* SRTM used for incoming packet routing by SRT. */ /* SRTM used for incoming packet routing by SRT. */
QUIC_SRTM *srtm; QUIC_SRTM *srtm;
/* Port-level permanent errors (causing failure state) are stored here. */
ERR_STATE *err_state;
/* DCID length used for incoming short header packets. */ /* DCID length used for incoming short header packets. */
unsigned char rx_short_dcid_len; unsigned char rx_short_dcid_len;
/* For clients, CID length used for outgoing Initial packets. */ /* For clients, CID length used for outgoing Initial packets. */