diff options
author | Matt Caswell <matt@openssl.org> | 2023-05-04 17:18:01 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2023-05-24 12:18:33 +0100 |
commit | 70f0ea280af0bed9fb48b20b61c7a12c7f03e6d9 (patch) | |
tree | b231b0f2ddf2b70ded070dbc2a5829541209e52b /ssl | |
parent | 2a35a1bec0845d314f06a88d703a9eb30dbed10e (diff) |
Support trace for QUIC Frames
Extend the existing QUIC tracing capability for frames.
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20914)
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/quic/quic_record_rx.c | 1 | ||||
-rw-r--r-- | ssl/quic/quic_rx_depack.c | 40 | ||||
-rw-r--r-- | ssl/quic/quic_trace.c | 428 |
3 files changed, 464 insertions, 5 deletions
diff --git a/ssl/quic/quic_record_rx.c b/ssl/quic/quic_record_rx.c index afc5011a49..40c76a6bc1 100644 --- a/ssl/quic/quic_record_rx.c +++ b/ssl/quic/quic_record_rx.c @@ -1110,6 +1110,7 @@ int ossl_qrx_read_pkt(OSSL_QRX *qrx, OSSL_QRX_PKT **ppkt) = BIO_ADDR_family(&rxe->local) != AF_UNSPEC ? &rxe->local : NULL; rxe->pkt.qrx = qrx; *ppkt = &rxe->pkt; + return 1; } diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c index b78a9c00e7..996de79bdf 100644 --- a/ssl/quic/quic_rx_depack.c +++ b/ssl/quic/quic_rx_depack.c @@ -188,11 +188,14 @@ static int depack_do_frame_stop_sending(PACKET *pkt, static int depack_do_frame_crypto(PACKET *pkt, QUIC_CHANNEL *ch, OSSL_QRX_PKT *parent_pkt, - OSSL_ACKM_RX_PKT *ackm_data) + OSSL_ACKM_RX_PKT *ackm_data, + uint64_t *datalen) { OSSL_QUIC_FRAME_CRYPTO f; QUIC_RSTREAM *rstream; + *datalen = 0; + if (!ossl_quic_wire_decode_frame_crypto(pkt, &f)) { ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_FRAME_ENCODING_ERROR, @@ -217,6 +220,8 @@ static int depack_do_frame_crypto(PACKET *pkt, QUIC_CHANNEL *ch, f.offset, f.data, f.len, 0)) return 0; + *datalen = f.len; + return 1; } @@ -381,12 +386,15 @@ static int depack_do_implicit_stream_create(QUIC_CHANNEL *ch, static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch, OSSL_QRX_PKT *parent_pkt, OSSL_ACKM_RX_PKT *ackm_data, - uint64_t frame_type) + uint64_t frame_type, + uint64_t *datalen) { OSSL_QUIC_FRAME_STREAM frame_data; QUIC_STREAM *stream; uint64_t fce; + *datalen = 0; + if (!ossl_quic_wire_decode_frame_stream(pkt, &frame_data)) { ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_FRAME_ENCODING_ERROR, @@ -452,6 +460,8 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch, frame_data.is_fin)) return 0; + *datalen = frame_data.len; + return 1; } @@ -795,6 +805,11 @@ static int depack_process_frames(QUIC_CHANNEL *ch, PACKET *pkt, while (PACKET_remaining(pkt) > 0) { uint64_t frame_type; + const unsigned char *sof = NULL; + uint64_t datalen = 0; + + if (ch->msg_callback != NULL) + sof = PACKET_data(pkt); if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type)) return 0; @@ -863,7 +878,7 @@ static int depack_process_frames(QUIC_CHANNEL *ch, PACKET *pkt, "CRYPTO frame not valid in 0-RTT"); return 0; } - if (!depack_do_frame_crypto(pkt, ch, parent_pkt, ackm_data)) + if (!depack_do_frame_crypto(pkt, ch, parent_pkt, ackm_data, &datalen)) return 0; break; case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN: @@ -897,7 +912,7 @@ static int depack_process_frames(QUIC_CHANNEL *ch, PACKET *pkt, return 0; } if (!depack_do_frame_stream(pkt, ch, parent_pkt, ackm_data, - frame_type)) + frame_type, &datalen)) return 0; break; @@ -1077,6 +1092,23 @@ static int depack_process_frames(QUIC_CHANNEL *ch, PACKET *pkt, "Unknown frame type received"); return 0; } + + if (ch->msg_callback != NULL) { + int ctype = SSL3_RT_QUIC_FRAME_FULL; + + size_t framelen = PACKET_data(pkt) - sof; + + if (frame_type == OSSL_QUIC_FRAME_TYPE_PADDING) { + ctype = SSL3_RT_QUIC_FRAME_PADDING; + } else if (OSSL_QUIC_FRAME_TYPE_IS_STREAM(frame_type) + || frame_type == OSSL_QUIC_FRAME_TYPE_CRYPTO) { + ctype = SSL3_RT_QUIC_FRAME_HEADER; + framelen -= (size_t)datalen; + } + + ch->msg_callback(0, OSSL_QUIC1_VERSION, ctype, sof, framelen, + ch->msg_callback_s, ch->msg_callback_arg); + } } return 1; diff --git a/ssl/quic/quic_trace.c b/ssl/quic/quic_trace.c index 1a08356003..025e8189ef 100644 --- a/ssl/quic/quic_trace.c +++ b/ssl/quic/quic_trace.c @@ -59,10 +59,413 @@ static const char *conn_id(QUIC_CONN_ID *id, char *buf, size_t buflen) return obuf; } +static int frame_ack(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_ACK ack; + OSSL_QUIC_ACK_RANGE *ack_ranges = NULL; + uint64_t total_ranges = 0; + + if (!ossl_quic_wire_peek_frame_ack_num_ranges(pkt, &total_ranges) + /* In case sizeof(uint64_t) > sizeof(size_t) */ + || total_ranges > SIZE_MAX / sizeof(ack_ranges[0]) + || (ack_ranges = OPENSSL_zalloc(sizeof(ack_ranges[0]) + * (size_t)total_ranges)) == NULL) + return 0; + + ack.ack_ranges = ack_ranges; + ack.num_ack_ranges = (size_t)total_ranges; + + if (!ossl_quic_wire_decode_frame_ack(pkt, 0, &ack, NULL)) + return 0; + + /* TODO(QUIC): Display the ack data here */ + + OPENSSL_free(ack_ranges); + return 1; +} + +static int frame_reset_stream(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_RESET_STREAM frame_data; + + if (!ossl_quic_wire_decode_frame_reset_stream(pkt, &frame_data)) + return 0; + + /* TODO(QUIC): Display reset stream data here */ + + return 1; +} + +static int frame_stop_sending(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_STOP_SENDING frame_data; + + if (!ossl_quic_wire_decode_frame_stop_sending(pkt, &frame_data)) + return 0; + + return 1; +} + +static int frame_crypto(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_CRYPTO frame_data; + + if (!ossl_quic_wire_decode_frame_crypto(pkt, &frame_data)) + return 0; + + BIO_printf(bio, " Offset: %lu\n", frame_data.offset); + BIO_printf(bio, " Len: %lu\n", frame_data.len); + + return 1; +} + +static int frame_new_token(BIO *bio, PACKET *pkt) +{ + const uint8_t *token; + size_t token_len; + + if (!ossl_quic_wire_decode_frame_new_token(pkt, &token, &token_len)) + return 0; + + return 1; +} + +static int frame_stream(BIO *bio, PACKET *pkt, uint64_t frame_type) +{ + + OSSL_QUIC_FRAME_STREAM frame_data; + + BIO_puts(bio, "Stream"); + switch(frame_type) { + case OSSL_QUIC_FRAME_TYPE_STREAM: + BIO_puts(bio, "\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_FIN: + BIO_puts(bio, " (Fin)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_LEN: + BIO_puts(bio, " (Len)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN: + BIO_puts(bio, " (Len, Fin)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF: + BIO_puts(bio, " (Off)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN: + BIO_puts(bio, " (Off, Fin)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN: + BIO_puts(bio, " (Off, Len)\n"); + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN: + BIO_puts(bio, " (Off, Len, Fin)\n"); + break; + + default: + return 0; + } + + if (!ossl_quic_wire_decode_frame_stream(pkt, &frame_data)) + return 0; + + BIO_printf(bio, " Stream id: %lu\n", frame_data.stream_id); + BIO_printf(bio, " Offset: %lu\n", frame_data.offset); + BIO_printf(bio, " Len: %lu\n", frame_data.len); + + return 1; +} + +static int frame_max_data(BIO *bio, PACKET *pkt) +{ + uint64_t max_data = 0; + + if (!ossl_quic_wire_decode_frame_max_data(pkt, &max_data)) + return 0; + + return 1; +} + +static int frame_max_stream_data(BIO *bio, PACKET *pkt) +{ + uint64_t stream_id = 0; + uint64_t max_stream_data = 0; + + if (!ossl_quic_wire_decode_frame_max_stream_data(pkt, &stream_id, + &max_stream_data)) + return 0; + + return 1; +} + +static int frame_max_streams(BIO *bio, PACKET *pkt) +{ + uint64_t max_streams = 0; + + if (!ossl_quic_wire_decode_frame_max_streams(pkt, &max_streams)) + return 0; + + return 1; +} + +static int frame_data_blocked(BIO *bio, PACKET *pkt) +{ + uint64_t max_data = 0; + + if (!ossl_quic_wire_decode_frame_data_blocked(pkt, &max_data)) + return 0; + + return 1; +} + +static int frame_stream_data_blocked(BIO *bio, PACKET *pkt) +{ + uint64_t stream_id = 0; + uint64_t max_data = 0; + + if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt, &stream_id, + &max_data)) + return 0; + + return 1; +} + +static int frame_streams_blocked(BIO *bio, PACKET *pkt) +{ + uint64_t max_data = 0; + + if (!ossl_quic_wire_decode_frame_streams_blocked(pkt, &max_data)) + return 0; + + return 1; +} + +static int frame_new_conn_id(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_NEW_CONN_ID frame_data; + + if (!ossl_quic_wire_decode_frame_new_conn_id(pkt, &frame_data)) + return 0; + + return 1; +} + +static int frame_retire_conn_id(BIO *bio, PACKET *pkt) +{ + uint64_t seq_num; + + if (!ossl_quic_wire_decode_frame_retire_conn_id(pkt, &seq_num)) + return 0; + + return 1; +} + +static int frame_path_challenge(BIO *bio, PACKET *pkt) +{ + uint64_t frame_data = 0; + + if (!ossl_quic_wire_decode_frame_path_challenge(pkt, &frame_data)) + return 0; + + return 1; +} + +static int frame_path_response(BIO *bio, PACKET *pkt) +{ + uint64_t frame_data = 0; + + if (!ossl_quic_wire_decode_frame_path_response(pkt, &frame_data)) + return 0; + + return 1; +} + +static int frame_conn_closed(BIO *bio, PACKET *pkt) +{ + OSSL_QUIC_FRAME_CONN_CLOSE frame_data; + + if (!ossl_quic_wire_decode_frame_conn_close(pkt, &frame_data)) + return 0; + + return 1; +} + +static int trace_frame_data(BIO *bio, PACKET *pkt) +{ + uint64_t frame_type; + + if (!ossl_quic_wire_peek_frame_header(pkt, &frame_type)) + return 0; + + switch (frame_type) { + case OSSL_QUIC_FRAME_TYPE_PING: + BIO_puts(bio, "Ping\n"); + if (!ossl_quic_wire_decode_frame_ping(pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_PADDING: + BIO_puts(bio, "Padding\n"); + ossl_quic_wire_decode_padding(pkt); + break; + + case OSSL_QUIC_FRAME_TYPE_ACK_WITHOUT_ECN: + case OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN: + BIO_puts(bio, "Ack "); + if (frame_type == OSSL_QUIC_FRAME_TYPE_ACK_WITH_ECN) + BIO_puts(bio, " (with ECN)\n"); + else + BIO_puts(bio, " (without ECN)\n"); + if (!frame_ack(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_RESET_STREAM: + BIO_puts(bio, "Reset stream\n"); + if (!frame_reset_stream(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_STOP_SENDING: + BIO_puts(bio, "Stop sending\n"); + if (!frame_stop_sending(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_CRYPTO: + BIO_puts(bio, "Crypto\n"); + if (!frame_crypto(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_NEW_TOKEN: + BIO_puts(bio, "New token\n"); + if (!frame_new_token(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM: + case OSSL_QUIC_FRAME_TYPE_STREAM_FIN: + case OSSL_QUIC_FRAME_TYPE_STREAM_LEN: + case OSSL_QUIC_FRAME_TYPE_STREAM_LEN_FIN: + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF: + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_FIN: + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN: + case OSSL_QUIC_FRAME_TYPE_STREAM_OFF_LEN_FIN: + /* frame_stream() prints the frame type string */ + if (!frame_stream(bio, pkt, frame_type)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_MAX_DATA: + BIO_puts(bio, "Max data\n"); + if (!frame_max_data(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA: + BIO_puts(bio, "Max stream data\n"); + if (!frame_max_stream_data(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI: + case OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_UNI: + BIO_puts(bio, "Max streams "); + if (frame_type == OSSL_QUIC_FRAME_TYPE_MAX_STREAMS_BIDI) + BIO_puts(bio, " (Bidi)\n"); + else + BIO_puts(bio, " (Uni)\n"); + if (!frame_max_streams(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_DATA_BLOCKED: + BIO_puts(bio, "Data blocked\n"); + if (!frame_data_blocked(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED: + BIO_puts(bio, "Stream data blocked\n"); + if (!frame_stream_data_blocked(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI: + case OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_UNI: + BIO_puts(bio, "Streams blocked"); + if (frame_type == OSSL_QUIC_FRAME_TYPE_STREAMS_BLOCKED_BIDI) + BIO_puts(bio, " (Bidi)\n"); + else + BIO_puts(bio, " (Uni)\n"); + if (!frame_streams_blocked(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_NEW_CONN_ID: + BIO_puts(bio, "New conn id\n"); + if (!frame_new_conn_id(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_RETIRE_CONN_ID: + BIO_puts(bio, "Retire conn id\n"); + if (!frame_retire_conn_id(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_PATH_CHALLENGE: + BIO_puts(bio, "Path challenge\n"); + if (!frame_path_challenge(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE: + BIO_puts(bio, "Path response\n"); + if (!frame_path_response(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP: + case OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_TRANSPORT: + BIO_puts(bio, "Connection close"); + if (frame_type == OSSL_QUIC_FRAME_TYPE_CONN_CLOSE_APP) + BIO_puts(bio, " (app)\n"); + else + BIO_puts(bio, " (transport)\n"); + if (!frame_conn_closed(bio, pkt)) + return 0; + break; + + case OSSL_QUIC_FRAME_TYPE_HANDSHAKE_DONE: + BIO_puts(bio, "Handshake done\n"); + if (!ossl_quic_wire_decode_frame_handshake_done(pkt)) + return 0; + break; + + default: + return 0; + } + + if (PACKET_remaining(pkt) != 0) + BIO_puts(bio, " <unexpected trailing frame data skipped>\n"); + + return 1; +} + int ossl_quic_trace(int write_p, int version, int content_type, const void *buf, size_t msglen, SSL *ssl, void *arg) { BIO *bio = arg; + PACKET pkt; switch (content_type) { case SSL3_RT_QUIC_DATAGRAM: @@ -77,7 +480,6 @@ int ossl_quic_trace(int write_p, int version, int content_type, case SSL3_RT_QUIC_PACKET: { - PACKET pkt; QUIC_PKT_HDR hdr; /* * Max Conn id is 20 bytes (40 hex digits) plus "0x" bytes plus NUL @@ -128,6 +530,30 @@ int ossl_quic_trace(int write_p, int version, int content_type, break; } + case SSL3_RT_QUIC_FRAME_PADDING: + case SSL3_RT_QUIC_FRAME_FULL: + { + BIO_puts(bio, write_p ? "Sent" : "Received"); + BIO_puts(bio, " Frame: "); + + if (!PACKET_buf_init(&pkt, buf, msglen)) + return 0; + if (!trace_frame_data(bio, &pkt)) { + BIO_puts(bio, " <error processing frame data>\n"); + return 0; + } + } + break; + + case SSL3_RT_QUIC_FRAME_HEADER: + { + BIO_puts(bio, write_p ? "Sent" : "Received"); + BIO_puts(bio, " Frame Data\n"); + + /* TODO(QUIC): Implement me */ + BIO_puts(bio, " <content skipped>\n"); + } + break; default: /* Unrecognised content_type. We defer to SSL_trace */ |