Path: blob/main/crypto/openssl/ssl/quic/qlog_event_helpers.c
48266 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:265{266OSSL_QUIC_FRAME_RESET_STREAM f;267268if (!ossl_quic_wire_decode_frame_reset_stream(pkt, &f))269goto unknown;270271QLOG_STR("frame_type", "reset_stream");272QLOG_U64("stream_id", f.stream_id);273QLOG_U64("error_code", f.app_error_code);274QLOG_U64("final_size", f.final_size);275}276break;277case OSSL_QUIC_FRAME_TYPE_STOP_SENDING:278{279OSSL_QUIC_FRAME_STOP_SENDING f;280281if (!ossl_quic_wire_decode_frame_stop_sending(pkt, &f))282goto unknown;283284QLOG_STR("frame_type", "stop_sending");285QLOG_U64("stream_id", f.stream_id);286QLOG_U64("error_code", f.app_error_code);287}288break;289case OSSL_QUIC_FRAME_TYPE_CRYPTO:290{291OSSL_QUIC_FRAME_CRYPTO f;292293if (!ossl_quic_wire_decode_frame_crypto(pkt, 1, &f))294goto unknown;295296QLOG_STR("frame_type", "crypto");297QLOG_U64("offset", f.offset);298QLOG_U64("payload_length", f.len);299*need_skip += (size_t)f.len;300}301break;302case OSSL_QUIC_FRAME_TYPE_STREAM:303case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:304case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:305case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:306case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:307case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:308case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:309case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN:310{311OSSL_QUIC_FRAME_STREAM f;312313if (!ossl_quic_wire_decode_frame_stream(pkt, 1, &f))314goto unknown;315316QLOG_STR("frame_type", "stream");317QLOG_U64("stream_id", f.stream_id);318QLOG_U64("offset", f.offset);319QLOG_U64("payload_length", f.len);320QLOG_BOOL("explicit_length", f.has_explicit_len);321if (f.is_fin)322QLOG_BOOL("fin", 1);323*need_skip = f.has_explicit_len324? *need_skip + (size_t)f.len : SIZE_MAX;325}326break;327case OSSL_QUIC_FRAME_TYPE_MAX_DATA:328{329uint64_t x;330331if (!ossl_quic_wire_decode_frame_max_data(pkt, &x))332goto unknown;333334QLOG_STR("frame_type", "max_data");335QLOG_U64("maximum", x);336}337break;338case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI:339case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI:340{341uint64_t x;342343if (!ossl_quic_wire_decode_frame_max_streams(pkt, &x))344goto unknown;345346QLOG_STR("frame_type", "max_streams");347QLOG_STR("stream_type",348frame_type == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI349? "bidirectional" : "unidirectional");350QLOG_U64("maximum", x);351}352break;353case OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA:354{355uint64_t stream_id, max_data;356357if (!ossl_quic_wire_decode_frame_max_stream_data(pkt, &stream_id,358&max_data))359goto unknown;360361QLOG_STR("frame_type", "max_stream_data");362QLOG_U64("stream_id", stream_id);363QLOG_U64("maximum", max_data);364}365break;366case OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE:367{368uint64_t challenge;369370if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &challenge))371goto unknown;372373QLOG_STR("frame_type", "path_challenge");374}375break;376case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE:377{378uint64_t challenge;379380if (!ossl_quic_wire_decode_frame_path_response(pkt, &challenge))381goto unknown;382383QLOG_STR("frame_type", "path_response");384}385break;386case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP:387case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT:388{389OSSL_QUIC_FRAME_CONN_CLOSE f;390391if (!ossl_quic_wire_decode_frame_conn_close(pkt, &f))392goto unknown;393394QLOG_STR("frame_type", "connection_close");395QLOG_STR("error_space", f.is_app ? "application" : "transport");396QLOG_U64("error_code_value", f.error_code);397if (f.is_app)398QLOG_U64("error_code", f.error_code);399if (!f.is_app && f.frame_type != 0)400QLOG_U64("trigger_frame_type", f.frame_type);401QLOG_STR_LEN("reason", f.reason, f.reason_len);402}403break;404case OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE:405{406if (!ossl_quic_wire_decode_frame_handshake_done(pkt))407goto unknown;408409QLOG_STR("frame_type", "handshake_done");410}411break;412case OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID:413{414OSSL_QUIC_FRAME_NEW_CONN_ID f;415416if (!ossl_quic_wire_decode_frame_new_conn_id(pkt, &f))417goto unknown;418419QLOG_STR("frame_type", "new_connection_id");420QLOG_U64("sequence_number", f.seq_num);421QLOG_U64("retire_prior_to", f.retire_prior_to);422QLOG_CID("connection_id", &f.conn_id);423QLOG_BIN("stateless_reset_token",424f.stateless_reset.token,425sizeof(f.stateless_reset.token));426}427break;428case OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID:429{430uint64_t seq_num;431432if (!ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num))433goto unknown;434435QLOG_STR("frame_type", "retire_connection_id");436QLOG_U64("sequence_number", seq_num);437}438break;439case OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED:440{441uint64_t x;442443if (!ossl_quic_wire_decode_frame_data_blocked(pkt, &x))444goto unknown;445446QLOG_STR("frame_type", "data_blocked");447QLOG_U64("limit", x);448}449break;450case OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED:451{452uint64_t stream_id, x;453454if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt,455&stream_id,456&x))457goto unknown;458459QLOG_STR("frame_type", "stream_data_blocked");460QLOG_U64("stream_id", stream_id);461QLOG_U64("limit", x);462}463break;464case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI:465case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI:466{467uint64_t x;468469if (!ossl_quic_wire_decode_frame_streams_blocked(pkt, &x))470goto unknown;471472QLOG_STR("frame_type", "streams_blocked");473QLOG_STR("stream_type",474frame_type == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI475? "bidirectional" : "unidirectional");476QLOG_U64("limit", x);477}478break;479case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN:480{481const unsigned char *token;482size_t token_len;483484if (!ossl_quic_wire_decode_frame_new_token(pkt, &token, &token_len))485goto unknown;486487QLOG_STR("frame_type", "new_token");488QLOG_BEGIN("token");489QLOG_BEGIN("raw");490QLOG_BIN("data", token, token_len);491QLOG_END();492QLOG_END();493}494break;495default:496unknown:497QLOG_STR("frame_type", "unknown");498QLOG_U64("frame_type_value", frame_type);499500/*501* Can't continue scanning for frames in this case as the frame length502* is unknown. We log the entire body of the rest of the packet payload503* as the raw data of the frame.504*/505QLOG_BEGIN("raw");506QLOG_BIN("data", PACKET_data(&orig_pkt),507PACKET_remaining(&orig_pkt));508QLOG_END();509ignore_res(PACKET_forward(pkt, PACKET_remaining(pkt)));510break;511}512513return 1;514}515516static void log_frame(QLOG *qlog_instance, PACKET *pkt,517size_t *need_skip)518{519size_t rem_before, rem_after;520521rem_before = PACKET_remaining(pkt);522523if (!log_frame_actual(qlog_instance, pkt, need_skip))524return;525526rem_after = PACKET_remaining(pkt);527QLOG_U64("length", rem_before - rem_after);528}529530static int log_frames(QLOG *qlog_instance,531const OSSL_QTX_IOVEC *iovec,532size_t num_iovec)533{534size_t i;535PACKET pkt;536size_t need_skip = 0;537538for (i = 0; i < num_iovec; ++i) {539if (!PACKET_buf_init(&pkt, iovec[i].buf, iovec[i].buf_len))540return 0;541542while (PACKET_remaining(&pkt) > 0) {543if (need_skip > 0) {544size_t adv = need_skip;545546if (adv > PACKET_remaining(&pkt))547adv = PACKET_remaining(&pkt);548549if (!PACKET_forward(&pkt, adv))550return 0;551552need_skip -= adv;553continue;554}555556QLOG_BEGIN(NULL)557{558log_frame(qlog_instance, &pkt, &need_skip);559}560QLOG_END()561}562}563564return 1;565}566567static void log_packet(QLOG *qlog_instance,568const QUIC_PKT_HDR *hdr,569QUIC_PN pn,570const OSSL_QTX_IOVEC *iovec,571size_t num_iovec,572uint64_t datagram_id)573{574const char *type_s;575576QLOG_BEGIN("header")577type_s = quic_pkt_type_to_qlog(hdr->type);578if (type_s == NULL)579type_s = "unknown";580581QLOG_STR("packet_type", type_s);582if (ossl_quic_pkt_type_has_pn(hdr->type))583QLOG_U64("packet_number", pn);584585QLOG_CID("dcid", &hdr->dst_conn_id);586if (ossl_quic_pkt_type_has_scid(hdr->type))587QLOG_CID("scid", &hdr->src_conn_id);588589if (hdr->token_len > 0) {590QLOG_BEGIN("token")591QLOG_BEGIN("raw")592QLOG_BIN("data", hdr->token, hdr->token_len);593QLOG_END()594QLOG_END()595}596/* TODO(QLOG FUTURE): flags, length */597QLOG_END()598QLOG_U64("datagram_id", datagram_id);599600if (ossl_quic_pkt_type_is_encrypted(hdr->type)) {601QLOG_BEGIN_ARRAY("frames")602log_frames(qlog_instance, iovec, num_iovec);603QLOG_END_ARRAY()604}605}606607#endif608609void ossl_qlog_event_transport_packet_sent(QLOG *qlog,610const QUIC_PKT_HDR *hdr,611QUIC_PN pn,612const OSSL_QTX_IOVEC *iovec,613size_t num_iovec,614uint64_t datagram_id)615{616#ifndef OPENSSL_NO_QLOG617QLOG_EVENT_BEGIN(qlog, transport, packet_sent)618log_packet(qlog, hdr, pn, iovec, num_iovec, datagram_id);619QLOG_EVENT_END()620#endif621}622623void ossl_qlog_event_transport_packet_received(QLOG *qlog,624const QUIC_PKT_HDR *hdr,625QUIC_PN pn,626const OSSL_QTX_IOVEC *iovec,627size_t num_iovec,628uint64_t datagram_id)629{630#ifndef OPENSSL_NO_QLOG631QLOG_EVENT_BEGIN(qlog, transport, packet_received)632log_packet(qlog, hdr, pn, iovec, num_iovec, datagram_id);633QLOG_EVENT_END()634#endif635}636637638