Path: blob/main/crypto/openssl/ssl/quic/quic_trace.c
107264 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))93== NULL)94return ret;9596ack.ack_ranges = ack_ranges;97ack.num_ack_ranges = (size_t)total_ranges;9899/* Ack delay exponent is 0, so we can get the raw delay time below */100if (!ossl_quic_wire_decode_frame_ack(pkt, 0, &ack, NULL))101goto end;102103BIO_printf(bio, " Largest acked: %llu\n",104(unsigned long long)ack.ack_ranges[0].end);105BIO_printf(bio, " Ack delay (raw) %llu\n",106(unsigned long long)ossl_time2ticks(ack.delay_time));107BIO_printf(bio, " Ack range count: %llu\n",108(unsigned long long)total_ranges - 1);109BIO_printf(bio, " First ack range: %llu\n",110(unsigned long long)(ack.ack_ranges[0].end111- ack.ack_ranges[0].start));112for (i = 1; i < total_ranges; i++) {113BIO_printf(bio, " Gap: %llu\n",114(unsigned long long)(ack.ack_ranges[i - 1].start115- ack.ack_ranges[i].end - 2));116BIO_printf(bio, " Ack range len: %llu\n",117(unsigned long long)(ack.ack_ranges[i].end118- ack.ack_ranges[i].start));119}120121ret = 1;122end:123OPENSSL_free(ack_ranges);124return ret;125}126127static int frame_reset_stream(BIO *bio, PACKET *pkt)128{129OSSL_QUIC_FRAME_RESET_STREAM frame_data;130131if (!ossl_quic_wire_decode_frame_reset_stream(pkt, &frame_data))132return 0;133134BIO_printf(bio, " Stream id: %llu\n",135(unsigned long long)frame_data.stream_id);136BIO_printf(bio, " App Protocol Error Code: %llu\n",137(unsigned long long)frame_data.app_error_code);138BIO_printf(bio, " Final size: %llu\n",139(unsigned long long)frame_data.final_size);140141return 1;142}143144static int frame_stop_sending(BIO *bio, PACKET *pkt)145{146OSSL_QUIC_FRAME_STOP_SENDING frame_data;147148if (!ossl_quic_wire_decode_frame_stop_sending(pkt, &frame_data))149return 0;150151BIO_printf(bio, " Stream id: %llu\n",152(unsigned long long)frame_data.stream_id);153BIO_printf(bio, " App Protocol Error Code: %llu\n",154(unsigned long long)frame_data.app_error_code);155156return 1;157}158159static int frame_crypto(BIO *bio, PACKET *pkt)160{161OSSL_QUIC_FRAME_CRYPTO frame_data;162163if (!ossl_quic_wire_decode_frame_crypto(pkt, 1, &frame_data))164return 0;165166BIO_printf(bio, " Offset: %llu\n", (unsigned long long)frame_data.offset);167BIO_printf(bio, " Len: %llu\n", (unsigned long long)frame_data.len);168169return 1;170}171172static int frame_new_token(BIO *bio, PACKET *pkt)173{174const uint8_t *token;175size_t token_len;176177if (!ossl_quic_wire_decode_frame_new_token(pkt, &token, &token_len))178return 0;179180BIO_puts(bio, " Token: ");181put_token(bio, token, token_len);182BIO_puts(bio, "\n");183184return 1;185}186187static int frame_stream(BIO *bio, PACKET *pkt, uint64_t frame_type)188{189190OSSL_QUIC_FRAME_STREAM frame_data;191192BIO_puts(bio, "Stream");193switch (frame_type) {194case OSSL_QUIC_FRAME_TYPE_STREAM:195BIO_puts(bio, "\n");196break;197198case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:199BIO_puts(bio, " (Fin)\n");200break;201202case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:203BIO_puts(bio, " (Len)\n");204break;205206case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:207BIO_puts(bio, " (Len, Fin)\n");208break;209210case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:211BIO_puts(bio, " (Off)\n");212break;213214case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:215BIO_puts(bio, " (Off, Fin)\n");216break;217218case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:219BIO_puts(bio, " (Off, Len)\n");220break;221222case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN:223BIO_puts(bio, " (Off, Len, Fin)\n");224break;225226default:227return 0;228}229230if (!ossl_quic_wire_decode_frame_stream(pkt, 1, &frame_data))231return 0;232233BIO_printf(bio, " Stream id: %llu\n",234(unsigned long long)frame_data.stream_id);235BIO_printf(bio, " Offset: %llu\n",236(unsigned long long)frame_data.offset);237/*238* It would be nice to find a way of passing the implicit length through239* to the msg_callback. But this is not currently possible.240*/241if (frame_data.has_explicit_len)242BIO_printf(bio, " Len: %llu\n", (unsigned long long)frame_data.len);243else244BIO_puts(bio, " Len: <implicit length>\n");245246return 1;247}248249static int frame_max_data(BIO *bio, PACKET *pkt)250{251uint64_t max_data = 0;252253if (!ossl_quic_wire_decode_frame_max_data(pkt, &max_data))254return 0;255256BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);257258return 1;259}260261static int frame_max_stream_data(BIO *bio, PACKET *pkt)262{263uint64_t stream_id = 0;264uint64_t max_stream_data = 0;265266if (!ossl_quic_wire_decode_frame_max_stream_data(pkt, &stream_id,267&max_stream_data))268return 0;269270BIO_printf(bio, " Max Stream Data: %llu\n",271(unsigned long long)max_stream_data);272273return 1;274}275276static int frame_max_streams(BIO *bio, PACKET *pkt)277{278uint64_t max_streams = 0;279280if (!ossl_quic_wire_decode_frame_max_streams(pkt, &max_streams))281return 0;282283BIO_printf(bio, " Max Streams: %llu\n", (unsigned long long)max_streams);284285return 1;286}287288static int frame_data_blocked(BIO *bio, PACKET *pkt)289{290uint64_t max_data = 0;291292if (!ossl_quic_wire_decode_frame_data_blocked(pkt, &max_data))293return 0;294295BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);296297return 1;298}299300static int frame_stream_data_blocked(BIO *bio, PACKET *pkt)301{302uint64_t stream_id = 0;303uint64_t max_data = 0;304305if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt, &stream_id,306&max_data))307return 0;308309BIO_printf(bio, " Stream id: %llu\n", (unsigned long long)stream_id);310BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);311312return 1;313}314315static int frame_streams_blocked(BIO *bio, PACKET *pkt)316{317uint64_t max_data = 0;318319if (!ossl_quic_wire_decode_frame_streams_blocked(pkt, &max_data))320return 0;321322BIO_printf(bio, " Max Data: %llu\n", (unsigned long long)max_data);323324return 1;325}326327static int frame_new_conn_id(BIO *bio, PACKET *pkt)328{329OSSL_QUIC_FRAME_NEW_CONN_ID frame_data;330331if (!ossl_quic_wire_decode_frame_new_conn_id(pkt, &frame_data))332return 0;333334BIO_printf(bio, " Sequence Number: %llu\n",335(unsigned long long)frame_data.seq_num);336BIO_printf(bio, " Retire prior to: %llu\n",337(unsigned long long)frame_data.retire_prior_to);338BIO_puts(bio, " Connection id: ");339put_conn_id(bio, &frame_data.conn_id);340BIO_puts(bio, "\n Stateless Reset Token: ");341put_data(bio, frame_data.stateless_reset.token,342sizeof(frame_data.stateless_reset.token));343BIO_puts(bio, "\n");344345return 1;346}347348static int frame_retire_conn_id(BIO *bio, PACKET *pkt)349{350uint64_t seq_num;351352if (!ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num))353return 0;354355BIO_printf(bio, " Sequence Number: %llu\n", (unsigned long long)seq_num);356357return 1;358}359360static int frame_path_challenge(BIO *bio, PACKET *pkt)361{362uint64_t data = 0;363364if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &data))365return 0;366367BIO_printf(bio, " Data: %016llx\n", (unsigned long long)data);368369return 1;370}371372static int frame_path_response(BIO *bio, PACKET *pkt)373{374uint64_t data = 0;375376if (!ossl_quic_wire_decode_frame_path_response(pkt, &data))377return 0;378379BIO_printf(bio, " Data: %016llx\n", (unsigned long long)data);380381return 1;382}383384static int frame_conn_closed(BIO *bio, PACKET *pkt)385{386OSSL_QUIC_FRAME_CONN_CLOSE frame_data;387388if (!ossl_quic_wire_decode_frame_conn_close(pkt, &frame_data))389return 0;390391BIO_printf(bio, " Error Code: %llu\n",392(unsigned long long)frame_data.error_code);393BIO_puts(bio, " Reason: ");394put_str(bio, frame_data.reason, frame_data.reason_len);395BIO_puts(bio, "\n");396397return 1;398}399400static int trace_frame_data(BIO *bio, PACKET *pkt)401{402uint64_t frame_type;403404if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type, NULL))405return 0;406407switch (frame_type) {408case OSSL_QUIC_FRAME_TYPE_PING:409BIO_puts(bio, "Ping\n");410if (!ossl_quic_wire_decode_frame_ping(pkt))411return 0;412break;413414case OSSL_QUIC_FRAME_TYPE_PADDING:415BIO_puts(bio, "Padding\n");416ossl_quic_wire_decode_padding(pkt);417break;418419case OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN:420case OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN:421BIO_puts(bio, "Ack ");422if (frame_type == OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN)423BIO_puts(bio, " (with ECN)\n");424else425BIO_puts(bio, " (without ECN)\n");426if (!frame_ack(bio, pkt))427return 0;428break;429430case OSSL_QUIC_FRAME_TYPE_RESET_STREAM:431BIO_puts(bio, "Reset stream\n");432if (!frame_reset_stream(bio, pkt))433return 0;434break;435436case OSSL_QUIC_FRAME_TYPE_STOP_SENDING:437BIO_puts(bio, "Stop sending\n");438if (!frame_stop_sending(bio, pkt))439return 0;440break;441442case OSSL_QUIC_FRAME_TYPE_CRYPTO:443BIO_puts(bio, "Crypto\n");444if (!frame_crypto(bio, pkt))445return 0;446break;447448case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN:449BIO_puts(bio, "New token\n");450if (!frame_new_token(bio, pkt))451return 0;452break;453454case OSSL_QUIC_FRAME_TYPE_STREAM:455case OSSL_QUIC_FRAME_TYPE_STREAM_FIN:456case OSSL_QUIC_FRAME_TYPE_STREAM_LEN:457case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN:458case OSSL_QUIC_FRAME_TYPE_STREAM_OFF:459case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN:460case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN:461case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN:462/* frame_stream() prints the frame type string */463if (!frame_stream(bio, pkt, frame_type))464return 0;465break;466467case OSSL_QUIC_FRAME_TYPE_MAX_DATA:468BIO_puts(bio, "Max data\n");469if (!frame_max_data(bio, pkt))470return 0;471break;472473case OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA:474BIO_puts(bio, "Max stream data\n");475if (!frame_max_stream_data(bio, pkt))476return 0;477break;478479case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI:480case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI:481BIO_puts(bio, "Max streams ");482if (frame_type == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI)483BIO_puts(bio, " (Bidi)\n");484else485BIO_puts(bio, " (Uni)\n");486if (!frame_max_streams(bio, pkt))487return 0;488break;489490case OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED:491BIO_puts(bio, "Data blocked\n");492if (!frame_data_blocked(bio, pkt))493return 0;494break;495496case OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED:497BIO_puts(bio, "Stream data blocked\n");498if (!frame_stream_data_blocked(bio, pkt))499return 0;500break;501502case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI:503case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI:504BIO_puts(bio, "Streams blocked");505if (frame_type == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI)506BIO_puts(bio, " (Bidi)\n");507else508BIO_puts(bio, " (Uni)\n");509if (!frame_streams_blocked(bio, pkt))510return 0;511break;512513case OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID:514BIO_puts(bio, "New conn id\n");515if (!frame_new_conn_id(bio, pkt))516return 0;517break;518519case OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID:520BIO_puts(bio, "Retire conn id\n");521if (!frame_retire_conn_id(bio, pkt))522return 0;523break;524525case OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE:526BIO_puts(bio, "Path challenge\n");527if (!frame_path_challenge(bio, pkt))528return 0;529break;530531case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE:532BIO_puts(bio, "Path response\n");533if (!frame_path_response(bio, pkt))534return 0;535break;536537case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP:538case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT:539BIO_puts(bio, "Connection close");540if (frame_type == OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP)541BIO_puts(bio, " (app)\n");542else543BIO_puts(bio, " (transport)\n");544if (!frame_conn_closed(bio, pkt))545return 0;546break;547548case OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE:549BIO_puts(bio, "Handshake done\n");550if (!ossl_quic_wire_decode_frame_handshake_done(pkt))551return 0;552break;553554default:555return 0;556}557558if (PACKET_remaining(pkt) != 0)559BIO_puts(bio, " <unexpected trailing frame data skipped>\n");560561return 1;562}563564int ossl_quic_trace(int write_p, int version, int content_type,565const void *buf, size_t msglen, SSL *ssl, void *arg)566{567BIO *bio = arg;568PACKET pkt;569size_t id_len = 0;570QUIC_CHANNEL *ch;571572switch (content_type) {573case SSL3_RT_QUIC_DATAGRAM:574BIO_puts(bio, write_p ? "Sent" : "Received");575/*576* Unfortunately there is no way of receiving auxiliary information577* about the datagram through the msg_callback API such as the peer578* address579*/580BIO_printf(bio, " Datagram\n Length: %zu\n", msglen);581break;582583case SSL3_RT_QUIC_PACKET: {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)594!= 1)595return 0;596597BIO_puts(bio, write_p ? "Sent" : "Received");598BIO_puts(bio, " Packet\n");599BIO_printf(bio, " Packet Type: %s\n", packet_type(hdr.type));600if (hdr.type != QUIC_PKT_TYPE_1RTT)601BIO_printf(bio, " Version: 0x%08lx\n",602(unsigned long)hdr.version);603BIO_puts(bio, " Destination Conn Id: ");604put_conn_id(bio, &hdr.dst_conn_id);605BIO_puts(bio, "\n");606if (hdr.type != QUIC_PKT_TYPE_1RTT) {607BIO_puts(bio, " Source Conn Id: ");608put_conn_id(bio, &hdr.src_conn_id);609BIO_puts(bio, "\n");610}611BIO_printf(bio, " Payload length: %zu\n", hdr.len);612if (hdr.type == QUIC_PKT_TYPE_INITIAL) {613BIO_puts(bio, " Token: ");614put_token(bio, hdr.token, hdr.token_len);615BIO_puts(bio, "\n");616}617if (hdr.type != QUIC_PKT_TYPE_VERSION_NEG618&& hdr.type != QUIC_PKT_TYPE_RETRY) {619BIO_puts(bio, " Packet Number: 0x");620/* Will always be at least 1 byte */621for (i = 0; i < hdr.pn_len; i++)622BIO_printf(bio, "%02x", hdr.pn[i]);623BIO_puts(bio, "\n");624}625break;626}627628case SSL3_RT_QUIC_FRAME_PADDING:629case SSL3_RT_QUIC_FRAME_FULL:630case SSL3_RT_QUIC_FRAME_HEADER: {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} break;641642default:643/* Unrecognised content_type. We defer to SSL_trace */644return 0;645}646647return 1;648}649650651