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
370pub(super) struct QlogSinkWithTime<'a> {
372 #[cfg(feature = "qlog")]
373 sink: &'a QlogSink,
374 #[cfg(feature = "qlog")]
375 now: Instant,
376 #[cfg(not(feature = "qlog"))]
377 _phantom: PhantomData<&'a ()>,
378}
379
380impl<'a> QlogSinkWithTime<'a> {
381 pub(super) fn emit_timer_stop(&self, timer: Timer) {
382 #[cfg(feature = "qlog")]
383 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
384 }
385
386 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
387 #[cfg(feature = "qlog")]
388 self.sink
389 .emit_timer(timer, TimerOp::Set(expire_at), self.now)
390 }
391
392 pub(super) fn emit_timer_expire(&self, timer: Timer) {
393 #[cfg(feature = "qlog")]
394 self.sink.emit_timer(timer, TimerOp::Expire, self.now)
395 }
396}
397
398#[cfg(feature = "qlog")]
399enum TimerOp {
400 Set(Instant),
401 Expire,
402 Cancelled,
403}
404
405#[derive(Default)]
407pub(crate) struct QlogSentPacket {
408 #[cfg(feature = "qlog")]
409 inner: PacketSent,
410}
411
412impl QlogSentPacket {
413 pub(crate) fn header(
415 &mut self,
416 header: &Header,
417 pn: Option<u64>,
418 space: SpaceId,
419 is_0rtt: bool,
420 path_id: PathId,
421 ) {
422 #[cfg(feature = "qlog")]
423 {
424 self.inner.header.scid = header.src_cid().map(stringify_cid);
425 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
426 self.inner.header.packet_number = pn;
427 self.inner.header.packet_type = packet_type(space, is_0rtt);
428 self.inner.header.path_id = Some(path_id.as_u32() as u64);
429 }
430 }
431
432 pub(crate) fn frame(&mut self, frame: &Frame) {
434 #[cfg(feature = "qlog")]
435 self.frame_raw(frame.to_qlog())
436 }
437
438 pub(crate) fn frame_padding(&mut self, count: usize) {
442 #[cfg(feature = "qlog")]
443 self.frame_raw(QuicFrame::Padding {
444 raw: Some(RawInfo {
445 length: Some(count as u64),
446 payload_length: Some(count as u64),
447 data: None,
448 }),
449 });
450 }
451
452 pub(crate) fn frame_ack(
456 &mut self,
457 delay: u64,
458 ranges: &ArrayRangeSet,
459 ecn: Option<&EcnCounts>,
460 ) {
461 #[cfg(feature = "qlog")]
462 self.frame_raw(QuicFrame::Ack {
463 ack_delay: Some(delay as f32),
464 acked_ranges: Some(AckedRanges::Double(
465 ranges
466 .iter()
467 .map(|range| (range.start, range.end))
468 .collect(),
469 )),
470 ect1: ecn.map(|e| e.ect1),
471 ect0: ecn.map(|e| e.ect0),
472 ce: ecn.map(|e| e.ce),
473 raw: None,
474 });
475 }
476
477 pub(crate) fn frame_path_ack(
481 &mut self,
482 path_id: PathId,
483 delay: u64,
484 ranges: &ArrayRangeSet,
485 ecn: Option<&EcnCounts>,
486 ) {
487 #[cfg(feature = "qlog")]
488 self.frame_raw(QuicFrame::PathAck {
489 path_id: path_id.as_u32() as u64,
490 ack_delay: Some(delay as f32),
491 acked_ranges: Some(AckedRanges::Double(
492 ranges
493 .iter()
494 .map(|range| (range.start, range.end))
495 .collect(),
496 )),
497 ect1: ecn.map(|e| e.ect1),
498 ect0: ecn.map(|e| e.ect0),
499 ce: ecn.map(|e| e.ce),
500 raw: None,
501 });
502 }
503
504 pub(crate) fn frame_datagram(&mut self, len: u64) {
508 #[cfg(feature = "qlog")]
509 self.frame_raw(QuicFrame::Datagram {
510 raw: Some(RawInfo {
511 length: Some(len),
512 ..Default::default()
513 }),
514 });
515 }
516
517 pub(crate) fn frame_stream(&mut self, meta: &StreamMeta) {
521 #[cfg(feature = "qlog")]
522 self.frame_raw(QuicFrame::Stream {
523 stream_id: meta.id.into(),
524 offset: Some(meta.offsets.start),
525 fin: Some(meta.fin),
526 raw: Some(RawInfo {
527 length: Some(meta.offsets.end - meta.offsets.start),
528 ..Default::default()
529 }),
530 });
531 }
532
533 #[cfg(feature = "qlog")]
538 fn frame_raw(&mut self, frame: QuicFrame) {
539 self.inner.frames.get_or_insert_default().push(frame);
540 }
541
542 pub(super) fn finalize(&mut self, len: usize) {
544 #[cfg(feature = "qlog")]
545 {
546 self.inner.header.length = Some(len as u16);
547 }
548 }
549}
550
551pub(crate) struct QlogRecvPacket {
553 #[cfg(feature = "qlog")]
554 inner: PacketReceived,
555 #[cfg(feature = "qlog")]
556 padding: usize,
557}
558
559impl QlogRecvPacket {
560 pub(crate) fn new(len: usize) -> Self {
564 #[cfg(not(feature = "qlog"))]
565 let this = Self {};
566
567 #[cfg(feature = "qlog")]
568 let this = {
569 let mut this = Self {
570 inner: Default::default(),
571 padding: 0,
572 };
573 this.inner.header.length = Some(len as u16);
574 this
575 };
576
577 this
578 }
579
580 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
582 #[cfg(feature = "qlog")]
583 {
584 let is_0rtt = !header.is_1rtt();
585 self.inner.header.scid = header.src_cid().map(stringify_cid);
586 self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
587 self.inner.header.packet_number = pn;
588 self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
589 self.inner.header.path_id = Some(path_id.as_u32() as u64);
590 }
591 }
592
593 pub(crate) fn frame(&mut self, frame: &Frame) {
595 #[cfg(feature = "qlog")]
596 {
597 if matches!(frame, crate::Frame::Padding) {
598 self.padding += 1;
599 } else {
600 self.emit_padding();
601 self.inner
602 .frames
603 .get_or_insert_default()
604 .push(frame.to_qlog())
605 }
606 }
607 }
608
609 #[cfg(feature = "qlog")]
610 fn emit_padding(&mut self) {
611 if self.padding > 0 {
612 self.inner
613 .frames
614 .get_or_insert_default()
615 .push(QuicFrame::Padding {
616 raw: Some(RawInfo {
617 length: Some(self.padding as u64),
618 payload_length: Some(self.padding as u64),
619 data: None,
620 }),
621 });
622 self.padding = 0;
623 }
624 }
625}
626
627#[cfg(feature = "qlog")]
628impl Frame {
629 pub(crate) fn to_qlog(&self) -> QuicFrame {
631 match self {
632 Self::Padding => QuicFrame::Padding {
633 raw: Some(RawInfo {
634 length: None,
635 payload_length: Some(1),
636 data: None,
637 }),
638 },
639 Self::Ping => QuicFrame::Ping { raw: None },
640 Self::Ack(f) => QuicFrame::Ack {
641 ack_delay: Some(f.delay as f32),
642 acked_ranges: Some(AckedRanges::Double(
643 f.iter()
644 .map(|range| (*range.start(), *range.end()))
645 .collect(),
646 )),
647 ect1: f.ecn.as_ref().map(|e| e.ect1),
648 ect0: f.ecn.as_ref().map(|e| e.ect0),
649 ce: f.ecn.as_ref().map(|e| e.ce),
650 raw: None,
651 },
652 Self::ResetStream(f) => QuicFrame::ResetStream {
653 stream_id: f.id.into(),
654 error_code: Some(f.error_code.into_inner()),
655 final_size: f.final_offset.into(),
656 error: ApplicationError::Unknown,
657 raw: None,
658 },
659 Self::StopSending(f) => QuicFrame::StopSending {
660 stream_id: f.id.into(),
661 error_code: Some(f.error_code.into_inner()),
662 error: ApplicationError::Unknown,
663 raw: None,
664 },
665 Self::Crypto(f) => QuicFrame::Crypto {
666 offset: f.offset,
667 raw: Some(RawInfo {
668 length: Some(f.data.len() as u64),
669 ..Default::default()
670 }),
671 },
672 Self::NewToken(f) => QuicFrame::NewToken {
673 token: qlog::Token {
674 ty: Some(TokenType::Retry),
675 raw: Some(RawInfo {
676 data: HexSlice::maybe_string(Some(&f.token)),
677 length: Some(f.token.len() as u64),
678 payload_length: None,
679 }),
680 details: None,
681 },
682 raw: None,
683 },
684 Self::Stream(s) => QuicFrame::Stream {
685 stream_id: s.id.into(),
686 offset: Some(s.offset),
687 fin: Some(s.fin),
688 raw: Some(RawInfo {
689 length: Some(s.data.len() as u64),
690 ..Default::default()
691 }),
692 },
693 Self::MaxData(v) => QuicFrame::MaxData {
694 maximum: (*v).into(),
695 raw: None,
696 },
697 Self::MaxStreamData { id, offset } => QuicFrame::MaxStreamData {
698 stream_id: (*id).into(),
699 maximum: *offset,
700 raw: None,
701 },
702 Self::MaxStreams { dir, count } => QuicFrame::MaxStreams {
703 maximum: *count,
704 stream_type: (*dir).into(),
705 raw: None,
706 },
707 Self::DataBlocked { offset } => QuicFrame::DataBlocked {
708 limit: *offset,
709 raw: None,
710 },
711 Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked {
712 stream_id: (*id).into(),
713 limit: *offset,
714 raw: None,
715 },
716 Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked {
717 stream_type: (*dir).into(),
718 limit: *limit,
719 raw: None,
720 },
721 Self::NewConnectionId(f) => QuicFrame::NewConnectionId {
722 sequence_number: f.sequence,
723 retire_prior_to: f.retire_prior_to,
724 connection_id_length: Some(f.id.len() as u8),
725 connection_id: f.id.to_string(),
726 stateless_reset_token: Some(f.reset_token.to_string()),
727 raw: None,
728 },
729 Self::RetireConnectionId(f) => QuicFrame::RetireConnectionId {
730 sequence_number: f.sequence,
731 raw: None,
732 },
733 Self::PathChallenge(token) => QuicFrame::PathChallenge {
734 data: Some(token.to_string()),
735 raw: None,
736 },
737 Self::PathResponse(token) => QuicFrame::PathResponse {
738 data: Some(token.to_string()),
739 raw: None,
740 },
741 Self::Close(close) => match close {
742 Close::Connection(f) => {
743 let (error, error_code) = transport_error(f.error_code);
744 let error = error.map(|transport_error| {
745 ConnectionClosedFrameError::TransportError(transport_error)
746 });
747 QuicFrame::ConnectionClose {
748 error_space: Some(ErrorSpace::TransportError),
749 error,
750 error_code,
751 reason: String::from_utf8(f.reason.to_vec()).ok(),
752 reason_bytes: None,
753 trigger_frame_type: None,
754 }
755 }
756 Close::Application(f) => QuicFrame::ConnectionClose {
757 error_space: Some(ErrorSpace::ApplicationError),
758 error: None,
759 error_code: Some(f.error_code.into_inner()),
760 reason: String::from_utf8(f.reason.to_vec()).ok(),
761 reason_bytes: None,
762 trigger_frame_type: None,
763 },
764 },
765 Self::Datagram(d) => QuicFrame::Datagram {
766 raw: Some(RawInfo {
767 length: Some(d.data.len() as u64),
768 ..Default::default()
769 }),
770 },
771 Self::HandshakeDone => QuicFrame::HandshakeDone { raw: None },
772 Self::PathAck(ack) => QuicFrame::PathAck {
773 path_id: ack.path_id.as_u32().into(),
774 ack_delay: Some(ack.delay as f32),
775 ect1: ack.ecn.as_ref().map(|e| e.ect1),
776 ect0: ack.ecn.as_ref().map(|e| e.ect0),
777 ce: ack.ecn.as_ref().map(|e| e.ce),
778 raw: None,
779 acked_ranges: Some(AckedRanges::Double(
780 ack.into_iter()
781 .map(|range| (*range.start(), *range.end()))
782 .collect(),
783 )),
784 },
785 Self::PathAbandon(frame) => QuicFrame::PathAbandon {
786 path_id: frame.path_id.as_u32().into(),
787 error_code: frame.error_code.into(),
788 raw: None,
789 },
790 Self::PathStatusAvailable(frame) => QuicFrame::PathStatusAvailable {
791 path_id: frame.path_id.as_u32().into(),
792 path_status_sequence_number: frame.status_seq_no.into(),
793 raw: None,
794 },
795 Self::PathStatusBackup(frame) => QuicFrame::PathStatusBackup {
796 path_id: frame.path_id.as_u32().into(),
797 path_status_sequence_number: frame.status_seq_no.into(),
798 raw: None,
799 },
800 Self::PathsBlocked(frame) => QuicFrame::PathsBlocked {
801 maximum_path_id: frame.0.as_u32().into(),
802 raw: None,
803 },
804 Self::PathCidsBlocked(frame) => QuicFrame::PathCidsBlocked {
805 path_id: frame.path_id.as_u32().into(),
806 next_sequence_number: frame.next_seq.into(),
807 raw: None,
808 },
809 Self::MaxPathId(id) => QuicFrame::MaxPathId {
810 maximum_path_id: id.0.as_u32().into(),
811 raw: None,
812 },
813 Self::AckFrequency(f) => QuicFrame::AckFrequency {
814 sequence_number: f.sequence.into_inner(),
815 ack_eliciting_threshold: f.ack_eliciting_threshold.into_inner(),
816 requested_max_ack_delay: f.request_max_ack_delay.into_inner(),
817 reordering_threshold: f.reordering_threshold.into_inner(),
818 raw: None,
819 },
820 Self::ImmediateAck => QuicFrame::ImmediateAck { raw: None },
821 Self::ObservedAddr(f) => QuicFrame::ObservedAddress {
822 sequence_number: f.seq_no.into_inner(),
823 ip_v4: match f.ip {
824 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
825 IpAddr::V6(ipv6_addr) => None,
826 },
827 ip_v6: match f.ip {
828 IpAddr::V4(ipv4_addr) => None,
829 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
830 },
831 port: f.port,
832 raw: None,
833 },
834 Self::AddAddress(f) => QuicFrame::AddAddress {
835 sequence_number: f.seq_no.into_inner(),
836 ip_v4: match f.ip {
837 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
838 IpAddr::V6(ipv6_addr) => None,
839 },
840 ip_v6: match f.ip {
841 IpAddr::V4(ipv4_addr) => None,
842 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
843 },
844 port: f.port,
845 },
846 Self::ReachOut(f) => QuicFrame::ReachOut {
847 round: f.round.into_inner(),
848 ip_v4: match f.ip {
849 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
850 IpAddr::V6(ipv6_addr) => None,
851 },
852 ip_v6: match f.ip {
853 IpAddr::V4(ipv4_addr) => None,
854 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
855 },
856 port: f.port,
857 },
858 Self::RemoveAddress(f) => QuicFrame::RemoveAddress {
859 sequence_number: f.seq_no.into_inner(),
860 },
861 }
862 }
863}
864
865#[cfg(feature = "qlog")]
866impl From<crate::Dir> for StreamType {
867 fn from(value: crate::Dir) -> Self {
868 match value {
869 crate::Dir::Bi => Self::Bidirectional,
870 crate::Dir::Uni => Self::Unidirectional,
871 }
872 }
873}
874
875#[cfg(feature = "qlog")]
876fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
877 match space {
878 SpaceId::Initial => PacketType::Initial,
879 SpaceId::Handshake => PacketType::Handshake,
880 SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
881 SpaceId::Data => PacketType::OneRtt,
882 }
883}
884
885#[cfg(feature = "qlog")]
886fn stringify_cid(cid: ConnectionId) -> String {
887 format!("{cid}")
888}
889
890#[cfg(feature = "qlog")]
891fn tuple_endpoint_info(
892 ip: Option<IpAddr>,
893 port: Option<u16>,
894 cid: Option<ConnectionId>,
895) -> TupleEndpointInfo {
896 let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
897 Some(addr) => match addr {
898 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
899 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
900 },
901 None => (None, None, None, None),
902 };
903 TupleEndpointInfo {
904 ip_v4,
905 port_v4,
906 ip_v6,
907 port_v6,
908 connection_ids: cid.map(|cid| vec![cid.to_string()]),
909 }
910}
911
912#[cfg(feature = "qlog")]
913fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
914 let transport_error = match code {
915 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
916 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
917 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
918 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
919 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
920 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
921 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
922 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
923 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
924 Some(quic::TransportError::TransportParameterError)
925 }
926 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
927 Some(quic::TransportError::ConnectionIdLimitError)
928 }
929 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
930 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
931 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
932 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
933 Some(quic::TransportError::CryptoBufferExceeded)
934 }
935 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
936 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
937 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
938 TransportErrorCode::APPLICATION_ABANDON_PATH => {
940 Some(quic::TransportError::ApplicationAbandonPath)
941 }
942 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
943 Some(quic::TransportError::PathResourceLimitReached)
944 }
945 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
946 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
947 Some(quic::TransportError::NoCidsAvailableForPath)
948 }
949 _ => None,
950 };
951 let code = match transport_error {
952 None => Some(code.into()),
953 Some(_) => None,
954 };
955 (transport_error, code)
956}
957
958#[cfg(feature = "qlog")]
959fn fmt_tuple_id(path_id: u64) -> String {
960 format!("p{path_id}")
961}
962
963#[cfg(feature = "qlog")]
964impl TransportParameters {
965 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
966 ParametersSet {
967 initiator: Some(initiator),
968 resumption_allowed: None,
969 early_data_enabled: None,
970 tls_cipher: None,
971 original_destination_connection_id: self
972 .original_dst_cid
973 .as_ref()
974 .map(ToString::to_string),
975 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
976 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
977 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
978 disable_active_migration: Some(self.disable_active_migration),
979 max_idle_timeout: Some(self.max_idle_timeout.into()),
980 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
981 ack_delay_exponent: Some(self.ack_delay_exponent.into()),
982 max_ack_delay: Some(self.max_ack_delay.into()),
983 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
984 initial_max_data: Some(self.initial_max_data.into()),
985 initial_max_stream_data_bidi_local: Some(
986 self.initial_max_stream_data_bidi_local.into(),
987 ),
988 initial_max_stream_data_bidi_remote: Some(
989 self.initial_max_stream_data_bidi_remote.into(),
990 ),
991 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
992 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
993 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
994 preferred_address: self.preferred_address.as_ref().map(Into::into),
995 min_ack_delay: self.min_ack_delay.map(Into::into),
996 address_discovery: self.address_discovery_role.to_qlog(),
997 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
998 max_remote_nat_traversal_addresses: self
999 .max_remote_nat_traversal_addresses
1000 .map(|v| u64::from(v.get())),
1001 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1002 grease_quic_bit: Some(self.grease_quic_bit),
1003 unknown_parameters: Default::default(),
1004 }
1005 }
1006
1007 fn to_qlog_restored(self) -> ParametersRestored {
1008 ParametersRestored {
1009 disable_active_migration: Some(self.disable_active_migration),
1010 max_idle_timeout: Some(self.max_idle_timeout.into()),
1011 max_udp_payload_size: Some(self.max_udp_payload_size.into()),
1012 active_connection_id_limit: Some(self.active_connection_id_limit.into()),
1013 initial_max_data: Some(self.initial_max_data.into()),
1014 initial_max_stream_data_bidi_local: Some(
1015 self.initial_max_stream_data_bidi_local.into(),
1016 ),
1017 initial_max_stream_data_bidi_remote: Some(
1018 self.initial_max_stream_data_bidi_remote.into(),
1019 ),
1020 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
1021 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
1022 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
1023 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
1024 grease_quic_bit: Some(self.grease_quic_bit),
1025 }
1026 }
1027}
1028
1029#[cfg(feature = "qlog")]
1030impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
1031 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
1032 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
1033 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
1034 let ip_v4 = value
1035 .address_v4
1036 .map(|addr| addr.ip().to_string())
1037 .unwrap_or_default();
1038 let ip_v6 = value
1039 .address_v6
1040 .map(|addr| addr.ip().to_string())
1041 .unwrap_or_default();
1042 let connection_id = value.connection_id.to_string();
1043 let stateless_reset_token = value.stateless_reset_token.to_string();
1044
1045 Self {
1046 ip_v4,
1047 ip_v6,
1048 port_v4,
1049 port_v6,
1050 connection_id,
1051 stateless_reset_token,
1052 }
1053 }
1054}
1055
1056#[cfg(feature = "qlog")]
1057impl crate::address_discovery::Role {
1058 fn to_qlog(self) -> Option<AddressDiscoveryRole> {
1059 match self {
1060 Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
1061 Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
1062 Self::Both => Some(AddressDiscoveryRole::Both),
1063 Self::Disabled => None,
1064 }
1065 }
1066}