From cbd59c7ab6dbc1b2f6896d3ceb433977eefb2f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= Date: Fri, 20 May 2022 08:11:26 +0200 Subject: [PATCH] MINOR: quic: QUIC stats counters handling First commit to handle the QUIC stats counters. There is nothing special to say except perhaps for ->conn_openings which is a gauge to count the number of connection openings. It is incremented after having instantiated a quic_conn struct, then decremented when the handshake was successful (handshake completed state) or failed or when the connection timed out without reaching the handshake completed state. --- include/haproxy/xprt_quic-t.h | 1 + src/xprt_quic.c | 97 ++++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 30 deletions(-) diff --git a/include/haproxy/xprt_quic-t.h b/include/haproxy/xprt_quic-t.h index e259c3f96..5ea31189f 100644 --- a/include/haproxy/xprt_quic-t.h +++ b/include/haproxy/xprt_quic-t.h @@ -685,6 +685,7 @@ enum qc_mux_state { #define QUIC_FL_CONN_IDLE_TIMER_RESTARTED_AFTER_READ (1U << 6) #define QUIC_FL_CONN_RETRANS_NEEDED (1U << 7) #define QUIC_FL_CONN_RETRANS_OLD_DATA (1U << 8) +#define QUIC_FL_CONN_TLS_ALERT (1U << 9) #define QUIC_FL_CONN_NOTIFY_CLOSE (1U << 27) /* MUX notified about quic-conn imminent closure (idle-timeout or CONNECTION_CLOSE emission/reception) */ #define QUIC_FL_CONN_EXP_TIMER (1U << 28) /* timer has expired, quic-conn can be freed */ #define QUIC_FL_CONN_CLOSING (1U << 29) diff --git a/src/xprt_quic.c b/src/xprt_quic.c index ebade7b2b..f73c51ae7 100644 --- a/src/xprt_quic.c +++ b/src/xprt_quic.c @@ -1111,8 +1111,9 @@ static int quic_crypto_data_cpy(struct quic_enc_level *qel, /* Set TLS alert as QUIC CRYPTO_ERROR error */ void quic_set_tls_alert(struct quic_conn *qc, int alert) { + HA_ATOMIC_DEC(&qc->prx_counters->conn_opening); qc->err_code = QC_ERR_CRYPTO_ERROR | alert; - qc->flags |= QUIC_FL_CONN_IMMEDIATE_CLOSE; + qc->flags |= QUIC_FL_CONN_IMMEDIATE_CLOSE | QUIC_FL_CONN_TLS_ALERT; TRACE_PROTO("Alert set", QUIC_EV_CONN_SSLDATA, qc); } @@ -2098,6 +2099,8 @@ static inline int qc_provide_cdata(struct quic_enc_level *el, goto out; } + HA_ATOMIC_INC(&qc->prx_counters->hdshk_fail); + HA_ATOMIC_DEC(&qc->prx_counters->conn_opening); TRACE_DEVEL("SSL handshake error", QUIC_EV_CONN_IO_CB, qc, &state, &ssl_err); qc_ssl_dump_errors(ctx->conn); @@ -2114,6 +2117,7 @@ static inline int qc_provide_cdata(struct quic_enc_level *el, goto err; } + HA_ATOMIC_DEC(&qc->prx_counters->conn_opening); /* I/O callback switch */ ctx->wait_event.tasklet->process = quic_conn_app_io_cb; if (qc_is_listener(ctx->qc)) { @@ -2591,10 +2595,18 @@ static int qc_parse_pkt_frms(struct quic_rx_packet *pkt, struct ssl_sock_ctx *ct break; case QUIC_FT_MAX_STREAMS_BIDI: case QUIC_FT_MAX_STREAMS_UNI: + break; case QUIC_FT_DATA_BLOCKED: + HA_ATOMIC_INC(&qc->prx_counters->data_blocked); + break; case QUIC_FT_STREAM_DATA_BLOCKED: + HA_ATOMIC_INC(&qc->prx_counters->stream_data_blocked); + break; case QUIC_FT_STREAMS_BLOCKED_BIDI: + HA_ATOMIC_INC(&qc->prx_counters->streams_data_blocked_bidi); + break; case QUIC_FT_STREAMS_BLOCKED_UNI: + HA_ATOMIC_INC(&qc->prx_counters->streams_data_blocked_uni); break; case QUIC_FT_NEW_CONNECTION_ID: case QUIC_FT_RETIRE_CONNECTION_ID: @@ -4511,6 +4523,9 @@ static void qc_idle_timer_rearm(struct quic_conn *qc, int read) static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int state) { struct quic_conn *qc = ctx; + struct quic_counters *prx_counters = qc->prx_counters; + int qc_state = qc->state; + unsigned int qc_flags = qc->flags; /* Notify the MUX before settings QUIC_FL_CONN_EXP_TIMER or the MUX * might free the quic-conn too early via quic_close(). @@ -4528,6 +4543,14 @@ static struct task *qc_idle_timer_task(struct task *t, void *ctx, unsigned int s * least clean some parts of it such as the tasklet. */ + /* If the connection did not reached the handshake complete state, + * the counter was not decremented. Note that if + * a TLS alert was received from the TLS stack, this counter + * has already been decremented. + */ + if (qc_state < QUIC_HS_ST_COMPLETE && !(qc_flags|QUIC_FL_CONN_TLS_ALERT)) + HA_ATOMIC_DEC(&prx_counters->conn_opening); + return NULL; } @@ -5197,8 +5220,10 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, unsigned char *beg, *payload; struct quic_conn *qc, *qc_to_purge = NULL; struct listener *l; + struct proxy *prx; + struct quic_counters *prx_counters; struct ssl_sock_ctx *conn_ctx; - int long_header = 0, io_cb_wakeup = 0; + int drop_no_conn = 0, long_header = 0, io_cb_wakeup = 0; size_t b_cspace; struct quic_enc_level *qel; @@ -5207,41 +5232,44 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, conn_ctx = NULL; qel = NULL; TRACE_ENTER(QUIC_EV_CONN_LPKT); + l = dgram->owner; + prx = l->bind_conf->frontend; + prx_counters = EXTRA_COUNTERS_GET(prx->extra_counters_fe, &quic_stats_module); /* This ist only to please to traces and distinguish the * packet with parsed packet number from others. */ pkt->pn_node.key = (uint64_t)-1; - if (end <= buf) - goto err; + if (end <= buf) { + TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); + goto drop; + } /* Fixed bit */ if (!(*buf & QUIC_PACKET_FIXED_BIT)) { /* XXX TO BE DISCARDED */ TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } - l = dgram->owner; /* Header form */ qc_parse_hd_form(pkt, *buf++, &long_header); if (long_header) { uint64_t len; - int drop_no_con = 0; struct quic_cid odcid; if (!quic_packet_read_long_header(&buf, end, pkt)) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } if (pkt->type == QUIC_PACKET_TYPE_0RTT && !l->bind_conf->ssl_conf.early_data) { TRACE_PROTO("0-RTT packet not supported", QUIC_EV_CONN_LPKT, qc); - drop_no_con = 1; + drop_no_conn = 1; } else if (pkt->type == QUIC_PACKET_TYPE_INITIAL && dgram->len < QUIC_INITIAL_PACKET_MINLEN) { TRACE_PROTO("Too short datagram with an Initial packet", QUIC_EV_CONN_LPKT, qc); - drop_no_con = 1; + drop_no_conn = 1; } /* When multiple QUIC packets are coalesced on the same UDP datagram, @@ -5251,13 +5279,13 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, (pkt->dcid.len != dgram->dcid_len || memcmp(dgram->dcid, pkt->dcid.data, pkt->dcid.len))) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc); - goto err; + goto drop; } /* Retry of Version Negotiation packets are only sent by servers */ if (pkt->type == QUIC_PACKET_TYPE_RETRY || !pkt->version) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } /* RFC9000 6. Version Negotiation */ @@ -5281,7 +5309,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (!quic_dec_int(&token_len, (const unsigned char **)&buf, end) || end - buf < token_len) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } /* TODO Retry should be automatically activated if @@ -5295,23 +5323,27 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, goto err; } - drop_no_con = 1; + HA_ATOMIC_INC(&prx_counters->retry_sent); + goto err; } else { if (*buf == QUIC_TOKEN_FMT_RETRY) { if (!quic_retry_token_check(buf, token_len, pkt->version, &odcid, &pkt->scid, qc, &dgram->saddr)) { + HA_ATOMIC_INC(&prx_counters->retry_error); TRACE_PROTO("Wrong retry token", QUIC_EV_CONN_LPKT); /* TODO: RFC 9000 8.1.2 A server SHOULD immediately close the connection * with an INVALID_TOKEN error. */ - goto err; + goto drop; } + + HA_ATOMIC_INC(&prx_counters->retry_validated); } else { /* TODO: New token check */ TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } } } @@ -5323,20 +5355,20 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, else if (pkt->type != QUIC_PACKET_TYPE_0RTT) { if (pkt->dcid.len != QUIC_HAP_CID_LEN) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } } if (!quic_dec_int(&len, (const unsigned char **)&buf, end) || end - buf < len) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } payload = buf; pkt->len = len + payload - beg; - if (drop_no_con) - goto drop_no_con; + if (drop_no_conn) + goto drop_no_conn; qc = retrieve_qc_conn_from_cid(pkt, l, &dgram->saddr); if (!qc) { @@ -5345,7 +5377,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (pkt->type != QUIC_PACKET_TYPE_INITIAL) { TRACE_PROTO("Non Initial packet", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } /* RFC 9000 7.2. Negotiating Connection IDs: @@ -5356,7 +5388,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, */ if (pkt->dcid.len < QUIC_ODCID_MINLEN) { TRACE_PROTO("dropped packet", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } pkt->saddr = dgram->saddr; @@ -5364,8 +5396,9 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, qc = qc_new_conn(pkt->version, ipv4, &pkt->dcid, &pkt->scid, &odcid, &pkt->saddr, pkt->token, pkt->token_len, 1, l); if (qc == NULL) - goto err; + goto drop; + HA_ATOMIC_INC(&prx_counters->conn_opening); /* Insert the DCID the QUIC client has chosen (only for listeners) */ n = ebmb_insert(&quic_dghdlrs[tid].odcids, &qc->odcid_node, qc->odcid.len + qc->odcid.addrlen); @@ -5396,7 +5429,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, else { if (end - buf < QUIC_HAP_CID_LEN) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT); - goto err; + goto drop; } memcpy(pkt->dcid.data, buf, QUIC_HAP_CID_LEN); @@ -5409,7 +5442,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, (pkt->dcid.len != dgram->dcid_len || memcmp(dgram->dcid, pkt->dcid.data, pkt->dcid.len))) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc); - goto err; + goto drop; } buf += QUIC_HAP_CID_LEN; @@ -5424,7 +5457,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, NULL, pkt, &pktlen); if (global.cluster_secret && !send_stateless_reset(l->rx.fd, &dgram->saddr, pkt)) TRACE_PROTO("stateless reset not sent", QUIC_EV_CONN_LPKT, qc); - goto err; + goto drop; } pkt->qc = qc; @@ -5439,7 +5472,7 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, /* Skip the entire datagram */ pkt->len = end - beg; TRACE_PROTO("Closing state connection", QUIC_EV_CONN_LPKT, pkt->qc); - goto out; + goto drop; } /* When multiple QUIC packets are coalesced on the same UDP datagram, @@ -5486,13 +5519,13 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (b_contig_space(&qc->rx.buf) < pkt->len) { TRACE_PROTO("Too big packet", QUIC_EV_CONN_LPKT, qc, pkt, &pkt->len); qc_list_all_rx_pkts(qc); - goto err; + goto drop; } } if (!qc_try_rm_hp(qc, pkt, payload, beg, end, &qel)) { TRACE_PROTO("Packet dropped", QUIC_EV_CONN_LPKT, qc); - goto err; + goto drop; } TRACE_PROTO("New packet", QUIC_EV_CONN_LPKT, qc, pkt); @@ -5509,11 +5542,15 @@ static void qc_lstnr_pkt_rcv(unsigned char *buf, const unsigned char *end, if (conn_ctx) tasklet_wakeup(conn_ctx->wait_event.tasklet); - drop_no_con: + drop_no_conn: + if (drop_no_conn) + HA_ATOMIC_INC(&prx_counters->dropped_pkt); TRACE_LEAVE(QUIC_EV_CONN_LPKT, qc ? qc : NULL, pkt); return; + drop: + HA_ATOMIC_INC(&prx_counters->dropped_pkt); err: /* Wakeup the I/O handler callback if the PTO timer must be armed. * This cannot be done by this thread.