mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-10 19:58:06 +00:00
MINOR: quic: Export QUIC CLI code from quic_conn.c
To accelerate the compilation of quic_conn.c file, export the code in relation with the QUIC CLI from quic_conn.c to quic_cli.c.
This commit is contained in:
parent
6334f4f6c5
commit
953e67abb6
2
Makefile
2
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/qpack-dec.o src/hq_interop.o src/quic_stream.o \
|
||||||
src/h3_stats.o src/qmux_http.o src/cfgparse-quic.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/cbuf.o src/quic_cc.o src/quic_cc_nocc.o \
|
||||||
src/quic_trace.o
|
src/quic_trace.o src/quic_cli.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq ($(USE_QUIC_OPENSSL_COMPAT),)
|
ifneq ($(USE_QUIC_OPENSSL_COMPAT),)
|
||||||
|
18
include/haproxy/quic_cli-t.h
Normal file
18
include/haproxy/quic_cli-t.h
Normal file
@ -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 */
|
401
src/quic_cli.c
Normal file
401
src/quic_cli.c
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
#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);
|
||||||
|
|
395
src/quic_conn.c
395
src/quic_conn.c
@ -34,8 +34,6 @@
|
|||||||
#include <haproxy/ticks.h>
|
#include <haproxy/ticks.h>
|
||||||
#include <haproxy/xxhash.h>
|
#include <haproxy/xxhash.h>
|
||||||
|
|
||||||
#include <haproxy/applet-t.h>
|
|
||||||
#include <haproxy/cli.h>
|
|
||||||
#include <haproxy/connection.h>
|
#include <haproxy/connection.h>
|
||||||
#include <haproxy/fd.h>
|
#include <haproxy/fd.h>
|
||||||
#include <haproxy/freq_ctr.h>
|
#include <haproxy/freq_ctr.h>
|
||||||
@ -48,6 +46,7 @@
|
|||||||
#include <haproxy/pipe.h>
|
#include <haproxy/pipe.h>
|
||||||
#include <haproxy/proxy.h>
|
#include <haproxy/proxy.h>
|
||||||
#include <haproxy/quic_cc.h>
|
#include <haproxy/quic_cc.h>
|
||||||
|
#include <haproxy/quic_cli-t.h>
|
||||||
#include <haproxy/quic_frame.h>
|
#include <haproxy/quic_frame.h>
|
||||||
#include <haproxy/quic_enc.h>
|
#include <haproxy/quic_enc.h>
|
||||||
#include <haproxy/quic_loss.h>
|
#include <haproxy/quic_loss.h>
|
||||||
@ -64,9 +63,6 @@
|
|||||||
#include <haproxy/thread.h>
|
#include <haproxy/thread.h>
|
||||||
#include <haproxy/trace.h>
|
#include <haproxy/trace.h>
|
||||||
|
|
||||||
/* incremented by each "show quic". */
|
|
||||||
static unsigned int qc_epoch = 0;
|
|
||||||
|
|
||||||
/* list of supported QUIC versions by this implementation */
|
/* list of supported QUIC versions by this implementation */
|
||||||
const struct quic_version quic_versions[] = {
|
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);
|
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()
|
static void init_quic()
|
||||||
{
|
{
|
||||||
int thr;
|
int thr;
|
||||||
|
Loading…
Reference in New Issue
Block a user