diff --git a/Makefile b/Makefile index 26f995be5..a8c5a53b4 100644 --- a/Makefile +++ b/Makefile @@ -608,7 +608,7 @@ OPTIONS_OBJS += src/quic_conn.o src/mux_quic.o src/h3.o src/xprt_quic.o \ 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),) diff --git a/include/haproxy/quic_cli-t.h b/include/haproxy/quic_cli-t.h new file mode 100644 index 000000000..6f95899e2 --- /dev/null +++ b/include/haproxy/quic_cli-t.h @@ -0,0 +1,18 @@ +/* + * 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 */ diff --git a/src/quic_cli.c b/src/quic_cli.c new file mode 100644 index 000000000..2b9ba64d6 --- /dev/null +++ b/src/quic_cli.c @@ -0,0 +1,401 @@ +#include + +#include +#include +#include +#include +#include +#include + +/* 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); + diff --git a/src/quic_conn.c b/src/quic_conn.c index 1a8ed164e..60374da97 100644 --- a/src/quic_conn.c +++ b/src/quic_conn.c @@ -34,8 +34,6 @@ #include #include -#include -#include #include #include #include @@ -48,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -64,9 +63,6 @@ #include #include -/* 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[] = { { @@ -8013,395 +8009,6 @@ void qc_finalize_affinity_rebind(struct quic_conn *qc) 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;