Path: blob/main/crypto/openssl/ssl/quic/qlog_event_helpers.c
108775 views
/*1* Copyright 2023-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89#include "internal/qlog_event_helpers.h"10#include "internal/common.h"11#include "internal/packet.h"12#include "internal/quic_channel.h"13#include "internal/quic_error.h"1415void ossl_qlog_event_connectivity_connection_started(QLOG *qlog,16const QUIC_CONN_ID *init_dcid)17{18#ifndef OPENSSL_NO_QLOG19QLOG_EVENT_BEGIN(qlog, connectivity, connection_started)20QLOG_STR("protocol", "quic");21QLOG_CID("dst_cid", init_dcid);22QLOG_EVENT_END()23#endif24}2526#ifndef OPENSSL_NO_QLOG27static const char *map_state_to_qlog(uint32_t state,28int handshake_complete,29int handshake_confirmed)30{31switch (state) {32default:33case QUIC_CHANNEL_STATE_IDLE:34return NULL;3536case QUIC_CHANNEL_STATE_ACTIVE:37if (handshake_confirmed)38return "handshake_confirmed";39else if (handshake_complete)40return "handshake_complete";41else42return "attempted";4344case QUIC_CHANNEL_STATE_TERMINATING_CLOSING:45return "closing";4647case QUIC_CHANNEL_STATE_TERMINATING_DRAINING:48return "draining";4950case QUIC_CHANNEL_STATE_TERMINATED:51return "closed";52}53}54#endif5556void ossl_qlog_event_connectivity_connection_state_updated(QLOG *qlog,57uint32_t old_state,58uint32_t new_state,59int handshake_complete,60int handshake_confirmed)61{62#ifndef OPENSSL_NO_QLOG63const char *state_s;6465QLOG_EVENT_BEGIN(qlog, connectivity, connection_state_updated)66state_s = map_state_to_qlog(new_state,67handshake_complete,68handshake_confirmed);6970if (state_s != NULL)71QLOG_STR("state", state_s);72QLOG_EVENT_END()73#endif74}7576#ifndef OPENSSL_NO_QLOG77static const char *quic_err_to_qlog(uint64_t error_code)78{79switch (error_code) {80case OSSL_QUIC_ERR_INTERNAL_ERROR:81return "internal_error";82case OSSL_QUIC_ERR_CONNECTION_REFUSED:83return "connection_refused";84case OSSL_QUIC_ERR_FLOW_CONTROL_ERROR:85return "flow_control_error";86case OSSL_QUIC_ERR_STREAM_LIMIT_ERROR:87return "stream_limit_error";88case OSSL_QUIC_ERR_STREAM_STATE_ERROR:89return "stream_state_error";90case OSSL_QUIC_ERR_FINAL_SIZE_ERROR:91return "final_size_error";92case OSSL_QUIC_ERR_FRAME_ENCODING_ERROR:93return "frame_encoding_error";94case OSSL_QUIC_ERR_TRANSPORT_PARAMETER_ERROR:95return "transport_parameter_error";96case OSSL_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR:97return "connection_id_limit_error";98case OSSL_QUIC_ERR_PROTOCOL_VIOLATION:99return "protocol_violation";100case OSSL_QUIC_ERR_INVALID_TOKEN:101return "invalid_token";102case OSSL_QUIC_ERR_APPLICATION_ERROR:103return "application_error";104case OSSL_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED:105return "crypto_buffer_exceeded";106case OSSL_QUIC_ERR_KEY_UPDATE_ERROR:107return "key_update_error";108case OSSL_QUIC_ERR_AEAD_LIMIT_REACHED:109return "aead_limit_reached";110case OSSL_QUIC_ERR_NO_VIABLE_PATH:111return "no_viable_path";112default:113return NULL;114}115}116#endif117118void ossl_qlog_event_connectivity_connection_closed(QLOG *qlog,119const QUIC_TERMINATE_CAUSE *tcause)120{121#ifndef OPENSSL_NO_QLOG122QLOG_EVENT_BEGIN(qlog, connectivity, connection_closed)123QLOG_STR("owner", tcause->remote ? "remote" : "local");124if (tcause->app) {125QLOG_U64("application_code", tcause->error_code);126} else {127const char *m = quic_err_to_qlog(tcause->error_code);128char ce[32];129130if (tcause->error_code >= OSSL_QUIC_ERR_CRYPTO_ERR_BEGIN131&& tcause->error_code <= OSSL_QUIC_ERR_CRYPTO_ERR_END) {132BIO_snprintf(ce, sizeof(ce), "crypto_error_0x%03llx",133(unsigned long long)tcause->error_code);134m = ce;135}136/* TODO(QLOG FUTURE): Consider adding ERR information in the output. */137138if (m != NULL)139QLOG_STR("connection_code", m);140else141QLOG_U64("connection_code", tcause->error_code);142}143144QLOG_STR_LEN("reason", tcause->reason, tcause->reason_len);145QLOG_EVENT_END()146#endif147}148149#ifndef OPENSSL_NO_QLOG150static const char *quic_pkt_type_to_qlog(uint32_t pkt_type)151{152switch (pkt_type) {153case QUIC_PKT_TYPE_INITIAL:154return "initial";155case QUIC_PKT_TYPE_HANDSHAKE:156return "handshake";157case QUIC_PKT_TYPE_0RTT:158return "0RTT";159case QUIC_PKT_TYPE_1RTT:160return "1RTT";161case QUIC_PKT_TYPE_VERSION_NEG:162return "version_negotiation";163case QUIC_PKT_TYPE_RETRY:164return "retry";165default:166return "unknown";167}168}169#endif170171void ossl_qlog_event_recovery_packet_lost(QLOG *qlog,172const QUIC_TXPIM_PKT *tpkt)173{174#ifndef OPENSSL_NO_QLOG175QLOG_EVENT_BEGIN(qlog, recovery, packet_lost)176QLOG_BEGIN("header")177QLOG_STR("packet_type", quic_pkt_type_to_qlog(tpkt->pkt_type));178if (ossl_quic_pkt_type_has_pn(tpkt->pkt_type))179QLOG_U64("packet_number", tpkt->ackm_pkt.pkt_num);180QLOG_END()181QLOG_EVENT_END()182#endif183}184185#ifndef OPENSSL_NO_QLOG186#define MAX_ACK_RANGES 32187188static void ignore_res(int x) { }189190/*191* For logging received packets, we need to parse all the frames in the packet192* to log them. We should do this separately to the RXDP code because we want to193* log the packet and its contents before we start to actually process it in194* case it causes an error. We also in general don't want to do other195* non-logging related work in the middle of an event logging transaction.196* Reparsing packet data allows us to meet these needs while avoiding the need197* to keep around bookkeeping data on what frames were in a packet, etc.198*199* For logging transmitted packets, we actually reuse the same code and reparse200* the outgoing packet's payload. This again has the advantage that we only log201* a packet when it is actually queued for transmission (and not if something202* goes wrong before then) while avoiding the need to keep around bookkeeping203* data on what frames it contained.204*/205static int log_frame_actual(QLOG *qlog_instance, PACKET *pkt,206size_t *need_skip)207{208uint64_t frame_type;209OSSL_QUIC_FRAME_ACK ack;210OSSL_QUIC_ACK_RANGE ack_ranges[MAX_ACK_RANGES];211uint64_t num_ranges, total_ranges;212size_t i;213PACKET orig_pkt = *pkt;214215if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type, NULL)) {216*need_skip = SIZE_MAX;217return 0;218}219220/*221* If something goes wrong decoding a frame we cannot log it as that frame222* as we need to know how to decode it in order to be able to do so, but in223* that case we log it as an unknown frame to assist with diagnosis.224*/225switch (frame_type) {226case OSSL_QUIC_FRAME_TYPE_PADDING:227QLOG_STR("frame_type", "padding");228QLOG_U64("payload_length",229ossl_quic_wire_decode_padding(pkt));230break;231case OSSL_QUIC_FRAME_TYPE_PING:232if (!ossl_quic_wire_decode_frame_ping(pkt))233goto unknown;234235QLOG_STR("frame_type", "ping");236break;237case OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN:238case OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN:239if (!ossl_quic_wire_peek_frame_ack_num_ranges(pkt, &num_ranges))240goto unknown;241242ack.ack_ranges = ack_ranges;243ack.num_ack_ranges = OSSL_NELEM(ack_ranges);244if (!ossl_quic_wire_decode_frame_ack(pkt, 3, &ack, &total_ranges))245goto unknown;246247QLOG_STR("frame_type", "ack");248QLOG_U64("ack_delay", ossl_time2ms(ack.delay_time));249if (ack.ecn_present) {250QLOG_U64("ect1", ack.ect0);251QLOG_U64("ect0", ack.ect1);252QLOG_U64("ce", ack.ecnce);253}254QLOG_BEGIN_ARRAY("acked_ranges");255for (i = 0; i < ack.num_ack_ranges; ++i) {256QLOG_BEGIN_ARRAY(NULL)257QLOG_U64(NULL, ack.ack_ranges[i].start);258if (ack.ack_ranges[i].end != ack.ack_ranges[i].start)259QLOG_U64(NULL, ack.ack_ranges[i].end);260QLOG_END_ARRAY()261}262QLOG_END_ARRAY()263break;264case OSSL_QUIC_FRAME_TYPE_RESET_STREAM: {265OSSL_QUIC_FRAME_RESET_STREAM f;266267if (!ossl_quic_wire_decode_frame_reset_stream(pkt, &f))268goto unknown;269270QLOG_STR("frame_type", "reset_stream");271QLOG_U64("stream_id", f.stream_id);272QLOG_U64("error_code", f.app_error_code);273QLOG_U64("final_size", f.final_size);274} break;275case OSSL_QUIC_FRAME_TYPE_STOP_SENDING: {276OSSL_QUIC_FRAME_STOP_SENDING f;277278if (!ossl_quic_wire_decode_frame_stop_sending(pkt, &f))279goto unknown;280281QLOG_STR("frame_type", "stop_sending");282QLOG_U64("stream_id", f.stream_id);283QLOG_U64("error_code", f.app_error_code);284} break;285case OSSL_QUIC_FRAME_TYPE_CRYPTO: {286OSSL_QUIC_FRAME_CRYPTO f;287288if (!ossl_quic_wire_decode_frame_crypto(pkt, 1, &f))289goto unknown;290291QLOG_STR("frame_type", "crypto");292QLOG_U64("offset", f.offset);293QLOG_U64("payload_length", f.len);294*need_skip += (size_t)f.len;295} break;296case OSSL_QUIC_FRAME_TYPE_STREAM:297case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:298case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:299case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:300case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:301case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:302case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:303case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN: {304OSSL_QUIC_FRAME_STREAM f;305306if (!ossl_quic_wire_decode_frame_stream(pkt, 1, &f))307goto unknown;308309QLOG_STR("frame_type", "stream");310QLOG_U64("stream_id", f.stream_id);311QLOG_U64("offset", f.offset);312QLOG_U64("payload_length", f.len);313QLOG_BOOL("explicit_length", f.has_explicit_len);314if (f.is_fin)315QLOG_BOOL("fin", 1);316*need_skip = f.has_explicit_len317? *need_skip + (size_t)f.len318: SIZE_MAX;319} break;320case OSSL_QUIC_FRAME_TYPE_MAX_DATA: {321uint64_t x;322323if (!ossl_quic_wire_decode_frame_max_data(pkt, &x))324goto unknown;325326QLOG_STR("frame_type", "max_data");327QLOG_U64("maximum", x);328} break;329case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI:330case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI: {331uint64_t x;332333if (!ossl_quic_wire_decode_frame_max_streams(pkt, &x))334goto unknown;335336QLOG_STR("frame_type", "max_streams");337QLOG_STR("stream_type",338frame_type == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI339? "bidirectional"340: "unidirectional");341QLOG_U64("maximum", x);342} break;343case OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA: {344uint64_t stream_id, max_data;345346if (!ossl_quic_wire_decode_frame_max_stream_data(pkt, &stream_id,347&max_data))348goto unknown;349350QLOG_STR("frame_type", "max_stream_data");351QLOG_U64("stream_id", stream_id);352QLOG_U64("maximum", max_data);353} break;354case OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE: {355uint64_t challenge;356357if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &challenge))358goto unknown;359360QLOG_STR("frame_type", "path_challenge");361} break;362case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE: {363uint64_t challenge;364365if (!ossl_quic_wire_decode_frame_path_response(pkt, &challenge))366goto unknown;367368QLOG_STR("frame_type", "path_response");369} break;370case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP:371case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT: {372OSSL_QUIC_FRAME_CONN_CLOSE f;373374if (!ossl_quic_wire_decode_frame_conn_close(pkt, &f))375goto unknown;376377QLOG_STR("frame_type", "connection_close");378QLOG_STR("error_space", f.is_app ? "application" : "transport");379QLOG_U64("error_code_value", f.error_code);380if (f.is_app)381QLOG_U64("error_code", f.error_code);382if (!f.is_app && f.frame_type != 0)383QLOG_U64("trigger_frame_type", f.frame_type);384QLOG_STR_LEN("reason", f.reason, f.reason_len);385} break;386case OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE: {387if (!ossl_quic_wire_decode_frame_handshake_done(pkt))388goto unknown;389390QLOG_STR("frame_type", "handshake_done");391} break;392case OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID: {393OSSL_QUIC_FRAME_NEW_CONN_ID f;394395if (!ossl_quic_wire_decode_frame_new_conn_id(pkt, &f))396goto unknown;397398QLOG_STR("frame_type", "new_connection_id");399QLOG_U64("sequence_number", f.seq_num);400QLOG_U64("retire_prior_to", f.retire_prior_to);401QLOG_CID("connection_id", &f.conn_id);402QLOG_BIN("stateless_reset_token",403f.stateless_reset.token,404sizeof(f.stateless_reset.token));405} break;406case OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID: {407uint64_t seq_num;408409if (!ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num))410goto unknown;411412QLOG_STR("frame_type", "retire_connection_id");413QLOG_U64("sequence_number", seq_num);414} break;415case OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED: {416uint64_t x;417418if (!ossl_quic_wire_decode_frame_data_blocked(pkt, &x))419goto unknown;420421QLOG_STR("frame_type", "data_blocked");422QLOG_U64("limit", x);423} break;424case OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED: {425uint64_t stream_id, x;426427if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt,428&stream_id,429&x))430goto unknown;431432QLOG_STR("frame_type", "stream_data_blocked");433QLOG_U64("stream_id", stream_id);434QLOG_U64("limit", x);435} break;436case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI:437case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI: {438uint64_t x;439440if (!ossl_quic_wire_decode_frame_streams_blocked(pkt, &x))441goto unknown;442443QLOG_STR("frame_type", "streams_blocked");444QLOG_STR("stream_type",445frame_type == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI446? "bidirectional"447: "unidirectional");448QLOG_U64("limit", x);449} break;450case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN: {451const unsigned char *token;452size_t token_len;453454if (!ossl_quic_wire_decode_frame_new_token(pkt, &token, &token_len))455goto unknown;456457QLOG_STR("frame_type", "new_token");458QLOG_BEGIN("token");459QLOG_BEGIN("raw");460QLOG_BIN("data", token, token_len);461QLOG_END();462QLOG_END();463} break;464default:465unknown:466QLOG_STR("frame_type", "unknown");467QLOG_U64("frame_type_value", frame_type);468469/*470* Can't continue scanning for frames in this case as the frame length471* is unknown. We log the entire body of the rest of the packet payload472* as the raw data of the frame.473*/474QLOG_BEGIN("raw");475QLOG_BIN("data", PACKET_data(&orig_pkt),476PACKET_remaining(&orig_pkt));477QLOG_END();478ignore_res(PACKET_forward(pkt, PACKET_remaining(pkt)));479break;480}481482return 1;483}484485static void log_frame(QLOG *qlog_instance, PACKET *pkt,486size_t *need_skip)487{488size_t rem_before, rem_after;489490rem_before = PACKET_remaining(pkt);491492if (!log_frame_actual(qlog_instance, pkt, need_skip))493return;494495rem_after = PACKET_remaining(pkt);496QLOG_U64("length", rem_before - rem_after);497}498499static int log_frames(QLOG *qlog_instance,500const OSSL_QTX_IOVEC *iovec,501size_t num_iovec)502{503size_t i;504PACKET pkt;505size_t need_skip = 0;506507for (i = 0; i < num_iovec; ++i) {508if (!PACKET_buf_init(&pkt, iovec[i].buf, iovec[i].buf_len))509return 0;510511while (PACKET_remaining(&pkt) > 0) {512if (need_skip > 0) {513size_t adv = need_skip;514515if (adv > PACKET_remaining(&pkt))516adv = PACKET_remaining(&pkt);517518if (!PACKET_forward(&pkt, adv))519return 0;520521need_skip -= adv;522continue;523}524525QLOG_BEGIN(NULL)526{527log_frame(qlog_instance, &pkt, &need_skip);528}529QLOG_END()530}531}532533return 1;534}535536static void log_packet(QLOG *qlog_instance,537const QUIC_PKT_HDR *hdr,538QUIC_PN pn,539const OSSL_QTX_IOVEC *iovec,540size_t num_iovec,541uint64_t datagram_id)542{543const char *type_s;544545QLOG_BEGIN("header")546type_s = quic_pkt_type_to_qlog(hdr->type);547if (type_s == NULL)548type_s = "unknown";549550QLOG_STR("packet_type", type_s);551if (ossl_quic_pkt_type_has_pn(hdr->type))552QLOG_U64("packet_number", pn);553554QLOG_CID("dcid", &hdr->dst_conn_id);555if (ossl_quic_pkt_type_has_scid(hdr->type))556QLOG_CID("scid", &hdr->src_conn_id);557558if (hdr->token_len > 0) {559QLOG_BEGIN("token")560QLOG_BEGIN("raw")561QLOG_BIN("data", hdr->token, hdr->token_len);562QLOG_END()563QLOG_END()564}565/* TODO(QLOG FUTURE): flags, length */566QLOG_END()567QLOG_U64("datagram_id", datagram_id);568569if (ossl_quic_pkt_type_is_encrypted(hdr->type)) {570QLOG_BEGIN_ARRAY("frames")571log_frames(qlog_instance, iovec, num_iovec);572QLOG_END_ARRAY()573}574}575576#endif577578void ossl_qlog_event_transport_packet_sent(QLOG *qlog,579const QUIC_PKT_HDR *hdr,580QUIC_PN pn,581const OSSL_QTX_IOVEC *iovec,582size_t num_iovec,583uint64_t datagram_id)584{585#ifndef OPENSSL_NO_QLOG586QLOG_EVENT_BEGIN(qlog, transport, packet_sent)587log_packet(qlog, hdr, pn, iovec, num_iovec, datagram_id);588QLOG_EVENT_END()589#endif590}591592void ossl_qlog_event_transport_packet_received(QLOG *qlog,593const QUIC_PKT_HDR *hdr,594QUIC_PN pn,595const OSSL_QTX_IOVEC *iovec,596size_t num_iovec,597uint64_t datagram_id)598{599#ifndef OPENSSL_NO_QLOG600QLOG_EVENT_BEGIN(qlog, transport, packet_received)601log_packet(qlog, hdr, pn, iovec, num_iovec, datagram_id);602QLOG_EVENT_END()603#endif604}605606607