]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-quic: support glitches
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 13 May 2024 07:05:27 +0000 (09:05 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 16 May 2024 08:58:20 +0000 (10:58 +0200)
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.

doc/configuration.txt
include/haproxy/global-t.h
include/haproxy/mux_quic-t.h
include/haproxy/mux_quic.h
src/cfgparse-quic.c
src/h3.c
src/mux_quic.c

index 27a616e6ba91dc57538d4a6a3c0365997960aabd..bc7cd7f0af7b91e73800b7039e91ca07ca47795f 100644 (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
index 224d055564dc2cfdaea79c69fd69ca671f0d8b2c..a1357ac43d4c05e7af3d304045836beb23134223 100644 (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;
index 92b8159ab0cb371702695117ed826e8dd0a19ce0..02f8a72fe59d7f387b9ad1cf288418ad32d87236 100644 (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 */
index b1d216860d0ffbaeef26bafde792d8f788110861..1ed8ad1dc49cf0a270e8d160ba392718e4cd1cc2 100644 (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);
index b2ab934d7387059d6f56301082dfae829d0944b6..4a23bf20ec3f791efdc851b1f1b4ae17039309c9 100644 (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 },
index 1373ca72e8227408f50b11b9b4ee63b7336286ff..3073dfdbec9fb1a7920e4c60fb4751ad4e1af7dd 100644 (file)
--- 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,
 };
index 7d7420f35de1435158254c19e5a51450cd380cf7..dcbe072b6d6267f18ff3b05ba0851def5e8588b3 100644 (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;