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