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 <report_susp> 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.
This commit is contained in:
Amaury Denoyelle 2024-05-13 09:05:27 +02:00
parent a6993a669b
commit 216f70f989
7 changed files with 54 additions and 0 deletions

View File

@ -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 <number>
and memory consumption and can be adjusted according to an estimated round
time-trip. Each buffer is tune.bufsize.
tune.quic.frontend.glitches-threshold <number>
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 <timeout>
Sets the QUIC max_idle_timeout transport parameters in milliseconds for
frontends which determines the period of time after which a connection silently

View File

@ -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;

View File

@ -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 */

View File

@ -13,6 +13,7 @@
#include <haproxy/stconn.h>
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);

View File

@ -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 },

View File

@ -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,
};

View File

@ -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 <qcc> connection by <inc> 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 <qcc>. Set <bidi> 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;