src/qpack-dec.o src/hq_interop.o src/quic_stream.o \
src/h3_stats.o src/qmux_http.o src/cfgparse-quic.o \
src/cbuf.o src/quic_cc.o src/quic_cc_nocc.o \
- src/quic_trace.o
+ src/quic_trace.o src/quic_cli.o
endif
ifneq ($(USE_QUIC_OPENSSL_COMPAT),)
--- /dev/null
+/*
+ * include/haproxy/quic_trace-t.h
+ * Definitions for QUIC CLI internal types, constants and flags.
+ *
+ * Copyright (C) 2023
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+#ifndef _HAPROXY_QUIC_CLI_T_H
+#define _HAPROXY_QUIC_CLI_T_H
+
+extern unsigned int qc_epoch;
+
+#endif /* _HAPROXY_QUIC_CLI_T_H */
--- /dev/null
+#include <import/eb64tree.h>
+
+#include <haproxy/applet-t.h>
+#include <haproxy/cli.h>
+#include <haproxy/list.h>
+#include <haproxy/tools.h>
+#include <haproxy/quic_conn-t.h>
+#include <haproxy/quic_tp.h>
+
+/* incremented by each "show quic". */
+unsigned int qc_epoch = 0;
+
+enum quic_dump_format {
+ QUIC_DUMP_FMT_ONELINE,
+ QUIC_DUMP_FMT_FULL,
+};
+
+/* appctx context used by "show quic" command */
+struct show_quic_ctx {
+ unsigned int epoch;
+ struct bref bref; /* back-reference to the quic-conn being dumped */
+ unsigned int thr;
+ int flags;
+ enum quic_dump_format format;
+};
+
+#define QC_CLI_FL_SHOW_ALL 0x1 /* show closing/draining connections */
+
+static int cli_parse_show_quic(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ struct show_quic_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
+ int argc = 2;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_OPER))
+ return 1;
+
+ ctx->epoch = _HA_ATOMIC_FETCH_ADD(&qc_epoch, 1);
+ ctx->thr = 0;
+ ctx->flags = 0;
+ ctx->format = QUIC_DUMP_FMT_ONELINE;
+
+ if (strcmp(args[argc], "oneline") == 0) {
+ /* format already used as default value */
+ ++argc;
+ }
+ else if (strcmp(args[argc], "full") == 0) {
+ ctx->format = QUIC_DUMP_FMT_FULL;
+ ++argc;
+ }
+
+ while (*args[argc]) {
+ if (strcmp(args[argc], "all") == 0)
+ ctx->flags |= QC_CLI_FL_SHOW_ALL;
+
+ ++argc;
+ }
+
+ LIST_INIT(&ctx->bref.users);
+
+ return 0;
+}
+
+/* Dump for "show quic" with "oneline" format. */
+static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
+{
+ char bufaddr[INET6_ADDRSTRLEN], bufport[6];
+ int ret;
+ unsigned char cid_len;
+
+ ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
+ qc->li->bind_conf->frontend->id);
+ chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
+
+ /* State */
+ if (qc->flags & QUIC_FL_CONN_CLOSING)
+ chunk_appendf(&trash, "CLOSE ");
+ else if (qc->flags & QUIC_FL_CONN_DRAINING)
+ chunk_appendf(&trash, "DRAIN ");
+ else if (qc->state < QUIC_HS_ST_COMPLETE)
+ chunk_appendf(&trash, "HDSHK ");
+ else
+ chunk_appendf(&trash, "ESTAB ");
+
+ /* Bytes in flight / Lost packets */
+ chunk_appendf(&trash, "%9llu %6llu %6llu ",
+ (ullong)qc->path->in_flight,
+ (ullong)qc->path->ifae_pkts,
+ (ullong)qc->path->loss.nb_lost_pkt);
+
+ /* Socket */
+ if (qc->local_addr.ss_family == AF_INET ||
+ qc->local_addr.ss_family == AF_INET6) {
+ addr_to_str(&qc->local_addr, bufaddr, sizeof(bufaddr));
+ port_to_str(&qc->local_addr, bufport, sizeof(bufport));
+ chunk_appendf(&trash, "%15s:%-5s ", bufaddr, bufport);
+
+ addr_to_str(&qc->peer_addr, bufaddr, sizeof(bufaddr));
+ port_to_str(&qc->peer_addr, bufport, sizeof(bufport));
+ chunk_appendf(&trash, "%15s:%-5s ", bufaddr, bufport);
+
+ }
+
+ /* CIDs */
+ for (cid_len = 0; cid_len < qc->scid.len; ++cid_len)
+ chunk_appendf(&trash, "%02x", qc->scid.data[cid_len]);
+
+ chunk_appendf(&trash, " ");
+ for (cid_len = 0; cid_len < qc->dcid.len; ++cid_len)
+ chunk_appendf(&trash, "%02x", qc->dcid.data[cid_len]);
+
+ chunk_appendf(&trash, "\n");
+}
+
+/* Dump for "show quic" with "full" format. */
+static void dump_quic_full(struct show_quic_ctx *ctx, struct quic_conn *qc)
+{
+ struct quic_pktns *pktns;
+ struct eb64_node *node;
+ struct qc_stream_desc *stream;
+ char bufaddr[INET6_ADDRSTRLEN], bufport[6];
+ int expire, i, addnl;
+ unsigned char cid_len;
+
+ addnl = 0;
+ /* CIDs */
+ chunk_appendf(&trash, "* %p[%02u]: scid=", qc, ctx->thr);
+ for (cid_len = 0; cid_len < qc->scid.len; ++cid_len)
+ chunk_appendf(&trash, "%02x", qc->scid.data[cid_len]);
+ while (cid_len++ < 20)
+ chunk_appendf(&trash, "..");
+
+ chunk_appendf(&trash, " dcid=");
+ for (cid_len = 0; cid_len < qc->dcid.len; ++cid_len)
+ chunk_appendf(&trash, "%02x", qc->dcid.data[cid_len]);
+ while (cid_len++ < 20)
+ chunk_appendf(&trash, "..");
+
+ chunk_appendf(&trash, "\n");
+
+ chunk_appendf(&trash, " loc. TPs:");
+ quic_transport_params_dump(&trash, qc, &qc->rx.params);
+ chunk_appendf(&trash, "\n");
+ chunk_appendf(&trash, " rem. TPs:");
+ quic_transport_params_dump(&trash, qc, &qc->tx.params);
+ chunk_appendf(&trash, "\n");
+
+ /* Connection state */
+ if (qc->flags & QUIC_FL_CONN_CLOSING)
+ chunk_appendf(&trash, " st=closing ");
+ else if (qc->flags & QUIC_FL_CONN_DRAINING)
+ chunk_appendf(&trash, " st=draining ");
+ else if (qc->state < QUIC_HS_ST_CONFIRMED)
+ chunk_appendf(&trash, " st=handshake ");
+ else
+ chunk_appendf(&trash, " st=opened ");
+
+ if (qc->mux_state == QC_MUX_NULL)
+ chunk_appendf(&trash, "mux=null ");
+ else if (qc->mux_state == QC_MUX_READY)
+ chunk_appendf(&trash, "mux=ready ");
+ else
+ chunk_appendf(&trash, "mux=released ");
+
+ expire = qc->idle_expire;
+ chunk_appendf(&trash, "expire=%02ds ",
+ TICKS_TO_MS(tick_remain(now_ms, expire)) / 1000);
+
+ chunk_appendf(&trash, "\n");
+
+ /* Socket */
+ chunk_appendf(&trash, " fd=%d", qc->fd);
+ if (qc->local_addr.ss_family == AF_INET ||
+ qc->local_addr.ss_family == AF_INET6) {
+ addr_to_str(&qc->local_addr, bufaddr, sizeof(bufaddr));
+ port_to_str(&qc->local_addr, bufport, sizeof(bufport));
+ chunk_appendf(&trash, " local_addr=%s:%s", bufaddr, bufport);
+
+ addr_to_str(&qc->peer_addr, bufaddr, sizeof(bufaddr));
+ port_to_str(&qc->peer_addr, bufport, sizeof(bufport));
+ chunk_appendf(&trash, " foreign_addr=%s:%s", bufaddr, bufport);
+ }
+
+ chunk_appendf(&trash, "\n");
+
+ /* Packet number spaces information */
+ pktns = qc->ipktns;
+ if (pktns) {
+ chunk_appendf(&trash, " [initl] rx.ackrng=%-6zu tx.inflight=%-6zu",
+ pktns->rx.arngs.sz, pktns->tx.in_flight);
+ }
+
+ pktns = qc->hpktns;
+ if (pktns) {
+ chunk_appendf(&trash, " [hndshk] rx.ackrng=%-6zu tx.inflight=%-6zu\n",
+ pktns->rx.arngs.sz, pktns->tx.in_flight);
+ }
+
+ pktns = qc->apktns;
+ if (pktns) {
+ chunk_appendf(&trash, " [01rtt] rx.ackrng=%-6zu tx.inflight=%-6zu\n",
+ pktns->rx.arngs.sz, pktns->tx.in_flight);
+ }
+
+ chunk_appendf(&trash, " srtt=%-4u rttvar=%-4u rttmin=%-4u ptoc=%-4u cwnd=%-6llu"
+ " mcwnd=%-6llu sentpkts=%-6llu lostpkts=%-6llu\n",
+ qc->path->loss.srtt >> 3, qc->path->loss.rtt_var >> 2,
+ qc->path->loss.rtt_min, qc->path->loss.pto_count, (ullong)qc->path->cwnd,
+ (ullong)qc->path->mcwnd, (ullong)qc->cntrs.sent_pkt, (ullong)qc->path->loss.nb_lost_pkt);
+
+ if (qc->cntrs.dropped_pkt) {
+ chunk_appendf(&trash, " droppkts=%-6llu", qc->cntrs.dropped_pkt);
+ addnl = 1;
+ }
+ if (qc->cntrs.dropped_pkt_bufoverrun) {
+ chunk_appendf(&trash, " dropbuff=%-6llu", qc->cntrs.dropped_pkt_bufoverrun);
+ addnl = 1;
+ }
+ if (qc->cntrs.dropped_parsing) {
+ chunk_appendf(&trash, " droppars=%-6llu", qc->cntrs.dropped_parsing);
+ addnl = 1;
+ }
+ if (qc->cntrs.socket_full) {
+ chunk_appendf(&trash, " sockfull=%-6llu", qc->cntrs.socket_full);
+ addnl = 1;
+ }
+ if (qc->cntrs.sendto_err) {
+ chunk_appendf(&trash, " sendtoerr=%-6llu", qc->cntrs.sendto_err);
+ addnl = 1;
+ }
+ if (qc->cntrs.sendto_err_unknown) {
+ chunk_appendf(&trash, " sendtounknerr=%-6llu", qc->cntrs.sendto_err);
+ addnl = 1;
+ }
+ if (qc->cntrs.conn_migration_done) {
+ chunk_appendf(&trash, " migrdone=%-6llu", qc->cntrs.conn_migration_done);
+ addnl = 1;
+ }
+ if (qc->cntrs.data_blocked) {
+ chunk_appendf(&trash, " datablocked=%-6llu", qc->cntrs.data_blocked);
+ addnl = 1;
+ }
+ if (qc->cntrs.stream_data_blocked) {
+ chunk_appendf(&trash, " sdatablocked=%-6llu", qc->cntrs.stream_data_blocked);
+ addnl = 1;
+ }
+ if (qc->cntrs.streams_blocked_bidi) {
+ chunk_appendf(&trash, " sblockebidi=%-6llu", qc->cntrs.streams_blocked_bidi);
+ addnl = 1;
+ }
+ if (qc->cntrs.streams_blocked_uni) {
+ chunk_appendf(&trash, " sblockeduni=%-6llu", qc->cntrs.streams_blocked_uni);
+ addnl = 1;
+ }
+ if (addnl)
+ chunk_appendf(&trash, "\n");
+
+ /* Streams */
+ node = eb64_first(&qc->streams_by_id);
+ i = 0;
+ while (node) {
+ stream = eb64_entry(node, struct qc_stream_desc, by_id);
+ node = eb64_next(node);
+
+ chunk_appendf(&trash, " | stream=%-8llu", (unsigned long long)stream->by_id.key);
+ chunk_appendf(&trash, " off=%-8llu ack=%-8llu",
+ (unsigned long long)stream->buf_offset,
+ (unsigned long long)stream->ack_offset);
+
+ if (!(++i % 3)) {
+ chunk_appendf(&trash, "\n");
+ i = 0;
+ }
+ }
+
+ chunk_appendf(&trash, "\n");
+}
+
+static int cli_io_handler_dump_quic(struct appctx *appctx)
+{
+ struct show_quic_ctx *ctx = appctx->svcctx;
+ struct stconn *sc = appctx_sc(appctx);
+ struct quic_conn *qc;
+
+ thread_isolate();
+
+ if (ctx->thr >= global.nbthread)
+ goto done;
+
+ /* FIXME: Don't watch the other side !*/
+ if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE)) {
+ /* If we're forced to shut down, we might have to remove our
+ * reference to the last stream being dumped.
+ */
+ if (!LIST_ISEMPTY(&ctx->bref.users))
+ LIST_DEL_INIT(&ctx->bref.users);
+ goto done;
+ }
+
+ chunk_reset(&trash);
+
+ if (!LIST_ISEMPTY(&ctx->bref.users)) {
+ /* Remove show_quic_ctx from previous quic_conn instance. */
+ LIST_DEL_INIT(&ctx->bref.users);
+ }
+ else if (!ctx->bref.ref) {
+ /* First invocation. */
+ ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
+
+ /* Print legend for oneline format. */
+ if (ctx->format == QUIC_DUMP_FMT_ONELINE) {
+ chunk_appendf(&trash, "# conn/frontend state "
+ "in_flight infl_p lost_p "
+ "Local Address Foreign Address "
+ "local & remote CIDs\n");
+ applet_putchk(appctx, &trash);
+ }
+ }
+
+ while (1) {
+ int done = 0;
+
+ if (ctx->bref.ref == &ha_thread_ctx[ctx->thr].quic_conns) {
+ /* If closing connections requested through "all", move
+ * to quic_conns_clo list after browsing quic_conns.
+ * Else move directly to the next quic_conns thread.
+ */
+ if (ctx->flags & QC_CLI_FL_SHOW_ALL) {
+ ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns_clo.n;
+ continue;
+ }
+
+ done = 1;
+ }
+ else if (ctx->bref.ref == &ha_thread_ctx[ctx->thr].quic_conns_clo) {
+ /* Closing list entirely browsed, go to next quic_conns
+ * thread.
+ */
+ done = 1;
+ }
+ else {
+ /* Retrieve next element of the current list. */
+ qc = LIST_ELEM(ctx->bref.ref, struct quic_conn *, el_th_ctx);
+ if ((int)(qc->qc_epoch - ctx->epoch) > 0)
+ done = 1;
+ }
+
+ if (done) {
+ ++ctx->thr;
+ if (ctx->thr >= global.nbthread)
+ break;
+ /* Switch to next thread quic_conns list. */
+ ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
+ continue;
+ }
+
+ switch (ctx->format) {
+ case QUIC_DUMP_FMT_FULL:
+ dump_quic_full(ctx, qc);
+ break;
+ case QUIC_DUMP_FMT_ONELINE:
+ dump_quic_oneline(ctx, qc);
+ break;
+ }
+
+ if (applet_putchk(appctx, &trash) == -1) {
+ /* Register show_quic_ctx to quic_conn instance. */
+ LIST_APPEND(&qc->back_refs, &ctx->bref.users);
+ goto full;
+ }
+
+ ctx->bref.ref = qc->el_th_ctx.n;
+ }
+
+ done:
+ thread_release();
+ return 1;
+
+ full:
+ thread_release();
+ return 0;
+}
+
+static void cli_release_show_quic(struct appctx *appctx)
+{
+ struct show_quic_ctx *ctx = appctx->svcctx;
+
+ if (ctx->thr < global.nbthread) {
+ thread_isolate();
+ if (!LIST_ISEMPTY(&ctx->bref.users))
+ LIST_DEL_INIT(&ctx->bref.users);
+ thread_release();
+ }
+}
+
+static struct cli_kw_list cli_kws = {{ }, {
+ { { "show", "quic", NULL }, "show quic [oneline|full] [all] : display quic connections status", cli_parse_show_quic, cli_io_handler_dump_quic, cli_release_show_quic },
+ {{},}
+}};
+
+INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
+
#include <haproxy/ticks.h>
#include <haproxy/xxhash.h>
-#include <haproxy/applet-t.h>
-#include <haproxy/cli.h>
#include <haproxy/connection.h>
#include <haproxy/fd.h>
#include <haproxy/freq_ctr.h>
#include <haproxy/pipe.h>
#include <haproxy/proxy.h>
#include <haproxy/quic_cc.h>
+#include <haproxy/quic_cli-t.h>
#include <haproxy/quic_frame.h>
#include <haproxy/quic_enc.h>
#include <haproxy/quic_loss.h>
#include <haproxy/thread.h>
#include <haproxy/trace.h>
-/* incremented by each "show quic". */
-static unsigned int qc_epoch = 0;
-
/* list of supported QUIC versions by this implementation */
const struct quic_version quic_versions[] = {
{
TRACE_LEAVE(QUIC_EV_CONN_SET_AFFINITY, qc);
}
-enum quic_dump_format {
- QUIC_DUMP_FMT_ONELINE,
- QUIC_DUMP_FMT_FULL,
-};
-
-/* appctx context used by "show quic" command */
-struct show_quic_ctx {
- unsigned int epoch;
- struct bref bref; /* back-reference to the quic-conn being dumped */
- unsigned int thr;
- int flags;
- enum quic_dump_format format;
-};
-
-#define QC_CLI_FL_SHOW_ALL 0x1 /* show closing/draining connections */
-
-static int cli_parse_show_quic(char **args, char *payload, struct appctx *appctx, void *private)
-{
- struct show_quic_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
- int argc = 2;
-
- if (!cli_has_level(appctx, ACCESS_LVL_OPER))
- return 1;
-
- ctx->epoch = _HA_ATOMIC_FETCH_ADD(&qc_epoch, 1);
- ctx->thr = 0;
- ctx->flags = 0;
- ctx->format = QUIC_DUMP_FMT_ONELINE;
-
- if (strcmp(args[argc], "oneline") == 0) {
- /* format already used as default value */
- ++argc;
- }
- else if (strcmp(args[argc], "full") == 0) {
- ctx->format = QUIC_DUMP_FMT_FULL;
- ++argc;
- }
-
- while (*args[argc]) {
- if (strcmp(args[argc], "all") == 0)
- ctx->flags |= QC_CLI_FL_SHOW_ALL;
-
- ++argc;
- }
-
- LIST_INIT(&ctx->bref.users);
-
- return 0;
-}
-
-/* Dump for "show quic" with "oneline" format. */
-static void dump_quic_oneline(struct show_quic_ctx *ctx, struct quic_conn *qc)
-{
- char bufaddr[INET6_ADDRSTRLEN], bufport[6];
- int ret;
- unsigned char cid_len;
-
- ret = chunk_appendf(&trash, "%p[%02u]/%-.12s ", qc, ctx->thr,
- qc->li->bind_conf->frontend->id);
- chunk_appendf(&trash, "%*s", 36 - ret, " "); /* align output */
-
- /* State */
- if (qc->flags & QUIC_FL_CONN_CLOSING)
- chunk_appendf(&trash, "CLOSE ");
- else if (qc->flags & QUIC_FL_CONN_DRAINING)
- chunk_appendf(&trash, "DRAIN ");
- else if (qc->state < QUIC_HS_ST_COMPLETE)
- chunk_appendf(&trash, "HDSHK ");
- else
- chunk_appendf(&trash, "ESTAB ");
-
- /* Bytes in flight / Lost packets */
- chunk_appendf(&trash, "%9llu %6llu %6llu ",
- (ullong)qc->path->in_flight,
- (ullong)qc->path->ifae_pkts,
- (ullong)qc->path->loss.nb_lost_pkt);
-
- /* Socket */
- if (qc->local_addr.ss_family == AF_INET ||
- qc->local_addr.ss_family == AF_INET6) {
- addr_to_str(&qc->local_addr, bufaddr, sizeof(bufaddr));
- port_to_str(&qc->local_addr, bufport, sizeof(bufport));
- chunk_appendf(&trash, "%15s:%-5s ", bufaddr, bufport);
-
- addr_to_str(&qc->peer_addr, bufaddr, sizeof(bufaddr));
- port_to_str(&qc->peer_addr, bufport, sizeof(bufport));
- chunk_appendf(&trash, "%15s:%-5s ", bufaddr, bufport);
-
- }
-
- /* CIDs */
- for (cid_len = 0; cid_len < qc->scid.len; ++cid_len)
- chunk_appendf(&trash, "%02x", qc->scid.data[cid_len]);
-
- chunk_appendf(&trash, " ");
- for (cid_len = 0; cid_len < qc->dcid.len; ++cid_len)
- chunk_appendf(&trash, "%02x", qc->dcid.data[cid_len]);
-
- chunk_appendf(&trash, "\n");
-}
-
-/* Dump for "show quic" with "full" format. */
-static void dump_quic_full(struct show_quic_ctx *ctx, struct quic_conn *qc)
-{
- struct quic_pktns *pktns;
- struct eb64_node *node;
- struct qc_stream_desc *stream;
- char bufaddr[INET6_ADDRSTRLEN], bufport[6];
- int expire, i, addnl;
- unsigned char cid_len;
-
- addnl = 0;
- /* CIDs */
- chunk_appendf(&trash, "* %p[%02u]: scid=", qc, ctx->thr);
- for (cid_len = 0; cid_len < qc->scid.len; ++cid_len)
- chunk_appendf(&trash, "%02x", qc->scid.data[cid_len]);
- while (cid_len++ < 20)
- chunk_appendf(&trash, "..");
-
- chunk_appendf(&trash, " dcid=");
- for (cid_len = 0; cid_len < qc->dcid.len; ++cid_len)
- chunk_appendf(&trash, "%02x", qc->dcid.data[cid_len]);
- while (cid_len++ < 20)
- chunk_appendf(&trash, "..");
-
- chunk_appendf(&trash, "\n");
-
- chunk_appendf(&trash, " loc. TPs:");
- quic_transport_params_dump(&trash, qc, &qc->rx.params);
- chunk_appendf(&trash, "\n");
- chunk_appendf(&trash, " rem. TPs:");
- quic_transport_params_dump(&trash, qc, &qc->tx.params);
- chunk_appendf(&trash, "\n");
-
- /* Connection state */
- if (qc->flags & QUIC_FL_CONN_CLOSING)
- chunk_appendf(&trash, " st=closing ");
- else if (qc->flags & QUIC_FL_CONN_DRAINING)
- chunk_appendf(&trash, " st=draining ");
- else if (qc->state < QUIC_HS_ST_CONFIRMED)
- chunk_appendf(&trash, " st=handshake ");
- else
- chunk_appendf(&trash, " st=opened ");
-
- if (qc->mux_state == QC_MUX_NULL)
- chunk_appendf(&trash, "mux=null ");
- else if (qc->mux_state == QC_MUX_READY)
- chunk_appendf(&trash, "mux=ready ");
- else
- chunk_appendf(&trash, "mux=released ");
-
- expire = qc->idle_expire;
- chunk_appendf(&trash, "expire=%02ds ",
- TICKS_TO_MS(tick_remain(now_ms, expire)) / 1000);
-
- chunk_appendf(&trash, "\n");
-
- /* Socket */
- chunk_appendf(&trash, " fd=%d", qc->fd);
- if (qc->local_addr.ss_family == AF_INET ||
- qc->local_addr.ss_family == AF_INET6) {
- addr_to_str(&qc->local_addr, bufaddr, sizeof(bufaddr));
- port_to_str(&qc->local_addr, bufport, sizeof(bufport));
- chunk_appendf(&trash, " local_addr=%s:%s", bufaddr, bufport);
-
- addr_to_str(&qc->peer_addr, bufaddr, sizeof(bufaddr));
- port_to_str(&qc->peer_addr, bufport, sizeof(bufport));
- chunk_appendf(&trash, " foreign_addr=%s:%s", bufaddr, bufport);
- }
-
- chunk_appendf(&trash, "\n");
-
- /* Packet number spaces information */
- pktns = qc->ipktns;
- if (pktns) {
- chunk_appendf(&trash, " [initl] rx.ackrng=%-6zu tx.inflight=%-6zu",
- pktns->rx.arngs.sz, pktns->tx.in_flight);
- }
-
- pktns = qc->hpktns;
- if (pktns) {
- chunk_appendf(&trash, " [hndshk] rx.ackrng=%-6zu tx.inflight=%-6zu\n",
- pktns->rx.arngs.sz, pktns->tx.in_flight);
- }
-
- pktns = qc->apktns;
- if (pktns) {
- chunk_appendf(&trash, " [01rtt] rx.ackrng=%-6zu tx.inflight=%-6zu\n",
- pktns->rx.arngs.sz, pktns->tx.in_flight);
- }
-
- chunk_appendf(&trash, " srtt=%-4u rttvar=%-4u rttmin=%-4u ptoc=%-4u cwnd=%-6llu"
- " mcwnd=%-6llu sentpkts=%-6llu lostpkts=%-6llu\n",
- qc->path->loss.srtt >> 3, qc->path->loss.rtt_var >> 2,
- qc->path->loss.rtt_min, qc->path->loss.pto_count, (ullong)qc->path->cwnd,
- (ullong)qc->path->mcwnd, (ullong)qc->cntrs.sent_pkt, (ullong)qc->path->loss.nb_lost_pkt);
-
- if (qc->cntrs.dropped_pkt) {
- chunk_appendf(&trash, " droppkts=%-6llu", qc->cntrs.dropped_pkt);
- addnl = 1;
- }
- if (qc->cntrs.dropped_pkt_bufoverrun) {
- chunk_appendf(&trash, " dropbuff=%-6llu", qc->cntrs.dropped_pkt_bufoverrun);
- addnl = 1;
- }
- if (qc->cntrs.dropped_parsing) {
- chunk_appendf(&trash, " droppars=%-6llu", qc->cntrs.dropped_parsing);
- addnl = 1;
- }
- if (qc->cntrs.socket_full) {
- chunk_appendf(&trash, " sockfull=%-6llu", qc->cntrs.socket_full);
- addnl = 1;
- }
- if (qc->cntrs.sendto_err) {
- chunk_appendf(&trash, " sendtoerr=%-6llu", qc->cntrs.sendto_err);
- addnl = 1;
- }
- if (qc->cntrs.sendto_err_unknown) {
- chunk_appendf(&trash, " sendtounknerr=%-6llu", qc->cntrs.sendto_err);
- addnl = 1;
- }
- if (qc->cntrs.conn_migration_done) {
- chunk_appendf(&trash, " migrdone=%-6llu", qc->cntrs.conn_migration_done);
- addnl = 1;
- }
- if (qc->cntrs.data_blocked) {
- chunk_appendf(&trash, " datablocked=%-6llu", qc->cntrs.data_blocked);
- addnl = 1;
- }
- if (qc->cntrs.stream_data_blocked) {
- chunk_appendf(&trash, " sdatablocked=%-6llu", qc->cntrs.stream_data_blocked);
- addnl = 1;
- }
- if (qc->cntrs.streams_blocked_bidi) {
- chunk_appendf(&trash, " sblockebidi=%-6llu", qc->cntrs.streams_blocked_bidi);
- addnl = 1;
- }
- if (qc->cntrs.streams_blocked_uni) {
- chunk_appendf(&trash, " sblockeduni=%-6llu", qc->cntrs.streams_blocked_uni);
- addnl = 1;
- }
- if (addnl)
- chunk_appendf(&trash, "\n");
-
- /* Streams */
- node = eb64_first(&qc->streams_by_id);
- i = 0;
- while (node) {
- stream = eb64_entry(node, struct qc_stream_desc, by_id);
- node = eb64_next(node);
-
- chunk_appendf(&trash, " | stream=%-8llu", (unsigned long long)stream->by_id.key);
- chunk_appendf(&trash, " off=%-8llu ack=%-8llu",
- (unsigned long long)stream->buf_offset,
- (unsigned long long)stream->ack_offset);
-
- if (!(++i % 3)) {
- chunk_appendf(&trash, "\n");
- i = 0;
- }
- }
-
- chunk_appendf(&trash, "\n");
-}
-
-static int cli_io_handler_dump_quic(struct appctx *appctx)
-{
- struct show_quic_ctx *ctx = appctx->svcctx;
- struct stconn *sc = appctx_sc(appctx);
- struct quic_conn *qc;
-
- thread_isolate();
-
- if (ctx->thr >= global.nbthread)
- goto done;
-
- /* FIXME: Don't watch the other side !*/
- if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE)) {
- /* If we're forced to shut down, we might have to remove our
- * reference to the last stream being dumped.
- */
- if (!LIST_ISEMPTY(&ctx->bref.users))
- LIST_DEL_INIT(&ctx->bref.users);
- goto done;
- }
-
- chunk_reset(&trash);
-
- if (!LIST_ISEMPTY(&ctx->bref.users)) {
- /* Remove show_quic_ctx from previous quic_conn instance. */
- LIST_DEL_INIT(&ctx->bref.users);
- }
- else if (!ctx->bref.ref) {
- /* First invocation. */
- ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
-
- /* Print legend for oneline format. */
- if (ctx->format == QUIC_DUMP_FMT_ONELINE) {
- chunk_appendf(&trash, "# conn/frontend state "
- "in_flight infl_p lost_p "
- "Local Address Foreign Address "
- "local & remote CIDs\n");
- applet_putchk(appctx, &trash);
- }
- }
-
- while (1) {
- int done = 0;
-
- if (ctx->bref.ref == &ha_thread_ctx[ctx->thr].quic_conns) {
- /* If closing connections requested through "all", move
- * to quic_conns_clo list after browsing quic_conns.
- * Else move directly to the next quic_conns thread.
- */
- if (ctx->flags & QC_CLI_FL_SHOW_ALL) {
- ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns_clo.n;
- continue;
- }
-
- done = 1;
- }
- else if (ctx->bref.ref == &ha_thread_ctx[ctx->thr].quic_conns_clo) {
- /* Closing list entirely browsed, go to next quic_conns
- * thread.
- */
- done = 1;
- }
- else {
- /* Retrieve next element of the current list. */
- qc = LIST_ELEM(ctx->bref.ref, struct quic_conn *, el_th_ctx);
- if ((int)(qc->qc_epoch - ctx->epoch) > 0)
- done = 1;
- }
-
- if (done) {
- ++ctx->thr;
- if (ctx->thr >= global.nbthread)
- break;
- /* Switch to next thread quic_conns list. */
- ctx->bref.ref = ha_thread_ctx[ctx->thr].quic_conns.n;
- continue;
- }
-
- switch (ctx->format) {
- case QUIC_DUMP_FMT_FULL:
- dump_quic_full(ctx, qc);
- break;
- case QUIC_DUMP_FMT_ONELINE:
- dump_quic_oneline(ctx, qc);
- break;
- }
-
- if (applet_putchk(appctx, &trash) == -1) {
- /* Register show_quic_ctx to quic_conn instance. */
- LIST_APPEND(&qc->back_refs, &ctx->bref.users);
- goto full;
- }
-
- ctx->bref.ref = qc->el_th_ctx.n;
- }
-
- done:
- thread_release();
- return 1;
-
- full:
- thread_release();
- return 0;
-}
-
-static void cli_release_show_quic(struct appctx *appctx)
-{
- struct show_quic_ctx *ctx = appctx->svcctx;
-
- if (ctx->thr < global.nbthread) {
- thread_isolate();
- if (!LIST_ISEMPTY(&ctx->bref.users))
- LIST_DEL_INIT(&ctx->bref.users);
- thread_release();
- }
-}
-
-static struct cli_kw_list cli_kws = {{ }, {
- { { "show", "quic", NULL }, "show quic [oneline|full] [all] : display quic connections status", cli_parse_show_quic, cli_io_handler_dump_quic, cli_release_show_quic },
- {{},}
-}};
-
-INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
-
static void init_quic()
{
int thr;