This patch reworks the installation of app-ops layer by QUIC MUX.
Previously, app_ops field was stored directly into the quic_conn
structure. Then the MUX reused it directly during its qmux_init().
This patch removes app_ops field from quic_conn and replaces it with a
copy of the negotiated ALPN. By using quic_alpn_to_app_ops(), it ensures
it remains compatible with a known application layer.
On the MUX layer, qcc_install_app_ops() now uses the standard
conn_get_alpn() to retrieve the ALPN from the transport layer. This is
done via the newly defined <get_alpn> QUIC xprt callback.
This new architecture should be cleaner as it better highlights the
responsibility of each layers in the ALPN/app negotiation.
/* QUIC application layer operations */
struct qcc_app_ops {
+ const char *alpn;
+
/* Initialize <qcc> connection app context. */
int (*init)(struct qcc *qcc);
/* Finish connection initialization if prelude required. */
}
}
-int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops);
+int qcc_install_app_ops(struct qcc *qcc);
/* Register <qcs> stream for http-request timeout. If the stream is not yet
* attached in the configured delay, qcc timeout task will be triggered. This
struct eb_root streams_by_id; /* qc_stream_desc tree */
+ const char *alpn;
+
/* MUX */
struct qcc *qcc;
struct task *timer_task;
/* Handshake expiration date */
unsigned int hs_expire;
- const struct qcc_app_ops *app_ops;
/* Callback to close any stream after MUX closure - set by the MUX itself */
int (*strm_reject)(struct list *out, uint64_t stream_id);
void chunk_frm_appendf(struct buffer *buf, const struct quic_frame *frm);
void quic_set_connection_close(struct quic_conn *qc, const struct quic_err err);
void quic_set_tls_alert(struct quic_conn *qc, int alert);
-int quic_set_app_ops(struct quic_conn *qc, const char *alpn, int alpn_len);
+int qc_register_alpn(struct quic_conn *qc, const char *alpn, int alpn_len);
int qc_check_dcid(struct quic_conn *qc, unsigned char *dcid, size_t dcid_len);
void qc_notify_err(struct quic_conn *qc);
/* HTTP/3 application layer operations */
const struct qcc_app_ops h3_ops = {
+ .alpn = "h3",
+
.init = h3_init,
.finalize = h3_finalize,
.attach = h3_attach,
}
const struct qcc_app_ops hq_interop_ops = {
+ .alpn = "hq-interop",
+
.rcv_buf = hq_interop_rcv_buf,
.snd_buf = hq_interop_snd_buf,
.nego_ff = hq_interop_nego_ff,
TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn, qcs);
}
-/* Install the <app_ops> applicative layer of a QUIC connection on mux <qcc>.
+/* Install the applicative layer of a QUIC connection on mux <qcc>.
* Returns 0 on success else non-zero.
*/
-int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops)
+int qcc_install_app_ops(struct qcc *qcc)
{
- TRACE_ENTER(QMUX_EV_QCC_NEW, qcc->conn);
+ struct connection *conn = qcc->conn;
+ const struct qcc_app_ops *app_ops;
+ const char *alpn;
+ int alpn_len;
+
+ TRACE_ENTER(QMUX_EV_QCC_NEW, conn);
+
+ if (!conn_get_alpn(conn, &alpn, &alpn_len))
+ goto err;
+
+ app_ops = quic_alpn_to_app_ops(alpn, alpn_len);
+ if (!app_ops)
+ goto err;
if (app_ops->init && !app_ops->init(qcc)) {
- TRACE_ERROR("application layer install error", QMUX_EV_QCC_NEW, qcc->conn);
+ TRACE_ERROR("application layer install error", QMUX_EV_QCC_NEW, conn);
goto err;
}
- TRACE_PROTO("application layer installed", QMUX_EV_QCC_NEW, qcc->conn);
+ TRACE_PROTO("application layer installed", QMUX_EV_QCC_NEW, conn);
qcc->app_ops = app_ops;
- TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn);
+ TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
return 0;
err:
- TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn);
+ TRACE_LEAVE(QMUX_EV_QCC_NEW, conn);
return 1;
}
/* Register conn as app_ops may use it. */
qcc->conn = conn;
- if (qcc_install_app_ops(qcc, conn->handle.qc->app_ops)) {
+ if (qcc_install_app_ops(qcc)) {
TRACE_PROTO("Cannot install app layer", QMUX_EV_QCC_NEW|QMUX_EV_QCC_ERR, conn);
goto err;
}
TRACE_LEAVE(QUIC_EV_CONN_SSLALERT, qc);
}
-/* Set the application for <qc> QUIC connection.
- * Return 1 if succeeded, 0 if not.
+/* Register the negotiated TLS ALPN <alpn> of length <alpn_len> for <qc> QUIC
+ * connection. This checks that the protocol is compatible with the QUIC stack.
+ *
+ * Returns 1 on success else 0.
*/
-int quic_set_app_ops(struct quic_conn *qc, const char *alpn, int alpn_len)
+int qc_register_alpn(struct quic_conn *qc, const char *alpn, int alpn_len)
{
- if (!(qc->app_ops = quic_alpn_to_app_ops(alpn, alpn_len)))
+ const struct qcc_app_ops *app_ops;
+
+ if (!(app_ops = quic_alpn_to_app_ops(alpn, alpn_len)))
return 0;
+ qc->alpn = app_ops->alpn;
return 1;
-
}
/* Try to reuse <alpn> ALPN and <etps> early transport parameters.
- * This function also sets the application operations calling
- * quic_set_app_ops().
* Return 1 if succeeded, 0 if not.
*/
int quic_reuse_srv_params(struct quic_conn *qc,
TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
- if (!alpn || !quic_set_app_ops(qc, alpn, strlen(alpn)))
+ if (!alpn || !qc_register_alpn(qc, alpn, strlen(alpn)))
goto err;
qc_early_transport_params_reuse(qc, &qc->tx.params, etps);
LIST_INIT(&qc->rx.pkt_list);
qc->streams_by_id = EB_ROOT_UNIQUE;
+ qc->alpn = NULL;
/* Required to call free_quic_conn_cids() from quic_conn_release() */
qc->cids = NULL;
qc->xprt_ctx = NULL;
qc->conn = conn;
qc->qcc = NULL;
- qc->app_ops = NULL;
qc->strm_reject = NULL;
qc->path = NULL;
/* Check the alpn could be negotiated */
if (!qc_is_back(qc)) {
- if (!qc->app_ops) {
+ if (!qc->alpn) {
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
goto err;
qc->conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
if (!ssl_sock_get_alpn(qc->conn, ctx, &alpn, &alpn_len) ||
- !quic_set_app_ops(qc, alpn, alpn_len)) {
+ !qc_register_alpn(qc, alpn, alpn_len)) {
TRACE_ERROR("No negotiated ALPN", QUIC_EV_CONN_IO_CB, qc, &state);
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
goto err;
}
#ifdef USE_QUIC
- if (qc && !quic_set_app_ops(qc, (const char *)*out, *outlen)) {
+ if (qc && !qc_register_alpn(qc, (const char *)*out, *outlen)) {
quic_set_tls_alert(qc, SSL_AD_NO_APPLICATION_PROTOCOL);
return SSL_TLSEXT_ERR_NOACK;
}
quic_dump_qc_info(msg, conn->handle.qc);
}
+static int qc_get_alpn(const struct connection *conn, void *xprt_ctx, const char **str, int *len)
+{
+ struct quic_conn *qc = conn->handle.qc;
+ int ret = 0;
+
+ TRACE_ENTER(QUIC_EV_CONN_NEW, qc);
+ if (qc->alpn) {
+ *str = qc->alpn;
+ *len = strlen(qc->alpn);
+ ret = 1;
+ }
+
+ TRACE_LEAVE(QUIC_EV_CONN_NEW, qc);
+ return ret;
+}
+
/* transport-layer operations for QUIC connections. */
static struct xprt_ops ssl_quic = {
.close = quic_close,
.destroy_bind_conf = ssl_sock_destroy_bind_conf,
.prepare_srv = ssl_sock_prepare_srv_ctx,
.destroy_srv = ssl_sock_free_srv_ctx,
- .get_alpn = ssl_sock_get_alpn,
+ .get_alpn = qc_get_alpn,
.get_ssl_sock_ctx = qc_get_ssl_sock_ctx,
.dump_info = qc_xprt_dump_info,
.name = "QUIC",