MEDIUM: quic: report closing state for the MUX

Define a new API to notify the MUX from the quic-conn when the
connection is about to be closed. This happens in the following cases :
- on idle timeout
- on CONNECTION_CLOSE emission or reception

The MUX wake callback is called on these conditions. The quic-conn
QUIC_FL_NOTIFY_CLOSE is set to only report once. On the MUX side,
connection flags CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH are set to interrupt
future emission/reception.

This patch is the counterpart to
  "MEDIUM: mux-quic: report CO_FL_ERROR on send".
Now the quic-conn is able to report its closing, which may be translated
by the MUX into a CO_FL_ERROR on the connection for the upper layer.
This allows the MUX to properly react to the QUIC closing mechanism for
both idle-timeout and closing/draining states.
This commit is contained in:
Amaury Denoyelle 2022-04-06 10:28:43 +02:00
parent fe035eca3a
commit b515b0af1d
4 changed files with 27 additions and 0 deletions

View File

@ -664,6 +664,7 @@ enum qc_mux_state {
#define QUIC_FL_CONN_LISTENER (1U << 3)
#define QUIC_FL_CONN_ACCEPT_REGISTERED (1U << 4)
#define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6)
#define QUIC_FL_CONN_NOTIFY_CLOSE (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */
#define QUIC_FL_CONN_EXP_TIMER (1U << 28) /* timer has expired, quic-conn can be freed */
#define QUIC_FL_CONN_CLOSING (1U << 29)
#define QUIC_FL_CONN_DRAINING (1U << 30)

View File

@ -1251,5 +1251,7 @@ int qc_send_app_pkts(struct quic_conn *qc, struct list *frms);
struct qc_stream_desc *qc_stream_desc_new(uint64_t id, void *ctx);
void qc_stream_desc_release(struct qc_stream_desc *stream, struct quic_conn *qc);
void qc_notify_close(struct quic_conn *qc);
#endif /* USE_QUIC */
#endif /* _HAPROXY_XPRT_QUIC_H */

View File

@ -1255,6 +1255,9 @@ static int qc_wake(struct connection *conn)
if (unlikely(prx->flags & (PR_FL_DISABLED|PR_FL_STOPPED)))
goto release;
if (conn->qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE)
qcc->conn->flags |= (CO_FL_SOCK_RD_SH|CO_FL_SOCK_WR_SH);
qc_send(qcc);
qc_wake_some_streams(qcc);

View File

@ -2590,6 +2590,7 @@ static int qc_parse_pkt_frms(struct quic_rx_packet *pkt, struct ssl_sock_ctx *ct
*/
qc_idle_timer_do_rearm(qc);
qc->flags |= QUIC_FL_CONN_DRAINING|QUIC_FL_CONN_IMMEDIATE_CLOSE;
qc_notify_close(qc);
}
break;
case QUIC_FT_HANDSHAKE_DONE:
@ -3045,6 +3046,8 @@ int qc_send_ppkts(struct qring *qr, struct ssl_sock_ctx *ctx)
if (!(qc->flags & QUIC_FL_CONN_CLOSING) &&
(pkt->flags & QUIC_FL_TX_PACKET_CC)) {
qc->flags |= QUIC_FL_CONN_CLOSING;
qc_notify_close(qc);
/* RFC 9000 10.2. Immediate Close:
* The closing and draining connection states exist to ensure
* that connections close cleanly and that delayed or reordered
@ -4115,6 +4118,11 @@ static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int s
{
struct quic_conn *qc = ctx;
/* Notify the MUX before settings QUIC_FL_CONN_EXP_TIMER or the MUX
* might free the quic-conn too early via quic_close().
*/
qc_notify_close(qc);
/* If the MUX is still alive, keep the quic-conn. The MUX is
* responsible to call quic_close to release it.
*/
@ -5925,6 +5933,19 @@ void qc_stream_desc_release(struct qc_stream_desc *stream,
eb64_insert(&qc->streams_by_id, &stream->by_id);
}
/* Notify the MUX layer if alive about an imminent close of <qc>. */
void qc_notify_close(struct quic_conn *qc)
{
if (qc->flags & QUIC_FL_CONN_NOTIFY_CLOSE)
return;
qc->flags |= QUIC_FL_CONN_NOTIFY_CLOSE;
/* wake up the MUX */
if (qc->mux_state == QC_MUX_READY && qc->conn->mux->wake)
qc->conn->mux->wake(qc->conn);
}
/* Function to automatically activate QUIC traces on stdout.
* Activated via the compilation flag -DENABLE_QUIC_STDOUT_TRACES.
* Main use for now is in the docker image for QUIC interop testing.