1#![allow(unused_variables)]
11
12#[cfg(not(feature = "qlog"))]
13use std::marker::PhantomData;
14#[cfg(feature = "qlog")]
15use std::sync::{Arc, Mutex};
16use std::{
17 net::{IpAddr, SocketAddr},
18 time::Duration,
19};
20
21#[cfg(feature = "qlog")]
22use qlog::{
23 CommonFields, HexSlice, TokenType, VantagePoint,
24 events::{
25 ApplicationError, ConnectionClosedFrameError, Event, EventData, RawInfo, TupleEndpointInfo,
26 quic::{
27 self, AckedRanges, AddressDiscoveryRole, ConnectionStarted, ErrorSpace, PacketHeader,
28 PacketLost, PacketLostTrigger, PacketReceived, PacketSent, PacketType,
29 ParametersRestored, ParametersSet, PreferredAddress, QlogTimerType, QuicFrame,
30 StreamType, TimerEventType, TimerType, TimerUpdated, TransportInitiator, TupleAssigned,
31 },
32 },
33 streamer::QlogStreamer,
34};
35#[cfg(feature = "qlog")]
36use tracing::warn;
37
38use crate::{
39 Connection, ConnectionId, Frame, Instant, PathId,
40 connection::{PathData, SentPacket, timer::Timer},
41 frame::{EcnCounts, StreamMeta},
42 packet::{Header, SpaceId},
43 range_set::ArrayRangeSet,
44 transport_parameters::TransportParameters,
45};
46#[cfg(feature = "qlog")]
47use crate::{
48 QlogConfig, Side, TransportErrorCode,
49 connection::timer::{ConnTimer, PathTimer},
50 frame::Close,
51};
52
53#[cfg(feature = "qlog")]
55#[derive(Clone)]
56pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>);
57
58#[cfg(feature = "qlog")]
59impl QlogStream {
60 pub(crate) fn new(
61 config: QlogConfig,
62 initial_dst_cid: ConnectionId,
63 side: Side,
64 now: Instant,
65 ) -> Result<Self, qlog::Error> {
66 let vantage_point = VantagePoint {
67 name: None,
68 ty: match side {
69 Side::Client => qlog::VantagePointType::Client,
70 Side::Server => qlog::VantagePointType::Server,
71 },
72 flow: None,
73 };
74
75 let common_fields = CommonFields {
76 group_id: Some(initial_dst_cid.to_string()),
77 ..Default::default()
78 };
79
80 let trace = qlog::TraceSeq::new(
81 config.title.clone(),
82 config.description.clone(),
83 Some(common_fields),
84 Some(vantage_point),
85 vec![],
86 );
87
88 let start_time = config.start_time.unwrap_or(now);
89
90 let mut streamer = QlogStreamer::new(
91 config.title,
92 config.description,
93 start_time,
94 trace,
95 qlog::events::EventImportance::Extra,
96 config.writer,
97 );
98
99 streamer.start_log()?;
100 Ok(Self(Arc::new(Mutex::new(streamer))))
101 }
102
103 fn emit_event(&self, event: EventData, now: Instant) {
104 self.emit_event_with_tuple_id(event, now, None);
105 }
106
107 fn emit_event_with_tuple_id(&self, event: EventData, now: Instant, tuple: Option<String>) {
108 let mut event = Event::with_time(0.0, event);
110 event.tuple = tuple;
111 let mut qlog_streamer = self.0.lock().unwrap();
112 if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
113 warn!("could not emit qlog event: {e}");
114 }
115 }
116}
117
118#[derive(Clone, Default)]
120pub(crate) struct QlogSink {
121 #[cfg(feature = "qlog")]
122 stream: Option<QlogStream>,
123}
124
125impl QlogSink {
126 #[cfg(feature = "qlog")]
127 pub(crate) fn new(
128 config: QlogConfig,
129 initial_dst_cid: ConnectionId,
130 side: Side,
131 now: Instant,
132 ) -> Self {
133 let stream = QlogStream::new(config, initial_dst_cid, side, now)
134 .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
135 .ok();
136 Self { stream }
137 }
138
139 pub(crate) fn emit_connection_started(
140 &self,
141 now: Instant,
142 loc_cid: ConnectionId,
143 rem_cid: ConnectionId,
144 remote: SocketAddr,
145 local_ip: Option<IpAddr>,
146 transport_params: &TransportParameters,
147 ) {
148 #[cfg(feature = "qlog")]
149 {
150 let Some(stream) = self.stream.as_ref() else {
151 return;
152 };
153 stream.emit_event(
154 EventData::ConnectionStarted(ConnectionStarted {
155 local: tuple_endpoint_info(local_ip, None, Some(loc_cid)),
156 remote: tuple_endpoint_info(
157 Some(remote.ip()),
158 Some(remote.port()),
159 Some(rem_cid),
160 ),
161 }),
162 now,
163 );
164
165 let params = transport_params.to_qlog(TransportInitiator::Local);
166 let event = EventData::ParametersSet(params);
167 stream.emit_event(event, now);
168 }
169 }
170
171 pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
172 #[cfg(feature = "qlog")]
173 {
174 let Some(stream) = self.stream.as_ref() else {
175 return;
176 };
177
178 let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
179 return;
180 };
181
182 stream.emit_event(EventData::MetricsUpdated(metrics), now);
183 }
184 }
185
186 pub(super) fn emit_packet_lost(
187 &self,
188 pn: u64,
189 info: &SentPacket,
190 loss_delay: Duration,
191 space: SpaceId,
192 now: Instant,
193 ) {
194 #[cfg(feature = "qlog")]
195 {
196 let Some(stream) = self.stream.as_ref() else {
197 return;
198 };
199
200 let event = PacketLost {
201 header: Some(PacketHeader {
202 packet_number: Some(pn),
203 packet_type: packet_type(space, false),
204 length: Some(info.size),
205 ..Default::default()
206 }),
207 frames: None,
208 trigger: Some(
209 match info.time_sent.saturating_duration_since(now) >= loss_delay {
210 true => PacketLostTrigger::TimeThreshold,
211 false => PacketLostTrigger::ReorderingThreshold,
212 },
213 ),
214 is_mtu_probe_packet: None,
215 };
216
217 stream.emit_event(EventData::PacketLost(event), now);
218 }
219 }
220
221 pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
222 #[cfg(feature = "qlog")]
223 {
224 let Some(stream) = self.stream.as_ref() else {
225 return;
226 };
227 let params = conn.peer_params.to_qlog_restored();
228 let event = EventData::ParametersRestored(params);
229 stream.emit_event(event, now);
230 }
231 }
232
233 pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
234 #[cfg(feature = "qlog")]
235 {
236 let Some(stream) = self.stream.as_ref() else {
237 return;
238 };
239 let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
240 let event = EventData::ParametersSet(params);
241 stream.emit_event(event, now);
242 }
243 }
244
245 pub(super) fn emit_tuple_assigned(&self, path_id: PathId, remote: SocketAddr, now: Instant) {
246 #[cfg(feature = "qlog")]
247 {
248 let Some(stream) = self.stream.as_ref() else {
249 return;
250 };
251 let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
252 let event = TupleAssigned {
253 tuple_id,
254 tuple_local: None,
255 tuple_remote: Some(tuple_endpoint_info(
256 Some(remote.ip()),
257 Some(remote.port()),
258 None,
259 )),
260 };
261
262 stream.emit_event(EventData::TupleAssigned(event), now);
263 }
264 }
265
266 pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
267 #[cfg(feature = "qlog")]
268 {
269 let Some(stream) = self.stream.as_ref() else {
270 return;
271 };
272 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
273 stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id);
274 }
275 }
276
277 pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
278 #[cfg(feature = "qlog")]
279 {
280 let Some(stream) = self.stream.as_ref() else {
281 return;
282 };
283 let mut packet = packet;
284 packet.emit_padding();
285 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
286 let event = packet.inner;
287 stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id);
288 }
289 }
290
291 #[cfg(feature = "qlog")]
296 fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
297 let Some(stream) = self.stream.as_ref() else {
298 return;
299 };
300
301 let timer_type: Option<TimerType> = match timer {
302 Timer::Conn(conn_timer) => match conn_timer {
303 ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
304 ConnTimer::Close => Some(TimerType::custom("close")),
305 ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
306 ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
307 ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
308 },
309 Timer::PerPath(_, path_timer) => match path_timer {
310 PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
311 PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
312 PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()),
313 PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
314 PathTimer::PathOpen => Some(TimerType::custom("path_open")),
315 PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
316 PathTimer::Pacing => Some(TimerType::custom("pacing")),
317 PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
318 PathTimer::DiscardPath => Some(TimerType::custom("discard_path")),
319 },
320 };
321
322 let Some(timer_type) = timer_type else {
323 return;
324 };
325
326 let delta = match op {
327 TimerOp::Set(instant) => instant
328 .checked_duration_since(now)
329 .map(|dur| dur.as_secs_f32() * 1000.),
330 _ => None,
331 };
332 let path_id = match timer {
333 Timer::Conn(_) => None,
334 Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
335 };
336
337 let event_type = match op {
338 TimerOp::Set(_) => TimerEventType::Set,
339 TimerOp::Expire => TimerEventType::Expired,
340 TimerOp::Cancelled => TimerEventType::Cancelled,
341 };
342
343 let event = TimerUpdated {
344 path_id,
345 timer_type: Some(timer_type),
346 timer_id: None,
347 packet_number_space: None,
348 event_type,
349 delta,
350 };
351 stream.emit_event(EventData::TimerUpdated(event), now);
352 }
353
354 pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
360 #[cfg(feature = "qlog")]
361 let s = QlogSinkWithTime { sink: self, now };
362 #[cfg(not(feature = "qlog"))]
363 let s = QlogSinkWithTime {
364 _phantom: PhantomData,
365 };
366 s
367 }
368}
369
370#[derive(Clone)]
372pub(super) struct QlogSinkWithTime<'a> {
373 #[cfg(feature = "qlog")]
374 sink: &'a QlogSink,
375 #[cfg(feature = "qlog")]
376 now: Instant,
377 #[cfg(not(feature = "qlog"))]
378 _phantom: PhantomData<&'a ()>,
379}
380
381impl<'a> QlogSinkWithTime<'a> {
382 pub(super) fn emit_timer_stop(&self, timer: Timer) {
383 #[cfg(feature = "qlog")]
384 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
385 }
386
387 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
388 #[cfg(feature = "qlog")]
389 self.sink
390 .emit_timer(timer, TimerOp::Set(expire_at), self.now)
391 }
392
393 pub(super) fn emit_timer_expire(&self, timer: Timer) {
394 #[cfg(feature = "qlog")]
395 self.sink.emit_timer(timer, TimerOp::Expire, self.now)
396 }
397}
398
399#[cfg(feature = "qlog")]
400enum TimerOp {
401 Set(Instant),
402 Expire,
403 Cancelled,
404}
405
406#[derive(Default)]
408pub(crate) struct QlogSentPacket {
409 #[cfg(feature = "qlog")]
410 inner: PacketSent,
411}
412
413impl QlogSentPacket {
414 pub(crate) fn header(
416 &mut self,
417 header: &Header,
418 pn: Option<u64>,
419 space: SpaceId,
420 is_0rtt: bool,
421 path_id: PathId,
422 ) {
423 #[cfg(feature = "qlog")]
424 {
425 self.inner.header.scid = header.src_cid().map(stringify_cid);
426 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
427 self.inner.header.packet_number = pn;
428 self.inner.header.packet_type = packet_type(space, is_0rtt);
429 self.inner.header.path_id = Some(path_id.as_u32() as u64);
430 }
431 }
432
433 pub(crate) fn frame(&mut self, frame: &Frame) {
435 #[cfg(feature = "qlog")]
436 self.frame_raw(frame.to_qlog())
437 }
438
439 pub(crate) fn frame_padding(&mut self, count: usize) {
443 #[cfg(feature = "qlog")]
444 self.frame_raw(QuicFrame::Padding {
445 raw: Some(RawInfo {
446 length: Some(count as u64),
447 payload_length: Some(count as u64),
448 data: None,
449 }),
450 });
451 }
452
453 pub(crate) fn frame_ack(
457 &mut self,
458 delay: u64,
459 ranges: &ArrayRangeSet,
460 ecn: Option<&EcnCounts>,
461 ) {
462 #[cfg(feature = "qlog")]
463 self.frame_raw(QuicFrame::Ack {
464 ack_delay: Some(delay as f32),
465 acked_ranges: Some(AckedRanges::Double(
466 ranges
467 .iter()
468 .map(|range| (range.start, range.end))
469 .collect(),
470 )),
471 ect1: ecn.map(|e| e.ect1),
472 ect0: ecn.map(|e| e.ect0),
473 ce: ecn.map(|e| e.ce),
474 raw: None,
475 });
476 }
477
478 pub(crate) fn frame_path_ack(
482 &mut self,
483 path_id: PathId,
484 delay: u64,
485 ranges: &ArrayRangeSet,
486 ecn: Option<&EcnCounts>,
487 ) {
488 #[cfg(feature = "qlog")]
489 self.frame_raw(QuicFrame::PathAck {
490 path_id: path_id.as_u32() as u64,
491 ack_delay: Some(delay as f32),
492 acked_ranges: Some(AckedRanges::Double(
493 ranges
494 .iter()
495 .map(|range| (range.start, range.end))
496 .collect(),
497 )),
498 ect1: ecn.map(|e| e.ect1),
499 ect0: ecn.map(|e| e.ect0),
500 ce: ecn.map(|e| e.ce),
501 raw: None,
502 });
503 }
504
505 pub(crate) fn frame_datagram(&mut self, len: u64) {
509 #[cfg(feature = "qlog")]
510 self.frame_raw(QuicFrame::Datagram {
511 raw: Some(RawInfo {
512 length: Some(len),
513 ..Default::default()
514 }),
515 });
516 }
517
518 pub(crate) fn frame_stream(&mut self, meta: &StreamMeta) {
522 #[cfg(feature = "qlog")]
523 self.frame_raw(QuicFrame::Stream {
524 stream_id: meta.id.into(),
525 offset: Some(meta.offsets.start),
526 fin: Some(meta.fin),
527 raw: Some(RawInfo {
528 length: Some(meta.offsets.end - meta.offsets.start),
529 ..Default::default()
530 }),
531 });
532 }
533
534 #[cfg(feature = "qlog")]
539 fn frame_raw(&mut self, frame: QuicFrame) {
540 self.inner.frames.get_or_insert_default().push(frame);
541 }
542
543 pub(super) fn finalize(&mut self, len: usize) {
545 #[cfg(feature = "qlog")]
546 {
547 self.inner.header.length = Some(len as u16);
548 }
549 }
550}
551
552pub(crate) struct QlogRecvPacket {
554 #[cfg(feature = "qlog")]
555 inner: PacketReceived,
556 #[cfg(feature = "qlog")]
557 padding: usize,
558}
559
560impl QlogRecvPacket {
561 pub(crate) fn new(len: usize) -> Self {
565 #[cfg(not(feature = "qlog"))]
566 let this = Self {};
567
568 #[cfg(feature = "qlog")]
569 let this = {
570 let mut this = Self {
571 inner: Default::default(),
572 padding: 0,
573 };
574 this.inner.header.length = Some(len as u16);
575 this
576 };
577
578 this
579 }
580
581 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
583 #[cfg(feature = "qlog")]
584 {
585 let is_0rtt = !header.is_1rtt();
586 self.inner.header.scid = header.src_cid().map(stringify_cid);
587 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
588 self.inner.header.packet_number = pn;
589 self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
590 self.inner.header.path_id = Some(path_id.as_u32() as u64);
591 }
592 }
593
594 pub(crate) fn frame(&mut self, frame: &Frame) {
596 #[cfg(feature = "qlog")]
597 {
598 if matches!(frame, crate::Frame::Padding) {
599 self.padding += 1;
600 } else {
601 self.emit_padding();
602 self.inner
603 .frames
604 .get_or_insert_default()
605 .push(frame.to_qlog())
606 }
607 }
608 }
609
610 #[cfg(feature = "qlog")]
611 fn emit_padding(&mut self) {
612 if self.padding > 0 {
613 self.inner
614 .frames
615 .get_or_insert_default()
616 .push(QuicFrame::Padding {
617 raw: Some(RawInfo {
618 length: Some(self.padding as u64),
619 payload_length: Some(self.padding as u64),
620 data: None,
621 }),
622 });
623 self.padding = 0;
624 }
625 }
626}
627
628#[cfg(feature = "qlog")]
629impl Frame {
630 pub(crate) fn to_qlog(&self) -> QuicFrame {
632 match self {
633 Self::Padding => QuicFrame::Padding {
634 raw: Some(RawInfo {
635 length: None,
636 payload_length: Some(1),
637 data: None,
638 }),
639 },
640 Self::Ping => QuicFrame::Ping { raw: None },
641 Self::Ack(f) => QuicFrame::Ack {
642 ack_delay: Some(f.delay as f32),
643 acked_ranges: Some(AckedRanges::Double(
644 f.iter()
645 .map(|range| (*range.start(), *range.end()))
646 .collect(),
647 )),
648 ect1: f.ecn.as_ref().map(|e| e.ect1),
649 ect0: f.ecn.as_ref().map(|e| e.ect0),
650 ce: f.ecn.as_ref().map(|e| e.ce),
651 raw: None,
652 },
653 Self::ResetStream(f) => QuicFrame::ResetStream {
654 stream_id: f.id.into(),
655 error_code: Some(f.error_code.into_inner()),
656 final_size: f.final_offset.into(),
657 error: ApplicationError::Unknown,
658 raw: None,
659 },
660 Self::StopSending(f) => QuicFrame::StopSending {
661 stream_id: f.id.into(),
662 error_code: Some(f.error_code.into_inner()),
663 error: ApplicationError::Unknown,
664 raw: None,
665 },
666 Self::Crypto(f) => QuicFrame::Crypto {
667 offset: f.offset,
668 raw: Some(RawInfo {
669 length: Some(f.data.len() as u64),
670 ..Default::default()
671 }),
672 },
673 Self::NewToken(f) => QuicFrame::NewToken {
674 token: qlog::Token {
675 ty: Some(TokenType::Retry),
676 raw: Some(RawInfo {
677 data: HexSlice::maybe_string(Some(&f.token)),
678 length: Some(f.token.len() as u64),
679 payload_length: None,
680 }),
681 details: None,
682 },
683 raw: None,
684 },
685 Self::Stream(s) => QuicFrame::Stream {
686 stream_id: s.id.into(),
687 offset: Some(s.offset),
688 fin: Some(s.fin),
689 raw: Some(RawInfo {
690 length: Some(s.data.len() as u64),
691 ..Default::default()
692 }),
693 },
694 Self::MaxData(v) => QuicFrame::MaxData {
695 maximum: (*v).into(),
696 raw: None,
697 },
698 Self::MaxStreamData { id, offset } => QuicFrame::MaxStreamData {
699 stream_id: (*id).into(),
700 maximum: *offset,
701 raw: None,
702 },
703 Self::MaxStreams { dir, count } => QuicFrame::MaxStreams {
704 maximum: *count,
705 stream_type: (*dir).into(),
706 raw: None,
707 },
708 Self::DataBlocked { offset } => QuicFrame::DataBlocked {
709 limit: *offset,
710 raw: None,
711 },
712 Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked {
713 stream_id: (*id).into(),
714 limit: *offset,
715 raw: None,
716 },
717 Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked {
718 stream_type: (*dir).into(),
719 limit: *limit,
720 raw: None,
721 },
722 Self::NewConnectionId(f) => QuicFrame::NewConnectionId {
723 sequence_number: f.sequence,
724 retire_prior_to: f.retire_prior_to,
725 connection_id_length: Some(f.id.len() as u8),
726 connection_id: f.id.to_string(),
727 stateless_reset_token: Some(f.reset_token.to_string()),
728 raw: None,
729 },
730 Self::RetireConnectionId(f) => QuicFrame::RetireConnectionId {
731 sequence_number: f.sequence,
732 raw: None,
733 },
734 Self::PathChallenge(token) => QuicFrame::PathChallenge {
735 data: Some(token.to_string()),
736 raw: None,
737 },
738 Self::PathResponse(token) => QuicFrame::PathResponse {
739 data: Some(token.to_string()),
740 raw: None,
741 },
742 Self::Close(close) => match close {
743 Close::Connection(f) => {
744 let (error, error_code) = transport_error(f.error_code);
745 let error = error.map(|transport_error| {
746 ConnectionClosedFrameError::TransportError(transport_error)
747 });
748 QuicFrame::ConnectionClose {
749 error_space: Some(ErrorSpace::TransportError),
750 error,
751 error_code,
752 reason: String::from_utf8(f.reason.to_vec()).ok(),
753 reason_bytes: None,
754 trigger_frame_type: None,
755 }
756 }
757 Close::Application(f) => QuicFrame::ConnectionClose {
758 error_space: Some(ErrorSpace::ApplicationError),
759 error: None,
760 error_code: Some(f.error_code.into_inner()),
761 reason: String::from_utf8(f.reason.to_vec()).ok(),
762 reason_bytes: None,
763 trigger_frame_type: None,
764 },
765 },
766 Self::Datagram(d) => QuicFrame::Datagram {
767 raw: Some(RawInfo {
768 length: Some(d.data.len() as u64),
769 ..Default::default()
770 }),
771 },
772 Self::HandshakeDone => QuicFrame::HandshakeDone { raw: None },
773 Self::PathAck(ack) => QuicFrame::PathAck {
774 path_id: ack.path_id.as_u32().into(),
775 ack_delay: Some(ack.delay as f32),
776 ect1: ack.ecn.as_ref().map(|e| e.ect1),
777 ect0: ack.ecn.as_ref().map(|e| e.ect0),
778 ce: ack.ecn.as_ref().map(|e| e.ce),
779 raw: None,
780 acked_ranges: Some(AckedRanges::Double(
781 ack.into_iter()
782 .map(|range| (*range.start(), *range.end()))
783 .collect(),
784 )),
785 },
786 Self::PathAbandon(frame) => QuicFrame::PathAbandon {
787 path_id: frame.path_id.as_u32().into(),
788 error_code: frame.error_code.into(),
789 raw: None,
790 },
791 Self::PathStatusAvailable(frame) => QuicFrame::PathStatusAvailable {
792 path_id: frame.path_id.as_u32().into(),
793 path_status_sequence_number: frame.status_seq_no.into(),
794 raw: None,
795 },
796 Self::PathStatusBackup(frame) => QuicFrame::PathStatusBackup {
797 path_id: frame.path_id.as_u32().into(),
798 path_status_sequence_number: frame.status_seq_no.into(),
799 raw: None,
800 },
801 Self::PathsBlocked(frame) => QuicFrame::PathsBlocked {
802 maximum_path_id: frame.0.as_u32().into(),
803 raw: None,
804 },
805 Self::PathCidsBlocked(frame) => QuicFrame::PathCidsBlocked {
806 path_id: frame.path_id.as_u32().into(),
807 next_sequence_number: frame.next_seq.into(),
808 raw: None,
809 },
810 Self::MaxPathId(id) => QuicFrame::MaxPathId {
811 maximum_path_id: id.0.as_u32().into(),
812 raw: None,
813 },
814 Self::AckFrequency(f) => QuicFrame::AckFrequency {
815 sequence_number: f.sequence.into_inner(),
816 ack_eliciting_threshold: f.ack_eliciting_threshold.into_inner(),
817 requested_max_ack_delay: f.request_max_ack_delay.into_inner(),
818 reordering_threshold: f.reordering_threshold.into_inner(),
819 raw: None,
820 },
821 Self::ImmediateAck => QuicFrame::ImmediateAck { raw: None },
822 Self::ObservedAddr(f) => QuicFrame::ObservedAddress {
823 sequence_number: f.seq_no.into_inner(),
824 ip_v4: match f.ip {
825 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
826 IpAddr::V6(ipv6_addr) => None,
827 },
828 ip_v6: match f.ip {
829 IpAddr::V4(ipv4_addr) => None,
830 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
831 },
832 port: f.port,
833 raw: None,
834 },
835 Self::AddAddress(f) => QuicFrame::AddAddress {
836 sequence_number: f.seq_no.into_inner(),
837 ip_v4: match f.ip {
838 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
839 IpAddr::V6(ipv6_addr) => None,
840 },
841 ip_v6: match f.ip {
842 IpAddr::V4(ipv4_addr) => None,
843 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
844 },
845 port: f.port,
846 },
847 Self::ReachOut(f) => QuicFrame::ReachOut {
848 round: f.round.into_inner(),
849 ip_v4: match f.ip {
850 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
851 IpAddr::V6(ipv6_addr) => None,
852 },
853 ip_v6: match f.ip {
854 IpAddr::V4(ipv4_addr) => None,
855 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
856 },
857 port: f.port,
858 },
859 Self::RemoveAddress(f) => QuicFrame::RemoveAddress {
860 sequence_number: f.seq_no.into_inner(),
861 },
862 }
863 }
864}
865
866#[cfg(feature = "qlog")]
867impl From<crate::Dir> for StreamType {
868 fn from(value: crate::Dir) -> Self {
869 match value {
870 crate::Dir::Bi => Self::Bidirectional,
871 crate::Dir::Uni => Self::Unidirectional,
872 }
873 }
874}
875
876#[cfg(feature = "qlog")]
877fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
878 match space {
879 SpaceId::Initial => PacketType::Initial,
880 SpaceId::Handshake => PacketType::Handshake,
881 SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
882 SpaceId::Data => PacketType::OneRtt,
883 }
884}
885
886#[cfg(feature = "qlog")]
887fn stringify_cid(cid: ConnectionId) -> String {
888 format!("{cid}")
889}
890
891#[cfg(feature = "qlog")]
892fn tuple_endpoint_info(
893 ip: Option<IpAddr>,
894 port: Option<u16>,
895 cid: Option<ConnectionId>,
896) -> TupleEndpointInfo {
897 let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
898 Some(addr) => match addr {
899 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
900 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
901 },
902 None => (None, None, None, None),
903 };
904 TupleEndpointInfo {
905 ip_v4,
906 port_v4,
907 ip_v6,
908 port_v6,
909 connection_ids: cid.map(|cid| vec![cid.to_string()]),
910 }
911}
912
913#[cfg(feature = "qlog")]
914fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
915 let transport_error = match code {
916 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
917 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
918 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
919 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
920 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
921 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
922 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
923 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
924 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
925 Some(quic::TransportError::TransportParameterError)
926 }
927 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
928 Some(quic::TransportError::ConnectionIdLimitError)
929 }
930 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
931 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
932 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
933 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
934 Some(quic::TransportError::CryptoBufferExceeded)
935 }
936 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
937 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
938 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
939 TransportErrorCode::APPLICATION_ABANDON_PATH => {
941 Some(quic::TransportError::ApplicationAbandonPath)
942 }
943 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
944 Some(quic::TransportError::PathResourceLimitReached)
945 }
946 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
947 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
948 Some(quic::TransportError::NoCidsAvailableForPath)
949 }
950 _ => None,
951 };
952 let code = match transport_error {
953 None => Some(code.into()),
954 Some(_) => None,
955 };
956 (transport_error, code)
957}
958
959#[cfg(feature = "qlog")]
960fn fmt_tuple_id(path_id: u64) -> String {
961 format!("p{path_id}")
962}
963
964#[cfg(feature = "qlog")]
965impl TransportParameters {
966 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
967 ParametersSet {
968 initiator: Some(initiator),
969 resumption_allowed: None,
970 early_data_enabled: None,
971 tls_cipher: None,
972 original_destination_connection_id: self
973 .original_dst_cid
974 .as_ref()
975 .map(ToString::to_string),
976 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
977 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
978 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
979 disable_active_migration: Some(self.disable_active_migration),
980 max_idle_timeout: Some(self.max_idle_timeout.into()),
981 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
982 ack_delay_exponent: Some(self.ack_delay_exponent.into()),
983 max_ack_delay: Some(self.max_ack_delay.into()),
984 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
985 initial_max_data: Some(self.initial_max_data.into()),
986 initial_max_stream_data_bidi_local: Some(
987 self.initial_max_stream_data_bidi_local.into(),
988 ),
989 initial_max_stream_data_bidi_remote: Some(
990 self.initial_max_stream_data_bidi_remote.into(),
991 ),
992 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
993 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
994 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
995 preferred_address: self.preferred_address.as_ref().map(Into::into),
996 min_ack_delay: self.min_ack_delay.map(Into::into),
997 address_discovery: self.address_discovery_role.to_qlog(),
998 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
999 max_remote_nat_traversal_addresses: self
1000 .max_remote_nat_traversal_addresses
1001 .map(|v| u64::from(v.get())),
1002 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1003 grease_quic_bit: Some(self.grease_quic_bit),
1004 unknown_parameters: Default::default(),
1005 }
1006 }
1007
1008 fn to_qlog_restored(self) -> ParametersRestored {
1009 ParametersRestored {
1010 disable_active_migration: Some(self.disable_active_migration),
1011 max_idle_timeout: Some(self.max_idle_timeout.into()),
1012 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1013 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1014 initial_max_data: Some(self.initial_max_data.into()),
1015 initial_max_stream_data_bidi_local: Some(
1016 self.initial_max_stream_data_bidi_local.into(),
1017 ),
1018 initial_max_stream_data_bidi_remote: Some(
1019 self.initial_max_stream_data_bidi_remote.into(),
1020 ),
1021 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1022 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1023 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1024 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1025 grease_quic_bit: Some(self.grease_quic_bit),
1026 }
1027 }
1028}
1029
1030#[cfg(feature = "qlog")]
1031impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
1032 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
1033 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
1034 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
1035 let ip_v4 = value
1036 .address_v4
1037 .map(|addr| addr.ip().to_string())
1038 .unwrap_or_default();
1039 let ip_v6 = value
1040 .address_v6
1041 .map(|addr| addr.ip().to_string())
1042 .unwrap_or_default();
1043 let connection_id = value.connection_id.to_string();
1044 let stateless_reset_token = value.stateless_reset_token.to_string();
1045
1046 Self {
1047 ip_v4,
1048 ip_v6,
1049 port_v4,
1050 port_v6,
1051 connection_id,
1052 stateless_reset_token,
1053 }
1054 }
1055}
1056
1057#[cfg(feature = "qlog")]
1058impl crate::address_discovery::Role {
1059 fn to_qlog(self) -> Option<AddressDiscoveryRole> {
1060 match self {
1061 Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
1062 Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
1063 Self::Both => Some(AddressDiscoveryRole::Both),
1064 Self::Disabled => None,
1065 }
1066 }
1067}