/* The maximum number of bytes of CRYPTO data in flight during handshakes. */
#define QUIC_CRYPTO_IN_FLIGHT_MAX 4096
-/* Status of the MUX layer. This defines how to handle app data.
- *
- * During a standard quic_conn lifetime it transitions like this :
- * QC_MUX_NULL -> QC_MUX_READY -> QC_MUX_RELEASED
- */
-enum qc_mux_state {
- QC_MUX_NULL, /* not allocated, data should be buffered */
- QC_MUX_READY, /* allocated, ready to handle data */
- QC_MUX_RELEASED, /* released, data can be dropped */
-};
-
/* Counters at QUIC connection level */
struct quic_conn_cntrs {
long long dropped_pkt; /* total number of dropped packets */
/* QUIC transport parameters TLS extension */
int tps_tls_ext;
int state;
- enum qc_mux_state mux_state; /* status of the connection/mux layer */
#ifdef HAVE_OPENSSL_QUIC
uint32_t prot_level;
#endif
#define QUIC_FL_CONN_PEER_VALIDATED_ADDR (1U << 17) /* Peer address is considered as validated for this connection. */
#define QUIC_FL_CONN_NO_TOKEN_RCVD (1U << 18) /* Client dit not send any token */
#define QUIC_FL_CONN_SCID_RECEIVED (1U << 19) /* (client only: first Initial received. */
+#define QUIC_FL_CONN_XPRT_CLOSED (1U << 20) /* close callback of xprt layer already called */
/* gap here */
#define QUIC_FL_CONN_TO_KILL (1U << 24) /* Unusable connection, to be killed */
#define QUIC_FL_CONN_TX_TP_RECEIVED (1U << 25) /* Peer transport parameters have been received (used for the transmitting part) */
/* Function pointer that can be used to derive a new CID from the previously computed hash */
extern void (*quic_newcid_from_hash64)(unsigned char *cid, int size, uint64_t hash, const unsigned char *secret, size_t secretlen);
+/* QUIC xprt layer functions */
+int qc_wait_for_conn(const struct quic_conn *qc);
+int qc_is_conn_ready(const struct quic_conn *qc);
+
#endif /* USE_QUIC */
#endif /* _HAPROXY_QUIC_CONN_H */
else
chunk_appendf(&trash, " st=opened ");
- if (qc->mux_state == QC_MUX_NULL)
+ if (qc_wait_for_conn(qc))
chunk_appendf(&trash, "mux=null ");
- else if (qc->mux_state == QC_MUX_READY)
+ else if (qc_is_conn_ready(qc))
chunk_appendf(&trash, "mux=ready ");
else
chunk_appendf(&trash, "mux=released ");
if (addnl)
chunk_appendf(&trash, "\n");
- if (ctx->fields & QUIC_DUMP_FLD_MUX && qc->mux_state == QC_MUX_READY)
+ if ((ctx->fields & QUIC_DUMP_FLD_MUX) && qc_is_conn_ready(qc))
qcc_show_quic(qc->qcc);
chunk_appendf(&trash, "\n");
{
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
- if (qc->mux_state == QC_MUX_RELEASED && eb_is_empty(&qc->streams_by_id)) {
+ if ((qc->flags & QUIC_FL_CONN_XPRT_CLOSED) && eb_is_empty(&qc->streams_by_id)) {
/* Reuse errcode which should have been previously set by the MUX on release. */
quic_set_connection_close(qc, qc->err);
tasklet_wakeup(qc->wait_event.tasklet);
{
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
- if (frm->type == QUIC_FT_CONNECTION_CLOSE)
+ if (frm->type == QUIC_FT_CONNECTION_CLOSE) {
quic_stats_transp_err_count_inc(qc->prx_counters, frm->connection_close.error_code);
+ }
else if (frm->type == QUIC_FT_CONNECTION_CLOSE_APP) {
- if (qc->mux_state != QC_MUX_READY || !qc->qcc->app_ops->inc_err_cnt)
+ if (!qc_is_conn_ready(qc) || !qc->qcc->app_ops->inc_err_cnt)
goto out;
qc->qcc->app_ops->inc_err_cnt(qc->qcc->ctx, frm->connection_close_app.error_code);
qc->next_cid_seq_num = 1;
}
- qc->mux_state = QC_MUX_NULL;
qc->err = quic_err_transport(QC_ERR_NO_ERROR);
/* Listener only: if connection is instantiated due to an INITIAL packet with an
/* Wake up streams layer waiting for buffer. Useful after congestion
* window increase.
*/
- if (qc->mux_state == QC_MUX_READY && (qc->qcc->flags & QC_CF_CONN_FULL))
+ if (qc_is_conn_ready(qc) && (qc->qcc->flags & QC_CF_CONN_FULL))
qcc_notify_buf(qc->qcc, 0);
return 0;
{
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
TRACE_STATE("error notified to mux", QUIC_EV_CONN_CLOSE, qc);
/* Mark socket as closed. */
break;
}
case QUIC_FT_RESET_STREAM:
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
struct qf_reset_stream *rs_frm = &frm->reset_stream;
qcc_recv_reset_stream(qc->qcc, rs_frm->id, rs_frm->app_error_code, rs_frm->final_size);
}
case QUIC_FT_STOP_SENDING:
{
struct qf_stop_sending *ss_frm = &frm->stop_sending;
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
if (qcc_recv_stop_sending(qc->qcc, ss_frm->id,
ss_frm->app_error_code)) {
TRACE_ERROR("qcc_recv_stop_sending() failed", QUIC_EV_CONN_PRSHPKT, qc);
qc->rx.stream_max_uni : qc->rx.stream_max_bidi;
/* The upper layer may not be allocated. */
- if (qc->mux_state != QC_MUX_READY) {
+ if (!qc_is_conn_ready(qc)) {
if (strm_frm->id < max) {
TRACE_DATA("Already closed stream", QUIC_EV_CONN_PRSHPKT, qc);
}
break;
}
case QUIC_FT_MAX_DATA:
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
struct qf_max_data *md_frm = &frm->max_data;
qcc_recv_max_data(qc->qcc, md_frm->max_data);
}
break;
case QUIC_FT_MAX_STREAM_DATA:
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
struct qf_max_stream_data *msd_frm = &frm->max_stream_data;
if (qcc_recv_max_stream_data(qc->qcc, msd_frm->id,
msd_frm->max_stream_data)) {
break;
case QUIC_FT_MAX_STREAMS_BIDI:
case QUIC_FT_MAX_STREAMS_UNI:
- if (qc->mux_state == QC_MUX_READY) {
+ if (qc_is_conn_ready(qc)) {
int bidi;
struct qf_max_streams *ms_frm;
}
/* check if the connection layer is ready before using app level */
- if ((qel == qc->ael || qel == qc->eel) &&
- qc->mux_state == QC_MUX_NULL) {
+ if ((qel == qc->ael || qel == qc->eel) && qc_wait_for_conn(qc)) {
TRACE_PROTO("connection layer not ready", QUIC_EV_CONN_TRMHP, qc);
goto cant_rm_hp;
}
/* Wake up MUX after its creation. Operation similar to TLS+ALPN on TCP stack. */
qc->conn->mux->wake(qc->conn);
- qc->mux_state = QC_MUX_READY;
}
else {
TRACE_PROTO("could not start the mux", QUIC_EV_CONN_IO_CB, qc);
int max_dgram = 0, sent;
TRACE_ENTER(QUIC_EV_CONN_TXPKT, qc);
- BUG_ON(qc->mux_state != QC_MUX_READY); /* Only MUX can uses this function so it must be ready. */
if (qc->conn->flags & CO_FL_SOCK_WR_SH) {
qc->conn->flags |= CO_FL_ERROR | CO_FL_SOCK_RD_SH;
#include <haproxy/quic_trace.h>
#include <haproxy/trace.h>
+/* Returns true if conn layer above <qc> has not been yet fully initialized, i.e. MUX is not yet opened. */
+int qc_wait_for_conn(const struct quic_conn *qc)
+{
+ return (!qc->conn || !qc->conn->mux) &&
+ !(qc->flags & QUIC_FL_CONN_XPRT_CLOSED);
+}
+
+/* Returns true if conn layer above <qc> is fully initialized and available. */
+int qc_is_conn_ready(const struct quic_conn *qc)
+{
+ return qc->conn && qc->conn->mux;
+}
+
static void quic_close(struct connection *conn, void *xprt_ctx)
{
struct ssl_sock_ctx *conn_ctx = xprt_ctx;
TRACE_ENTER(QUIC_EV_CONN_CLOSE, qc);
+ qc->flags |= QUIC_FL_CONN_XPRT_CLOSED;
qc->conn = NULL;
- /* Next application data can be dropped. */
- qc->mux_state = QC_MUX_RELEASED;
-
/* If the quic-conn timer has already expired or if already in "connection close"
* state, free the quic-conn.
*/
qc = conn->handle.qc;
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
- if (objt_listener(conn->target)) {
- /* mux-quic can now be considered ready. */
- qc->mux_state = QC_MUX_READY;
- }
- else {
- /* This has as side effet to create a SSL_SESSION object attached to
- * the SSL object.
- */
- if (!qc_ssl_do_hanshake(qc, ctx))
- goto out;
- }
+ /* This has as side effet to create a SSL_SESSION object attached to
+ * the SSL object.
+ */
+ if (qc_is_back(qc) && !qc_ssl_do_hanshake(qc, ctx))
+ goto out;
/* Schedule quic-conn to ensure post handshake frames are emitted. This
* is not done for 0-RTT as xprt->start happens before handshake