Path: blob/main/crypto/openssl/ssl/quic/quic_trace.c
48261 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 <openssl/bio.h>10#include "../ssl_local.h"11#include "internal/quic_trace.h"12#include "internal/quic_ssl.h"13#include "internal/quic_channel.h"14#include "internal/quic_wire_pkt.h"15#include "internal/quic_wire.h"16#include "internal/ssl_unwrap.h"1718static const char *packet_type(int type)19{20switch (type) {21case QUIC_PKT_TYPE_INITIAL:22return "Initial";2324case QUIC_PKT_TYPE_0RTT:25return "0RTT";2627case QUIC_PKT_TYPE_HANDSHAKE:28return "Handshake";2930case QUIC_PKT_TYPE_RETRY:31return "Retry";3233case QUIC_PKT_TYPE_1RTT:34return "1RTT";3536case QUIC_PKT_TYPE_VERSION_NEG:37return "VersionNeg";3839default:40return "Unknown";41}42}4344/* Print a non-NUL terminated string to BIO */45static void put_str(BIO *bio, char *str, size_t slen)46{47size_t i;4849for (i = 0; i < slen; i++)50BIO_printf(bio, "%c", str[i]);51}5253static void put_data(BIO *bio, const uint8_t *data, size_t datalen)54{55size_t i;5657for (i = 0; i < datalen; i++)58BIO_printf(bio, "%02x", data[i]);59}6061static void put_conn_id(BIO *bio, QUIC_CONN_ID *id)62{63if (id->id_len == 0) {64BIO_puts(bio, "<zero length id>");65return;66}6768BIO_puts(bio, "0x");69put_data(bio, id->id, id->id_len);70}7172static void put_token(BIO *bio, const uint8_t *token, size_t token_len)73{74if (token_len == 0)75BIO_puts(bio, "<zero length token>");76else77put_data(bio, token, token_len);78}7980static int frame_ack(BIO *bio, PACKET *pkt)81{82OSSL_QUIC_FRAME_ACK ack;83OSSL_QUIC_ACK_RANGE *ack_ranges = NULL;84uint64_t total_ranges = 0;85uint64_t i;86int ret = 0;8788if (!ossl_quic_wire_peek_frame_ack_num_ranges(pkt, &total_ranges)89/* In case sizeof(uint64_t) > sizeof(size_t) */90|| total_ranges > SIZE_MAX / sizeof(ack_ranges[0])91|| (ack_ranges = OPENSSL_zalloc(sizeof(ack_ranges[0])92* (size_t)total_ranges)) == NULL)93return ret;9495ack.ack_ranges = ack_ranges;96ack.num_ack_ranges = (size_t)total_ranges;9798/* Ack delay exponent is 0, so we can get the raw delay time below */99if (!ossl_quic_wire_decode_frame_ack(pkt, 0, &ack, NULL))100goto end;101102BIO_printf(bio, " Largest acked: %llu\n",103(unsigned long long)ack.ack_ranges[0].end);104BIO_printf(bio, " Ack delay (raw) %llu\n",105(unsigned long long)ossl_time2ticks(ack.delay_time));106BIO_printf(bio, " Ack range count: %llu\n",107(unsigned long long)total_ranges - 1);108BIO_printf(bio, " First ack range: %llu\n",109(unsigned long long)(ack.ack_ranges[0].end110- ack.ack_ranges[0].start));111for (i = 1; i < total_ranges; i++) {112BIO_printf(bio, " Gap: %llu\n",113(unsigned long long)(ack.ack_ranges[i - 1].start114- ack.ack_ranges[i].end - 2));115BIO_printf(bio, " Ack range len: %llu\n",116(unsigned long long)(ack.ack_ranges[i].end117- ack.ack_ranges[i].start));118}119120ret = 1;121end:122OPENSSL_free(ack_ranges);123return ret;124}125126static int frame_reset_stream(BIO *bio, PACKET *pkt)127{128OSSL_QUIC_FRAME_RESET_STREAM frame_data;129130if (!ossl_quic_wire_decode_frame_reset_stream(pkt, &frame_data))131return 0;132133BIO_printf(bio, " Stream id: %llu\n",134(unsigned long long)frame_data.stream_id);135BIO_printf(bio, " App Protocol Error Code: %llu\n",136(unsigned long long)frame_data.app_error_code);137BIO_printf(bio, " Final size: %llu\n",138(unsigned long long)frame_data.final_size);139140return 1;141}142143static int frame_stop_sending(BIO *bio, PACKET *pkt)144{145OSSL_QUIC_FRAME_STOP_SENDING frame_data;146147if (!ossl_quic_wire_decode_frame_stop_sending(pkt, &frame_data))148return 0;149150BIO_printf(bio, " Stream id: %llu\n",151(unsigned long long)frame_data.stream_id);152BIO_printf(bio, " App Protocol Error Code: %llu\n",153(unsigned long long)frame_data.app_error_code);154155return 1;156}157158static int frame_crypto(BIO *bio, PACKET *pkt)159{160OSSL_QUIC_FRAME_CRYPTO frame_data;161162if (!ossl_quic_wire_decode_frame_crypto(pkt, 1, &frame_data))163return 0;164165BIO_printf(bio, " Offset: %llu\n", (unsigned long long)frame_data.offset);166BIO_printf(bio, " Len: %llu\n", (unsigned long long)frame_data.len);167168return 1;169}170171static int frame_new_token(BIO *bio, PACKET *pkt)172{173const uint8_t *token;174size_t token_len;175176if (!ossl_quic_wire_decode_frame_new_token(pkt, &token, &token_len))177return 0;178179BIO_puts(bio, " Token: ");180put_token(bio, token, token_len);181BIO_puts(bio, "\n");182183return 1;184}185186static int frame_stream(BIO *bio, PACKET *pkt, uint64_t frame_type)187{188189OSSL_QUIC_FRAME_STREAM frame_data;190191BIO_puts(bio, "Stream");192switch(frame_type) {193case OSSL_QUIC_FRAME_TYPE_STREAM:194BIO_puts(bio, "\n");195break;196197case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:198BIO_puts(bio, " (Fin)\n");199break;200201case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:202BIO_puts(bio, " (Len)\n");203break;204205case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:206BIO_puts(bio, " (Len, Fin)\n");207break;208209case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:210BIO_puts(bio, " (Off)\n");211break;212213case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:214BIO_puts(bio, " (Off, Fin)\n");215break;216217case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:218BIO_puts(bio, " (Off, Len)\n");219break;220221case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN:222BIO_puts(bio, " (Off, Len, Fin)\n");223break;224225default:226return 0;227}228229if (!ossl_quic_wire_decode_frame_stream(pkt, 1, &frame_data))230return 0;231232BIO_printf(bio, " Stream id: %llu\n",233(unsigned long long)frame_data.stream_id);234BIO_printf(bio, " Offset: %llu\n",235(unsigned long long)frame_data.offset);236/*237* It would be nice to find a way of passing the implicit length through238* to the msg_callback. But this is not currently possible.239*/240if (frame_data.has_explicit_len)241BIO_printf(bio, " Len: %llu\n", (unsigned long long)frame_data.len);242else243BIO_puts(bio, " Len: <implicit length>\n");244245return 1;246}247248static int frame_max_data(BIO *bio, PACKET *pkt)249{250uint64_t max_data = 0;251252if (!ossl_quic_wire_decode_frame_max_data(pkt, &max_data))253return 0;254255BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);256257return 1;258}259260static int frame_max_stream_data(BIO *bio, PACKET *pkt)261{262uint64_t stream_id = 0;263uint64_t max_stream_data = 0;264265if (!ossl_quic_wire_decode_frame_max_stream_data(pkt, &stream_id,266&max_stream_data))267return 0;268269BIO_printf(bio, " Max Stream Data: %llu\n",270(unsigned long long)max_stream_data);271272return 1;273}274275static int frame_max_streams(BIO *bio, PACKET *pkt)276{277uint64_t max_streams = 0;278279if (!ossl_quic_wire_decode_frame_max_streams(pkt, &max_streams))280return 0;281282BIO_printf(bio, " Max Streams: %llu\n", (unsigned long long)max_streams);283284return 1;285}286287static int frame_data_blocked(BIO *bio, PACKET *pkt)288{289uint64_t max_data = 0;290291if (!ossl_quic_wire_decode_frame_data_blocked(pkt, &max_data))292return 0;293294BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);295296return 1;297}298299static int frame_stream_data_blocked(BIO *bio, PACKET *pkt)300{301uint64_t stream_id = 0;302uint64_t max_data = 0;303304if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt, &stream_id,305&max_data))306return 0;307308BIO_printf(bio, " Stream id: %llu\n", (unsigned long long)stream_id);309BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);310311return 1;312}313314static int frame_streams_blocked(BIO *bio, PACKET *pkt)315{316uint64_t max_data = 0;317318if (!ossl_quic_wire_decode_frame_streams_blocked(pkt, &max_data))319return 0;320321BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);322323return 1;324}325326static int frame_new_conn_id(BIO *bio, PACKET *pkt)327{328OSSL_QUIC_FRAME_NEW_CONN_ID frame_data;329330if (!ossl_quic_wire_decode_frame_new_conn_id(pkt, &frame_data))331return 0;332333BIO_printf(bio, " Sequence Number: %llu\n",334(unsigned long long)frame_data.seq_num);335BIO_printf(bio, " Retire prior to: %llu\n",336(unsigned long long)frame_data.retire_prior_to);337BIO_puts(bio, " Connection id: ");338put_conn_id(bio, &frame_data.conn_id);339BIO_puts(bio, "\n Stateless Reset Token: ");340put_data(bio, frame_data.stateless_reset.token,341sizeof(frame_data.stateless_reset.token));342BIO_puts(bio, "\n");343344return 1;345}346347static int frame_retire_conn_id(BIO *bio, PACKET *pkt)348{349uint64_t seq_num;350351if (!ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num))352return 0;353354BIO_printf(bio, " Sequence Number: %llu\n", (unsigned long long)seq_num);355356return 1;357}358359static int frame_path_challenge(BIO *bio, PACKET *pkt)360{361uint64_t data = 0;362363if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &data))364return 0;365366BIO_printf(bio, " Data: %016llx\n", (unsigned long long)data);367368return 1;369}370371static int frame_path_response(BIO *bio, PACKET *pkt)372{373uint64_t data = 0;374375if (!ossl_quic_wire_decode_frame_path_response(pkt, &data))376return 0;377378BIO_printf(bio, " Data: %016llx\n", (unsigned long long)data);379380return 1;381}382383static int frame_conn_closed(BIO *bio, PACKET *pkt)384{385OSSL_QUIC_FRAME_CONN_CLOSE frame_data;386387if (!ossl_quic_wire_decode_frame_conn_close(pkt, &frame_data))388return 0;389390BIO_printf(bio, " Error Code: %llu\n",391(unsigned long long)frame_data.error_code);392BIO_puts(bio, " Reason: ");393put_str(bio, frame_data.reason, frame_data.reason_len);394BIO_puts(bio, "\n");395396return 1;397}398399static int trace_frame_data(BIO *bio, PACKET *pkt)400{401uint64_t frame_type;402403if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type, NULL))404return 0;405406switch (frame_type) {407case OSSL_QUIC_FRAME_TYPE_PING:408BIO_puts(bio, "Ping\n");409if (!ossl_quic_wire_decode_frame_ping(pkt))410return 0;411break;412413case OSSL_QUIC_FRAME_TYPE_PADDING:414BIO_puts(bio, "Padding\n");415ossl_quic_wire_decode_padding(pkt);416break;417418case OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN:419case OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN:420BIO_puts(bio, "Ack ");421if (frame_type == OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN)422BIO_puts(bio, " (with ECN)\n");423else424BIO_puts(bio, " (without ECN)\n");425if (!frame_ack(bio, pkt))426return 0;427break;428429case OSSL_QUIC_FRAME_TYPE_RESET_STREAM:430BIO_puts(bio, "Reset stream\n");431if (!frame_reset_stream(bio, pkt))432return 0;433break;434435case OSSL_QUIC_FRAME_TYPE_STOP_SENDING:436BIO_puts(bio, "Stop sending\n");437if (!frame_stop_sending(bio, pkt))438return 0;439break;440441case OSSL_QUIC_FRAME_TYPE_CRYPTO:442BIO_puts(bio, "Crypto\n");443if (!frame_crypto(bio, pkt))444return 0;445break;446447case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN:448BIO_puts(bio, "New token\n");449if (!frame_new_token(bio, pkt))450return 0;451break;452453case OSSL_QUIC_FRAME_TYPE_STREAM:454case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:455case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:456case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:457case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:458case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:459case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:460case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN:461/* frame_stream() prints the frame type string */462if (!frame_stream(bio, pkt, frame_type))463return 0;464break;465466case OSSL_QUIC_FRAME_TYPE_MAX_DATA:467BIO_puts(bio, "Max data\n");468if (!frame_max_data(bio, pkt))469return 0;470break;471472case OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA:473BIO_puts(bio, "Max stream data\n");474if (!frame_max_stream_data(bio, pkt))475return 0;476break;477478case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI:479case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI:480BIO_puts(bio, "Max streams ");481if (frame_type == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI)482BIO_puts(bio, " (Bidi)\n");483else484BIO_puts(bio, " (Uni)\n");485if (!frame_max_streams(bio, pkt))486return 0;487break;488489case OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED:490BIO_puts(bio, "Data blocked\n");491if (!frame_data_blocked(bio, pkt))492return 0;493break;494495case OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED:496BIO_puts(bio, "Stream data blocked\n");497if (!frame_stream_data_blocked(bio, pkt))498return 0;499break;500501case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI:502case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI:503BIO_puts(bio, "Streams blocked");504if (frame_type == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI)505BIO_puts(bio, " (Bidi)\n");506else507BIO_puts(bio, " (Uni)\n");508if (!frame_streams_blocked(bio, pkt))509return 0;510break;511512case OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID:513BIO_puts(bio, "New conn id\n");514if (!frame_new_conn_id(bio, pkt))515return 0;516break;517518case OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID:519BIO_puts(bio, "Retire conn id\n");520if (!frame_retire_conn_id(bio, pkt))521return 0;522break;523524case OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE:525BIO_puts(bio, "Path challenge\n");526if (!frame_path_challenge(bio, pkt))527return 0;528break;529530case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE:531BIO_puts(bio, "Path response\n");532if (!frame_path_response(bio, pkt))533return 0;534break;535536case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP:537case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT:538BIO_puts(bio, "Connection close");539if (frame_type == OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP)540BIO_puts(bio, " (app)\n");541else542BIO_puts(bio, " (transport)\n");543if (!frame_conn_closed(bio, pkt))544return 0;545break;546547case OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE:548BIO_puts(bio, "Handshake done\n");549if (!ossl_quic_wire_decode_frame_handshake_done(pkt))550return 0;551break;552553default:554return 0;555}556557if (PACKET_remaining(pkt) != 0)558BIO_puts(bio, " <unexpected trailing frame data skipped>\n");559560return 1;561}562563int ossl_quic_trace(int write_p, int version, int content_type,564const void *buf, size_t msglen, SSL *ssl, void *arg)565{566BIO *bio = arg;567PACKET pkt;568size_t id_len = 0;569QUIC_CHANNEL *ch;570571switch (content_type) {572case SSL3_RT_QUIC_DATAGRAM:573BIO_puts(bio, write_p ? "Sent" : "Received");574/*575* Unfortunately there is no way of receiving auxiliary information576* about the datagram through the msg_callback API such as the peer577* address578*/579BIO_printf(bio, " Datagram\n Length: %zu\n", msglen);580break;581582case SSL3_RT_QUIC_PACKET:583{584QUIC_PKT_HDR hdr;585size_t i;586587if (!PACKET_buf_init(&pkt, buf, msglen))588return 0;589/* Decode the packet header */590ch = ossl_quic_conn_get_channel(ssl);591id_len = ossl_quic_channel_get_short_header_conn_id_len(ch);592if (ossl_quic_wire_decode_pkt_hdr(&pkt, id_len, 0, 1, &hdr, NULL,593NULL) != 1)594return 0;595596BIO_puts(bio, write_p ? "Sent" : "Received");597BIO_puts(bio, " Packet\n");598BIO_printf(bio, " Packet Type: %s\n", packet_type(hdr.type));599if (hdr.type != QUIC_PKT_TYPE_1RTT)600BIO_printf(bio, " Version: 0x%08lx\n",601(unsigned long)hdr.version);602BIO_puts(bio, " Destination Conn Id: ");603put_conn_id(bio, &hdr.dst_conn_id);604BIO_puts(bio, "\n");605if (hdr.type != QUIC_PKT_TYPE_1RTT) {606BIO_puts(bio, " Source Conn Id: ");607put_conn_id(bio, &hdr.src_conn_id);608BIO_puts(bio, "\n");609}610BIO_printf(bio, " Payload length: %zu\n", hdr.len);611if (hdr.type == QUIC_PKT_TYPE_INITIAL) {612BIO_puts(bio, " Token: ");613put_token(bio, hdr.token, hdr.token_len);614BIO_puts(bio, "\n");615}616if (hdr.type != QUIC_PKT_TYPE_VERSION_NEG617&& hdr.type != QUIC_PKT_TYPE_RETRY) {618BIO_puts(bio, " Packet Number: 0x");619/* Will always be at least 1 byte */620for (i = 0; i < hdr.pn_len; i++)621BIO_printf(bio, "%02x", hdr.pn[i]);622BIO_puts(bio, "\n");623}624break;625}626627case SSL3_RT_QUIC_FRAME_PADDING:628case SSL3_RT_QUIC_FRAME_FULL:629case SSL3_RT_QUIC_FRAME_HEADER:630{631BIO_puts(bio, write_p ? "Sent" : "Received");632BIO_puts(bio, " Frame: ");633634if (!PACKET_buf_init(&pkt, buf, msglen))635return 0;636if (!trace_frame_data(bio, &pkt)) {637BIO_puts(bio, " <error processing frame data>\n");638return 0;639}640}641break;642643default:644/* Unrecognised content_type. We defer to SSL_trace */645return 0;646}647648return 1;649}650651652