diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/neqo-transport/src/qlog.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-transport/src/qlog.rs')
-rw-r--r-- | third_party/rust/neqo-transport/src/qlog.rs | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/third_party/rust/neqo-transport/src/qlog.rs b/third_party/rust/neqo-transport/src/qlog.rs new file mode 100644 index 0000000000..ad86ec2b2e --- /dev/null +++ b/third_party/rust/neqo-transport/src/qlog.rs @@ -0,0 +1,438 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Functions that handle capturing QLOG traces. + +use std::convert::TryFrom; +use std::ops::{Deref, RangeInclusive}; +use std::string::String; +use std::time::Duration; + +use qlog::{self, event::Event, PacketHeader, QuicFrame}; + +use neqo_common::{hex, qinfo, qlog::NeqoQlog, Decoder}; + +use crate::connection::State; +use crate::frame::{CloseError, Frame}; +use crate::packet::{DecryptedPacket, PacketNumber, PacketType, PublicPacket}; +use crate::path::PathRef; +use crate::stream_id::StreamType as NeqoStreamType; +use crate::tparams::{self, TransportParametersHandler}; +use crate::tracking::SentPacket; +use crate::Version; + +pub fn connection_tparams_set(qlog: &mut NeqoQlog, tph: &TransportParametersHandler) { + qlog.add_event(|| { + let remote = tph.remote(); + Some(Event::transport_parameters_set( + None, + None, + None, + None, + None, + None, + remote + .get_bytes(tparams::ORIGINAL_DESTINATION_CONNECTION_ID) + .map(hex), + remote.get_bytes(tparams::STATELESS_RESET_TOKEN).map(hex), + if remote.get_empty(tparams::DISABLE_MIGRATION) { + Some(true) + } else { + None + }, + Some(remote.get_integer(tparams::IDLE_TIMEOUT)), + Some(remote.get_integer(tparams::MAX_UDP_PAYLOAD_SIZE)), + Some(remote.get_integer(tparams::ACK_DELAY_EXPONENT)), + Some(remote.get_integer(tparams::MAX_ACK_DELAY)), + // TODO(hawkinsw@obs.cr): We do not yet handle ACTIVE_CONNECTION_ID_LIMIT in tparams yet. + None, + Some(format!("{}", remote.get_integer(tparams::INITIAL_MAX_DATA))), + Some(format!( + "{}", + remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_BIDI_LOCAL) + )), + Some(format!( + "{}", + remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_BIDI_REMOTE) + )), + Some(format!( + "{}", + remote.get_integer(tparams::INITIAL_MAX_STREAM_DATA_UNI) + )), + Some(format!( + "{}", + remote.get_integer(tparams::INITIAL_MAX_STREAMS_BIDI) + )), + Some(format!( + "{}", + remote.get_integer(tparams::INITIAL_MAX_STREAMS_UNI) + )), + // TODO(hawkinsw@obs.cr): We do not yet handle PREFERRED_ADDRESS in tparams yet. + None, + )) + }) +} + +pub fn server_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { + connection_started(qlog, path) +} + +pub fn client_connection_started(qlog: &mut NeqoQlog, path: &PathRef) { + connection_started(qlog, path) +} + +fn connection_started(qlog: &mut NeqoQlog, path: &PathRef) { + qlog.add_event(|| { + let p = path.deref().borrow(); + Some(Event::connection_started( + if p.local_address().ip().is_ipv4() { + "ipv4".into() + } else { + "ipv6".into() + }, + format!("{}", p.local_address().ip()), + format!("{}", p.remote_address().ip()), + Some("QUIC".into()), + p.local_address().port().into(), + p.remote_address().port().into(), + Some(format!("{:x}", Version::default().wire_version())), + Some(format!("{}", p.local_cid())), + Some(format!("{}", p.remote_cid())), + )) + }) +} + +pub fn connection_state_updated(qlog: &mut NeqoQlog, new: &State) { + qlog.add_event(|| { + Some(Event::connection_state_updated_min(match new { + State::Init => qlog::ConnectionState::Attempted, + State::WaitInitial => qlog::ConnectionState::Attempted, + State::WaitVersion | State::Handshaking => qlog::ConnectionState::Handshake, + State::Connected => qlog::ConnectionState::Active, + State::Confirmed => qlog::ConnectionState::Active, + State::Closing { .. } => qlog::ConnectionState::Draining, + State::Draining { .. } => qlog::ConnectionState::Draining, + State::Closed { .. } => qlog::ConnectionState::Closed, + })) + }) +} + +pub fn packet_sent( + qlog: &mut NeqoQlog, + pt: PacketType, + pn: PacketNumber, + plen: usize, + body: &[u8], +) { + qlog.add_event_with_stream(|stream| { + let mut d = Decoder::from(body); + + stream.add_event(Event::packet_sent_min( + to_qlog_pkt_type(pt), + PacketHeader::new( + pn, + Some(u64::try_from(plen).unwrap()), + None, + None, + None, + None, + ), + Some(Vec::new()), + ))?; + + while d.remaining() > 0 { + match Frame::decode(&mut d) { + Ok(f) => { + stream.add_frame(frame_to_qlogframe(&f), false)?; + } + Err(_) => { + qinfo!("qlog: invalid frame"); + break; + } + } + } + + stream.finish_frames() + }) +} + +pub fn packet_dropped(qlog: &mut NeqoQlog, payload: &PublicPacket) { + qlog.add_event(|| { + Some(Event::packet_dropped( + Some(to_qlog_pkt_type(payload.packet_type())), + Some(u64::try_from(payload.len()).unwrap()), + None, + )) + }) +} + +pub fn packets_lost(qlog: &mut NeqoQlog, pkts: &[SentPacket]) { + qlog.add_event_with_stream(|stream| { + for pkt in pkts { + stream.add_event(Event::packet_lost_min( + to_qlog_pkt_type(pkt.pt), + pkt.pn.to_string(), + Vec::new(), + ))?; + + stream.finish_frames()?; + } + Ok(()) + }) +} + +pub fn packet_received( + qlog: &mut NeqoQlog, + public_packet: &PublicPacket, + payload: &DecryptedPacket, +) { + qlog.add_event_with_stream(|stream| { + let mut d = Decoder::from(&payload[..]); + + stream.add_event(Event::packet_received( + to_qlog_pkt_type(payload.packet_type()), + PacketHeader::new( + payload.pn(), + Some(u64::try_from(public_packet.len()).unwrap()), + None, + None, + None, + None, + ), + Some(Vec::new()), + None, + None, + None, + ))?; + + while d.remaining() > 0 { + match Frame::decode(&mut d) { + Ok(f) => stream.add_frame(frame_to_qlogframe(&f), false)?, + Err(_) => { + qinfo!("qlog: invalid frame"); + break; + } + } + } + + stream.finish_frames() + }) +} + +#[allow(dead_code)] +pub enum QlogMetric { + MinRtt(Duration), + SmoothedRtt(Duration), + LatestRtt(Duration), + RttVariance(u64), + MaxAckDelay(u64), + PtoCount(usize), + CongestionWindow(usize), + BytesInFlight(usize), + SsThresh(usize), + PacketsInFlight(u64), + InRecovery(bool), + PacingRate(u64), +} + +pub fn metrics_updated(qlog: &mut NeqoQlog, updated_metrics: &[QlogMetric]) { + debug_assert!(!updated_metrics.is_empty()); + + qlog.add_event(|| { + let mut min_rtt: Option<u64> = None; + let mut smoothed_rtt: Option<u64> = None; + let mut latest_rtt: Option<u64> = None; + let mut rtt_variance: Option<u64> = None; + let mut max_ack_delay: Option<u64> = None; + let mut pto_count: Option<u64> = None; + let mut congestion_window: Option<u64> = None; + let mut bytes_in_flight: Option<u64> = None; + let mut ssthresh: Option<u64> = None; + let mut packets_in_flight: Option<u64> = None; + let mut in_recovery: Option<bool> = None; + let mut pacing_rate: Option<u64> = None; + + for metric in updated_metrics { + match metric { + QlogMetric::MinRtt(v) => min_rtt = Some(u64::try_from(v.as_millis()).unwrap()), + QlogMetric::SmoothedRtt(v) => { + smoothed_rtt = Some(u64::try_from(v.as_millis()).unwrap()) + } + QlogMetric::LatestRtt(v) => { + latest_rtt = Some(u64::try_from(v.as_millis()).unwrap()) + } + QlogMetric::RttVariance(v) => rtt_variance = Some(*v), + QlogMetric::MaxAckDelay(v) => max_ack_delay = Some(*v), + QlogMetric::PtoCount(v) => pto_count = Some(u64::try_from(*v).unwrap()), + QlogMetric::CongestionWindow(v) => { + congestion_window = Some(u64::try_from(*v).unwrap()) + } + QlogMetric::BytesInFlight(v) => bytes_in_flight = Some(u64::try_from(*v).unwrap()), + QlogMetric::SsThresh(v) => ssthresh = Some(u64::try_from(*v).unwrap()), + QlogMetric::PacketsInFlight(v) => packets_in_flight = Some(*v), + QlogMetric::InRecovery(v) => in_recovery = Some(*v), + QlogMetric::PacingRate(v) => pacing_rate = Some(*v), + } + } + + Some(Event::metrics_updated( + min_rtt, + smoothed_rtt, + latest_rtt, + rtt_variance, + max_ack_delay, + pto_count, + congestion_window, + bytes_in_flight, + ssthresh, + packets_in_flight, + in_recovery, + pacing_rate, + )) + }) +} + +// Helper functions + +fn frame_to_qlogframe(frame: &Frame) -> QuicFrame { + match frame { + Frame::Padding => QuicFrame::padding(), + Frame::Ping => QuicFrame::ping(), + Frame::Ack { + largest_acknowledged, + ack_delay, + first_ack_range, + ack_ranges, + } => { + let ranges = + Frame::decode_ack_frame(*largest_acknowledged, *first_ack_range, ack_ranges).ok(); + + QuicFrame::ack( + Some(ack_delay.to_string()), + ranges.map(|all| { + all.into_iter() + .map(RangeInclusive::into_inner) + .collect::<Vec<_>>() + }), + None, + None, + None, + ) + } + Frame::ResetStream { + stream_id, + application_error_code, + final_size, + } => QuicFrame::reset_stream( + stream_id.as_u64().to_string(), + *application_error_code, + final_size.to_string(), + ), + Frame::StopSending { + stream_id, + application_error_code, + } => QuicFrame::stop_sending(stream_id.as_u64().to_string(), *application_error_code), + Frame::Crypto { offset, data } => { + QuicFrame::crypto(offset.to_string(), data.len().to_string()) + } + Frame::NewToken { token } => QuicFrame::new_token(token.len().to_string(), hex(token)), + Frame::Stream { + fin, + stream_id, + offset, + data, + .. + } => QuicFrame::stream( + stream_id.as_u64().to_string(), + offset.to_string(), + data.len().to_string(), + *fin, + None, + ), + Frame::MaxData { maximum_data } => QuicFrame::max_data(maximum_data.to_string()), + Frame::MaxStreamData { + stream_id, + maximum_stream_data, + } => QuicFrame::max_stream_data( + stream_id.as_u64().to_string(), + maximum_stream_data.to_string(), + ), + Frame::MaxStreams { + stream_type, + maximum_streams, + } => QuicFrame::max_streams( + match stream_type { + NeqoStreamType::BiDi => qlog::StreamType::Bidirectional, + NeqoStreamType::UniDi => qlog::StreamType::Unidirectional, + }, + maximum_streams.to_string(), + ), + Frame::DataBlocked { data_limit } => QuicFrame::data_blocked(data_limit.to_string()), + Frame::StreamDataBlocked { + stream_id, + stream_data_limit, + } => QuicFrame::stream_data_blocked( + stream_id.as_u64().to_string(), + stream_data_limit.to_string(), + ), + Frame::StreamsBlocked { + stream_type, + stream_limit, + } => QuicFrame::streams_blocked( + match stream_type { + NeqoStreamType::BiDi => qlog::StreamType::Bidirectional, + NeqoStreamType::UniDi => qlog::StreamType::Unidirectional, + }, + stream_limit.to_string(), + ), + Frame::NewConnectionId { + sequence_number, + retire_prior, + connection_id, + stateless_reset_token, + } => QuicFrame::new_connection_id( + sequence_number.to_string(), + retire_prior.to_string(), + connection_id.len() as u64, + hex(connection_id), + hex(stateless_reset_token), + ), + Frame::RetireConnectionId { sequence_number } => { + QuicFrame::retire_connection_id(sequence_number.to_string()) + } + Frame::PathChallenge { data } => QuicFrame::path_challenge(Some(hex(data))), + Frame::PathResponse { data } => QuicFrame::path_response(Some(hex(data))), + Frame::ConnectionClose { + error_code, + frame_type, + reason_phrase, + } => QuicFrame::connection_close( + match error_code { + CloseError::Transport(_) => qlog::ErrorSpace::TransportError, + CloseError::Application(_) => qlog::ErrorSpace::ApplicationError, + }, + error_code.code(), + 0, + String::from_utf8_lossy(reason_phrase).to_string(), + Some(frame_type.to_string()), + ), + Frame::HandshakeDone => QuicFrame::handshake_done(), + Frame::AckFrequency { .. } => QuicFrame::unknown(frame.get_type()), + Frame::Datagram { .. } => QuicFrame::unknown(frame.get_type()), + } +} + +fn to_qlog_pkt_type(ptype: PacketType) -> qlog::PacketType { + match ptype { + PacketType::Initial => qlog::PacketType::Initial, + PacketType::Handshake => qlog::PacketType::Handshake, + PacketType::ZeroRtt => qlog::PacketType::ZeroRtt, + PacketType::Short => qlog::PacketType::OneRtt, + PacketType::Retry => qlog::PacketType::Retry, + PacketType::VersionNegotiation => qlog::PacketType::VersionNegotiation, + PacketType::OtherVersion => qlog::PacketType::Unknown, + } +} |