MEDIUM: connections: Use both mux_proto and alpn to pick a mux

In conn_get_best_mux() and conn_get_best_mux_entry(), the mux name was
provided sometimes based on the "proto" directive, sometimes based on
the ALPN, but in any case, it was compared again the mux_proto_list
mux_proto field. This is not correct, as ALPN can be different from the
internal mux_proto. So enhance those functions so that they wll accept
an ALPN as well. If a mux_proto is provided, that will be used, if not,
and if an ALPN is provided, then that will be used, and compared against
the ALPN provided by the mux, if any.
This commit is contained in:
Olivier Houchard 2026-05-19 15:52:21 +02:00 committed by Amaury Denoyelle
parent 022681eca2
commit 6aab6d4e98
6 changed files with 25 additions and 18 deletions

View file

@ -668,6 +668,7 @@ void list_mux_proto(FILE *out);
*/
static inline const struct mux_proto_list *conn_get_best_mux_entry(
const struct ist mux_proto,
const struct ist alpn,
int proto_side, int proto_is_quic, int proto_mode)
{
struct mux_proto_list *item;
@ -679,6 +680,10 @@ static inline const struct mux_proto_list *conn_get_best_mux_entry(
if (istlen(mux_proto) && isteq(mux_proto, item->mux_proto)) {
return item;
}
else if (istlen(alpn) && item->alpn &&
strlen(item->alpn) == istlen(alpn) + 1 &&
!memcmp(alpn.ptr, item->alpn + 1, istlen(alpn)))
return item;
else if (!istlen(item->mux_proto)) {
if (!fallback || (item->mode == proto_mode && fallback->mode != proto_mode))
fallback = item;
@ -696,11 +701,12 @@ static inline const struct mux_proto_list *conn_get_best_mux_entry(
*/
static inline const struct mux_ops *conn_get_best_mux(struct connection *conn,
const struct ist mux_proto,
const struct ist alpn,
int proto_side, int proto_mode)
{
const struct mux_proto_list *item;
item = conn_get_best_mux_entry(mux_proto, proto_side, proto_is_quic(conn->ctrl), proto_mode);
item = conn_get_best_mux_entry(mux_proto, alpn, proto_side, proto_is_quic(conn->ctrl), proto_mode);
return item ? item->mux : NULL;
}

View file

@ -282,6 +282,7 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
struct ist mux_proto, int mode)
{
struct bind_conf *bind_conf = __objt_listener(conn->target)->bind_conf;
struct ist alpn = IST_NULL;
const struct mux_ops *old_mux, *new_mux;
void *old_mux_ctx;
const char *alpn_str = NULL;
@ -289,9 +290,9 @@ int conn_upgrade_mux_fe(struct connection *conn, void *ctx, struct buffer *buf,
if (!mux_proto.len) {
conn_get_alpn(conn, &alpn_str, &alpn_len);
mux_proto = ist2(alpn_str, alpn_len);
alpn = ist2(alpn_str, alpn_len);
}
new_mux = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
new_mux = conn_get_best_mux(conn, mux_proto, alpn, PROTO_SIDE_FE, mode);
old_mux = conn->mux;
/* No mux found */
@ -330,14 +331,14 @@ int conn_install_mux_fe(struct connection *conn, void *ctx)
if (bind_conf->mux_proto)
mux_ops = bind_conf->mux_proto->mux;
else {
struct ist mux_proto;
struct ist alpn;
const char *alpn_str = NULL;
int alpn_len = 0;
int mode = conn_pr_mode_to_proto_mode(bind_conf->frontend->mode);
conn_get_alpn(conn, &alpn_str, &alpn_len);
mux_proto = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_FE, mode);
alpn = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_FE, mode);
if (!mux_ops)
return -1;
}
@ -380,7 +381,7 @@ int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess
mux_ops = force_mux_ops;
}
else {
struct ist mux_proto;
struct ist alpn;
const char *alpn_str = NULL;
int alpn_len = 0;
int mode = conn_pr_mode_to_proto_mode(prx->mode);
@ -391,9 +392,9 @@ int conn_install_mux_be(struct connection *conn, void *ctx, struct session *sess
alpn_len = strlen(alpn_str);
}
}
mux_proto = ist2(alpn_str, alpn_len);
alpn = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode);
if (!mux_ops)
return -1;
}
@ -434,15 +435,15 @@ int conn_install_mux_chk(struct connection *conn, void *ctx, struct session *ses
if (check->mux_proto)
mux_ops = check->mux_proto->mux;
else {
struct ist mux_proto;
struct ist alpn;
const char *alpn_str = NULL;
int alpn_len = 0;
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
conn_get_alpn(conn, &alpn_str, &alpn_len);
mux_proto = ist2(alpn_str, alpn_len);
alpn = ist2(alpn_str, alpn_len);
mux_ops = conn_get_best_mux(conn, mux_proto, PROTO_SIDE_BE, mode);
mux_ops = conn_get_best_mux(conn, IST_NULL, alpn, PROTO_SIDE_BE, mode);
if (!mux_ops)
return -1;
}

View file

@ -1689,7 +1689,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
* due to the proxy's mode not being taken into account
* on first pass. Let's adjust it now.
*/
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->mux_proto, PROTO_SIDE_FE, is_quic, mode);
mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_FE, is_quic, mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, bind_conf->mux_proto->mux_proto)) {
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n",
@ -2849,7 +2849,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
* due to the proxy's mode not being taken into account
* on first pass. Let's adjust it now.
*/
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->mux_proto, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(newsrv), mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, newsrv->mux_proto->mux_proto)) {
ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n",

View file

@ -6278,7 +6278,7 @@ static int cli_parse_add_server(char **args, char *payload, struct appctx *appct
int proto_mode = conn_pr_mode_to_proto_mode(be->mode);
const struct mux_proto_list *mux_ent;
mux_ent = conn_get_best_mux_entry(srv->mux_proto->mux_proto, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
mux_ent = conn_get_best_mux_entry(srv->mux_proto->mux_proto, IST_NULL, PROTO_SIDE_BE, srv_is_quic(srv), proto_mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, srv->mux_proto->mux_proto)) {
ha_alert("MUX protocol is not usable for server.\n");

View file

@ -3268,7 +3268,7 @@ static int check_tcp_switch_stream_mode(struct act_rule *rule, struct proxy *px,
px->options |= PR_O_HTTP_UPG;
if (mux_proto) {
mux_ent = conn_get_best_mux_entry(mux_proto->mux_proto, PROTO_SIDE_FE, 0, mode);
mux_ent = conn_get_best_mux_entry(mux_proto->mux_proto, IST_NULL, PROTO_SIDE_FE, 0, mode);
if (!mux_ent || !isteq(mux_ent->mux_proto, mux_proto->mux_proto)) {
memprintf(err, "MUX protocol '%.*s' is not compatible with the selected mode",
(int)mux_proto->mux_proto.len, mux_proto->mux_proto.ptr);
@ -3276,7 +3276,7 @@ static int check_tcp_switch_stream_mode(struct act_rule *rule, struct proxy *px,
}
}
else {
mux_ent = conn_get_best_mux_entry(IST_NULL, PROTO_SIDE_FE, 0, mode);
mux_ent = conn_get_best_mux_entry(IST_NULL, IST_NULL, PROTO_SIDE_FE, 0, mode);
if (!mux_ent) {
memprintf(err, "Unable to find compatible MUX protocol with the selected mode");
return 0;

View file

@ -1570,7 +1570,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
else {
int mode = tcpchk_rules_type_to_proto_mode(check->tcpcheck->rs->flags);
mux_ops = conn_get_best_mux(conn, IST_NULL, PROTO_SIDE_BE, mode);
mux_ops = conn_get_best_mux(conn, IST_NULL, IST_NULL, PROTO_SIDE_BE, mode);
}
if (mux_ops && conn_install_mux(conn, mux_ops, check->sc, proxy, check->sess) < 0) {
TRACE_ERROR("failed to install mux", CHK_EV_TCPCHK_CONN|CHK_EV_TCPCHK_ERR, check);