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