From: Amaury Denoyelle Date: Mon, 13 May 2024 07:05:27 +0000 (+0200) Subject: MINOR: mux-quic: support glitches X-Git-Tag: v3.0-dev12~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=216f70f9894c5915255bbecc8e68be91b157e3af;p=thirdparty%2Fhaproxy.git MINOR: mux-quic: support glitches Implement basic support for glitches on QUIC multiplexer. This is mostly identical too glitches for HTTP/2. A new configuration option named tune.quic.frontend.glitches-threshold is defined to limit the number of glitches on a connection before closing it. Glitches counter is incremented via qcc_report_glitch(). A new qcc_app_ops callback is defined. On threshold reaching, it allows to set an application error code to close the connection. For HTTP/3, value H3_EXCESSIVE_LOAD is returned. If not defined, default code INTERNAL_ERROR is used. For the moment, no glitch are reported for QUIC or HTTP/3 usage. This will be added in future patches as needed. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 27a616e6ba..bc7cd7f0af 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1406,6 +1406,7 @@ The following keywords are supported in the "global" section : - tune.pt.zero-copy-forwarding - tune.quic.cc-hystart - tune.quic.frontend.conn-tx-buffers.limit + - tune.quic.frontend.glitches-threshold - tune.quic.frontend.max-idle-timeout - tune.quic.frontend.max-streams-bidi - tune.quic.max-frame-loss @@ -3696,6 +3697,18 @@ tune.quic.frontend.conn-tx-buffers.limit and memory consumption and can be adjusted according to an estimated round time-trip. Each buffer is tune.bufsize. +tune.quic.frontend.glitches-threshold + Sets the threshold for the number of glitches on a frontend connection, where + that connection will automatically be killed. This allows to automatically + kill misbehaving connections without having to write explicit rules for them. + The default value is zero, indicating that no threshold is set so that no + event will cause a connection to be closed. Beware that some QUIC clients may + occasionally cause a few glitches over long lasting connection, so any non- + zero value here should probably be in the hundreds or thousands to be + effective without affecting slightly bogus clients. + + See also: fc_glitches + tune.quic.frontend.max-idle-timeout Sets the QUIC max_idle_timeout transport parameters in milliseconds for frontends which determines the period of time after which a connection silently diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 224d055564..a1357ac43d 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -196,6 +196,7 @@ struct global { #ifdef USE_QUIC unsigned int quic_backend_max_idle_timeout; unsigned int quic_frontend_max_idle_timeout; + unsigned int quic_frontend_glitches_threshold; unsigned int quic_frontend_max_streams_bidi; unsigned int quic_retry_threshold; unsigned int quic_reorder_ratio; diff --git a/include/haproxy/mux_quic-t.h b/include/haproxy/mux_quic-t.h index 92b8159ab0..02f8a72fe5 100644 --- a/include/haproxy/mux_quic-t.h +++ b/include/haproxy/mux_quic-t.h @@ -41,6 +41,7 @@ struct qcc { uint64_t nb_sc; /* number of attached stream connectors */ uint64_t nb_hreq; /* number of in-progress http requests */ uint32_t flags; /* QC_CF_* */ + int glitches; /* total number of glitches on this connection */ /* flow-control fields set by us enforced on our side. */ struct { @@ -216,6 +217,8 @@ struct qcc_app_ops { /* Increment app counters on CONNECTION_CLOSE_APP reception. */ void (*inc_err_cnt)(void *ctx, int err_code); + /* Set QCC error code as suspicious activity has been detected. */ + void (*report_susp)(void *ctx); }; #endif /* USE_QUIC */ diff --git a/include/haproxy/mux_quic.h b/include/haproxy/mux_quic.h index b1d216860d..1ed8ad1dc4 100644 --- a/include/haproxy/mux_quic.h +++ b/include/haproxy/mux_quic.h @@ -13,6 +13,7 @@ #include void qcc_set_error(struct qcc *qcc, int err, int app); +int qcc_report_glitch(struct qcc *qcc, int inc); struct qcs *qcc_init_stream_local(struct qcc *qcc, int bidi); struct stconn *qcs_attach_sc(struct qcs *qcs, struct buffer *buf, char fin); int qcs_is_close_local(struct qcs *qcs); diff --git a/src/cfgparse-quic.c b/src/cfgparse-quic.c index b2ab934d73..4a23bf20ec 100644 --- a/src/cfgparse-quic.c +++ b/src/cfgparse-quic.c @@ -235,6 +235,8 @@ static int cfg_parse_quic_tune_setting(char **args, int section_type, suffix = args[0] + prefix_len; if (strcmp(suffix, "frontend.conn-tx-buffers.limit") == 0) global.tune.quic_streams_buf = arg; + else if (strcmp(suffix, "frontend.glitches-threshold") == 0) + global.tune.quic_frontend_glitches_threshold = arg; else if (strcmp(suffix, "frontend.max-streams-bidi") == 0) global.tune.quic_frontend_max_streams_bidi = arg; else if (strcmp(suffix, "max-frame-loss") == 0) @@ -300,6 +302,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "tune.quic.backend.max-idle-timeou", cfg_parse_quic_time }, { CFG_GLOBAL, "tune.quic.cc-hystart", cfg_parse_quic_tune_on_off }, { CFG_GLOBAL, "tune.quic.frontend.conn-tx-buffers.limit", cfg_parse_quic_tune_setting }, + { CFG_GLOBAL, "tune.quic.frontend.glitches-threshold", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.max-streams-bidi", cfg_parse_quic_tune_setting }, { CFG_GLOBAL, "tune.quic.frontend.max-idle-timeout", cfg_parse_quic_time }, { CFG_GLOBAL, "tune.quic.max-frame-loss", cfg_parse_quic_tune_setting }, diff --git a/src/h3.c b/src/h3.c index 1373ca72e8..3073dfdbec 100644 --- a/src/h3.c +++ b/src/h3.c @@ -2399,6 +2399,12 @@ static void h3_stats_inc_err_cnt(void *ctx, int err_code) h3_inc_err_cnt(h3c->prx_counters, err_code); } +static void h3_report_susp(void *ctx) +{ + struct h3c *h3c = ctx; + h3c->qcc->err = quic_err_app(H3_ERR_EXCESSIVE_LOAD); +} + static inline const char *h3_ft_str(uint64_t type) { switch (type) { @@ -2455,5 +2461,6 @@ const struct qcc_app_ops h3_ops = { .detach = h3_detach, .shutdown = h3_shutdown, .inc_err_cnt = h3_stats_inc_err_cnt, + .report_susp = h3_report_susp, .release = h3_release, }; diff --git a/src/mux_quic.c b/src/mux_quic.c index 7d7420f35d..dcbe072b6d 100644 --- a/src/mux_quic.c +++ b/src/mux_quic.c @@ -556,6 +556,28 @@ void qcc_set_error(struct qcc *qcc, int err, int app) tasklet_wakeup(qcc->wait_event.tasklet); } +/* Increment glitch counter for connection by steps. If configured + * threshold reached, close the connection with an error code. + */ +int qcc_report_glitch(struct qcc *qcc, int inc) +{ + const int max = global.tune.quic_frontend_glitches_threshold; + + qcc->glitches += inc; + if (max && qcc->glitches >= max && !(qcc->flags & QC_CF_ERRL)) { + if (qcc->app_ops->report_susp) { + qcc->app_ops->report_susp(qcc->ctx); + qcc_set_error(qcc, qcc->err.code, 1); + } + else { + qcc_set_error(qcc, QC_ERR_INTERNAL_ERROR, 0); + } + return 1; + } + + return 0; +} + /* Open a locally initiated stream for the connection . Set for a * bidirectional stream, else an unidirectional stream is opened. The next * available ID on the connection will be used according to the stream type. @@ -2622,6 +2644,7 @@ static int qmux_init(struct connection *conn, struct proxy *prx, conn->ctx = qcc; qcc->nb_hreq = qcc->nb_sc = 0; qcc->flags = 0; + qcc->glitches = 0; qcc->err = quic_err_transport(QC_ERR_NO_ERROR); /* Server parameters, params used for RX flow control. */ @@ -3144,6 +3167,9 @@ static int qmux_ctl(struct connection *conn, enum mux_ctl_type mux_ctl, void *ou case MUX_CTL_EXIT_STATUS: return MUX_ES_UNKNOWN; + case MUX_CTL_GET_GLITCHES: + return qcc->glitches; + case MUX_CTL_GET_NBSTRM: { struct qcs *qcs; unsigned int nb_strm = qcc->nb_sc;