diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index aa7b17706..44f0ceb18 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -200,6 +200,8 @@ enum qcc_app_ops_close_side { /* QUIC application layer operations */ struct qcc_app_ops { + const char *alpn; + /* Initialize connection app context. */ int (*init)(struct qcc *qcc); /* Finish connection initialization if prelude required. */ diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h index b44ec2d6b..2c8086297 100644 --- a/include/haproxy/mux_quic.h +++ b/include/haproxy/mux_quic.h @@ -91,7 +91,7 @@ static inline char *qcs_st_to_str(enum qcs_state st) } } -int qcc_install_app_ops(struct qcc *qcc, const struct qcc_app_ops *app_ops); +int qcc_install_app_ops(struct qcc *qcc); /* Register stream for http-request timeout. If the stream is not yet * attached in the configured delay, qcc timeout task will be triggered. This diff --git a/include/haproxy/quic_conn-t.h b/include/haproxy/quic_conn-t.h index 3baad3fc1..0a7ce3951 100644 --- a/include/haproxy/quic_conn-t.h +++ b/include/haproxy/quic_conn-t.h @@ -400,6 +400,8 @@ struct quic_conn { struct eb_root streams_by_id; /* qc_stream_desc tree */ + const char *alpn; + /* MUX */ struct qcc *qcc; struct task *timer_task; @@ -408,7 +410,6 @@ struct quic_conn { /* 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); diff --git a/include/haproxy/quic_conn.h b/include/haproxy/quic_conn.h index c34681e7d..3424389a2 100644 --- a/include/haproxy/quic_conn.h +++ b/include/haproxy/quic_conn.h @@ -204,7 +204,7 @@ static inline void *qc_counters(enum obj_type *o, const struct stats_module *m) 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); diff --git a/src/h3.c b/src/h3.c index ebf1830e6..0b7c53303 100644 --- a/src/h3.c +++ b/src/h3.c @@ -3398,6 +3398,8 @@ int h3_reject(struct list *out, uint64_t id) /* HTTP/3 application layer operations */ const struct qcc_app_ops h3_ops = { + .alpn = "h3", + .init = h3_init, .finalize = h3_finalize, .attach = h3_attach, diff --git a/src/hq_interop.c b/src/hq_interop.c index cf1cf7c83..8268e62a7 100644 --- a/src/hq_interop.c +++ b/src/hq_interop.c @@ -324,6 +324,8 @@ static int hq_interop_attach(struct qcs *qcs, void *conn_ctx) } 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, diff --git a/src/mux_quic.c b/src/mux_quic.c index 512d0eda9..5b672b174 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -1688,26 +1688,38 @@ void qcc_abort_stream_read(struct qcs *qcs) TRACE_LEAVE(QMUX_EV_QCC_NEW, qcc->conn, qcs); } -/* Install the applicative layer of a QUIC connection on mux . +/* Install the applicative layer of a QUIC connection on mux . * 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; } @@ -3740,7 +3752,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx, /* 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; } diff --git a/src/quic_conn.c b/src/quic_conn.c index 2d87a42a9..26c67c7d4 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -267,21 +267,23 @@ void quic_set_tls_alert(struct quic_conn *qc, int alert) TRACE_LEAVE(QUIC_EV_CONN_SSLALERT, qc); } -/* Set the application for QUIC connection. - * Return 1 if succeeded, 0 if not. +/* Register the negotiated TLS ALPN of length for 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 and 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, @@ -292,7 +294,7 @@ 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); @@ -1124,6 +1126,7 @@ struct quic_conn *qc_new_conn(void *target, 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; @@ -1143,7 +1146,6 @@ struct quic_conn *qc_new_conn(void *target, qc->xprt_ctx = NULL; qc->conn = conn; qc->qcc = NULL; - qc->app_ops = NULL; qc->strm_reject = NULL; qc->path = NULL; diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 88f3268d0..710fef4e4 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -1004,7 +1004,7 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx) /* 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; @@ -1016,7 +1016,7 @@ int qc_ssl_do_hanshake(struct quic_conn *qc, struct ssl_sock_ctx *ctx) 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; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index ff266f0ea..aeb408ffa 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2242,7 +2242,7 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out, } #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; } diff --git a/src/xprt_quic.c b/src/xprt_quic.c index ca687f021..4670f6a86 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -239,6 +239,22 @@ static void qc_xprt_dump_info(struct buffer *msg, const struct connection *conn) 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, @@ -250,7 +266,7 @@ static struct xprt_ops ssl_quic = { .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",