1use core::fmt::Debug;
6use core::time::Duration;
7
8use derivative::Derivative;
9use netstack3_base::{Inspectable, Inspector, Instant};
10
11use crate::internal::buffer::{ReceiveBuffer, SendBuffer};
12use crate::internal::congestion::LossRecoveryMode;
13use crate::internal::counters::TcpCountersWithSocketInner;
14use crate::internal::state::{
15 CloseWait, Closed, Closing, Established, FinWait1, FinWait2, LastAck, Listen, Recv, RecvParams,
16 Send, State, SynRcvd, SynSent, TimeWait,
17};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct TcpSocketInfo<I> {
22 pub state: netstack3_base::TcpSocketState,
24 pub ca_state: CongestionControlState,
26 pub rto: Option<Duration>,
28 pub rtt: Option<Duration>,
30 pub rtt_var: Option<Duration>,
32 pub snd_ssthresh: u32,
34 pub snd_cwnd: u32,
36 pub retransmits: u64,
38 pub last_ack_recv: Option<I>,
40 pub segs_out: u64,
42 pub segs_in: u64,
44 pub snd_mss: Option<u32>,
46 pub rcv_mss: Option<u32>,
48 pub last_data_sent: Option<I>,
50}
51
52impl<I: Instant> Inspectable for TcpSocketInfo<I> {
53 fn record<II: Inspector>(&self, inspector: &mut II) {
54 let Self {
55 ca_state,
56 rto,
57 rtt,
58 rtt_var,
59 snd_ssthresh,
60 snd_cwnd,
61 last_ack_recv,
62 last_data_sent,
63 snd_mss,
64 rcv_mss,
65
66 state: _,
68 retransmits: _,
70 segs_out: _,
71 segs_in: _,
72 } = self;
73
74 inspector.record_debug("CongestionControlState", ca_state);
75 if let Some(rto) = rto {
76 inspector.record_uint("RtoMs", u64::try_from(rto.as_millis()).unwrap_or(u64::MAX));
77 }
78 if let Some(rtt) = rtt {
79 inspector.record_uint("RttMs", u64::try_from(rtt.as_millis()).unwrap_or(u64::MAX));
80 }
81 if let Some(rtt_var) = rtt_var {
82 inspector
83 .record_uint("RttVarMs", u64::try_from(rtt_var.as_millis()).unwrap_or(u64::MAX));
84 }
85 inspector.record_uint("SndSsthresh", *snd_ssthresh);
86 inspector.record_uint("SndCwnd", *snd_cwnd);
87 if let Some(last_ack_recv) = last_ack_recv {
88 inspector.record_inspectable_value("LastAckRecv", last_ack_recv);
89 }
90 if let Some(last_data_sent) = last_data_sent {
91 inspector.record_inspectable_value("LastDataSent", last_data_sent);
92 }
93 if let Some(snd_mss) = snd_mss {
94 inspector.record_uint("SndMss", *snd_mss);
95 }
96 if let Some(rcv_mss) = rcv_mss {
97 inspector.record_uint("RcvMss", *rcv_mss);
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
104pub enum CongestionControlState {
105 #[default]
107 Open,
108 Disorder,
110 CongestionWindowReduced,
112 Recovery,
114 Loss,
116}
117
118#[derive(Debug, Clone, Derivative)]
121#[derivative(Default(bound = ""))]
122#[cfg_attr(test, derive(PartialEq, Eq))]
123pub(crate) struct SendInfo<I> {
124 rto: Option<Duration>,
125 rtt: Option<Duration>,
126 rtt_var: Option<Duration>,
127 snd_ssthresh: u32,
128 snd_cwnd: u32,
129 snd_mss: Option<u32>,
130 last_data_sent: Option<I>,
131 ca_state: CongestionControlState,
132}
133
134impl<I: Instant> SendInfo<I> {
135 pub(super) fn from_send<S: SendBuffer, const FIN: bool>(snd: &Send<I, S, FIN>) -> Self {
136 let cc = &snd.congestion_control;
137 let est = &snd.rtt_estimator;
138
139 Self {
140 snd_cwnd: cc.inspect_cwnd().cwnd(),
141 snd_ssthresh: cc.slow_start_threshold(),
142 ca_state: match cc.inspect_loss_recovery_mode() {
143 Some(LossRecoveryMode::FastRecovery) => CongestionControlState::Recovery,
144 Some(LossRecoveryMode::SackRecovery) => CongestionControlState::Recovery,
145 None => CongestionControlState::Open,
146 },
147 rtt: est.srtt(),
148 rtt_var: est.rtt_var(),
149 rto: Some(est.rto().into()),
150 snd_mss: Some(u32::from(cc.mss())),
151 last_data_sent: snd.last_data_sent,
152 }
153 }
154}
155
156#[derive(Debug, Clone, Derivative)]
159#[derivative(Default(bound = ""))]
160#[cfg_attr(test, derive(PartialEq, Eq))]
161struct RecvInfo<I> {
162 last_ack_recv: Option<I>,
163 rcv_mss: Option<u32>,
164}
165
166impl<I: Instant> RecvInfo<I> {
167 fn from_recv<R: ReceiveBuffer>(rcv: &Recv<I, R>) -> Self {
168 Self { last_ack_recv: rcv.last_segment_at, rcv_mss: Some(u32::from(rcv.mss)) }
169 }
170
171 fn from_recv_params(rcv: &RecvParams<I>) -> Self {
172 let RecvParams { last_ack_recv, ack: _, wnd_scale: _, wnd: _, ts_opt: _ } = rcv;
173 Self { last_ack_recv: *last_ack_recv, rcv_mss: None }
174 }
175}
176
177struct CounterParams {
180 retransmits: u64,
181 segs_out: u64,
182 segs_in: u64,
183}
184
185impl CounterParams {
186 fn from_counters(counters: &TcpCountersWithSocketInner) -> Self {
187 Self {
188 retransmits: counters.retransmits.get(),
189 segs_out: counters.segments_sent.get(),
190 segs_in: counters.received_segments_dispatched.get(),
191 }
192 }
193}
194
195impl<I: Instant> TcpSocketInfo<I> {
196 pub(crate) fn from_partial_state(
199 state: netstack3_base::TcpSocketState,
200 counters: &TcpCountersWithSocketInner,
201 ) -> Self {
202 let SendInfo {
203 rto,
204 rtt,
205 rtt_var,
206 snd_ssthresh,
207 snd_cwnd,
208 last_data_sent,
209 ca_state,
210 snd_mss: _,
211 } = SendInfo::default();
212 let RecvInfo { last_ack_recv, rcv_mss: _ } = RecvInfo::default();
213 let CounterParams { retransmits, segs_out, segs_in } =
214 CounterParams::from_counters(counters);
215
216 Self {
217 state,
218 retransmits,
219 segs_out,
220 ca_state,
221 rto,
222 rtt,
223 rtt_var,
224 snd_ssthresh,
225 snd_cwnd,
226 last_ack_recv,
227 segs_in,
228 snd_mss: None,
229 rcv_mss: None,
230 last_data_sent,
231 }
232 }
233
234 pub(crate) fn from_full_state<R, S, ActiveOpen>(
237 state: &State<I, R, S, ActiveOpen>,
238 counters: &TcpCountersWithSocketInner,
239 ) -> Self
240 where
241 R: ReceiveBuffer,
242 S: SendBuffer,
243 {
244 let (state, send_params, recv_params) = match state {
245 State::Closed(Closed { reason: _ }) => {
246 (netstack3_base::TcpSocketState::Close, SendInfo::default(), RecvInfo::default())
247 }
248 State::Listen(Listen {
249 iss: _,
250 timestamp_offset: _,
251 buffer_sizes: _,
252 device_mss: _,
253 default_mss: _,
254 user_timeout: _,
255 }) => {
256 (netstack3_base::TcpSocketState::Listen, SendInfo::default(), RecvInfo::default())
257 }
258 State::SynSent(SynSent {
259 iss: _,
260 timestamp: _,
261 retrans_timer: _,
262 active_open: _,
263 buffer_sizes: _,
264 device_mss: _,
265 default_mss: _,
266 rcv_wnd_scale: _,
267 ts_opt: _,
268 }) => {
269 (netstack3_base::TcpSocketState::SynSent, SendInfo::default(), RecvInfo::default())
270 }
271 State::SynRcvd(SynRcvd {
272 iss: _,
273 irs: _,
274 timestamp: _,
275 retrans_timer: _,
276 simultaneous_open: _,
277 buffer_sizes: _,
278 smss: _,
279 rcv_wnd_scale: _,
280 snd_wnd_scale: _,
281 sack_permitted: _,
282 rcv: _,
284 }) => {
285 (netstack3_base::TcpSocketState::SynRecv, SendInfo::default(), RecvInfo::default())
286 }
287 State::Established(Established { snd, rcv }) => (
288 netstack3_base::TcpSocketState::Established,
289 SendInfo::from_send(snd.get()),
290 RecvInfo::from_recv(rcv.get()),
291 ),
292 State::FinWait1(FinWait1 { snd, rcv }) => (
293 netstack3_base::TcpSocketState::FinWait1,
294 SendInfo::from_send(snd.get()),
295 RecvInfo::from_recv(rcv.get()),
296 ),
297 State::FinWait2(FinWait2 { last_seq: _, rcv, timeout_at: _, snd_info }) => (
298 netstack3_base::TcpSocketState::FinWait2,
299 snd_info.clone(),
300 RecvInfo::from_recv(rcv),
301 ),
302 State::CloseWait(CloseWait { snd, closed_rcv }) => (
303 netstack3_base::TcpSocketState::CloseWait,
304 SendInfo::from_send(snd.get()),
305 RecvInfo::from_recv_params(closed_rcv),
306 ),
307 State::Closing(Closing { snd, closed_rcv }) => (
308 netstack3_base::TcpSocketState::Closing,
309 SendInfo::from_send(snd),
310 RecvInfo::from_recv_params(closed_rcv),
311 ),
312 State::LastAck(LastAck { snd, closed_rcv }) => (
313 netstack3_base::TcpSocketState::LastAck,
314 SendInfo::from_send(snd),
315 RecvInfo::from_recv_params(closed_rcv),
316 ),
317 State::TimeWait(TimeWait { last_seq: _, expiry: _, closed_rcv, snd_info }) => (
318 netstack3_base::TcpSocketState::TimeWait,
319 snd_info.clone(),
320 RecvInfo::from_recv_params(closed_rcv),
321 ),
322 };
323
324 let SendInfo {
325 snd_cwnd,
326 snd_ssthresh,
327 ca_state,
328 rtt,
329 rtt_var,
330 rto,
331 snd_mss,
332 last_data_sent,
333 } = send_params;
334 let RecvInfo { last_ack_recv, rcv_mss } = recv_params;
335 let CounterParams { retransmits, segs_out, segs_in } =
336 CounterParams::from_counters(counters);
337
338 Self {
339 state,
340 ca_state,
341 rto,
342 rtt,
343 rtt_var,
344 snd_ssthresh,
345 snd_cwnd,
346 retransmits,
347 last_ack_recv,
348 segs_out,
349 segs_in,
350 snd_mss,
351 rcv_mss,
352 last_data_sent,
353 }
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 use core::time::Duration;
362
363 use netstack3_base::testutil::FakeInstant;
364 use netstack3_base::{
365 EffectiveMss, Mss, MssSizeLimiters, SeqNum, TcpSocketState, Timestamp, WindowScale,
366 WindowSize,
367 };
368
369 use crate::internal::buffer::Assembler;
370 use crate::internal::congestion::CongestionControl;
371 use crate::internal::rtt::{Estimator, RttSampler};
372 use crate::internal::state::{Established, Listen, Recv, RecvBufferState, Send, State};
373 use crate::internal::timestamp::TimestampOptionState;
374 use crate::testutil::RingBuffer;
375
376 impl TcpCountersWithSocketInner {
377 fn new_for_test(retransmits: u64, segs_out: u64, segs_in: u64) -> Self {
378 let counters = Self::default();
379 counters.retransmits.add(retransmits);
380 counters.segments_sent.add(segs_out);
381 counters.received_segments_dispatched.add(segs_in);
382 counters
383 }
384 }
385
386 #[test]
387 fn test_from_partial_state() {
388 let state = TcpSocketState::Listen;
389 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
390
391 let info = TcpSocketInfo::<FakeInstant>::from_partial_state(state, &counters);
392
393 assert_eq!(
394 info,
395 TcpSocketInfo {
396 state: TcpSocketState::Listen,
397 retransmits: 5,
398 segs_out: 10,
399 segs_in: 20,
400
401 ca_state: CongestionControlState::Open,
403 rto: None,
404 rtt: None,
405 rtt_var: None,
406 snd_ssthresh: 0,
407 snd_cwnd: 0,
408 last_ack_recv: None,
409 snd_mss: None,
410 rcv_mss: None,
411 last_data_sent: None,
412 }
413 );
414 }
415
416 #[test]
417 fn test_from_full_state_listen() {
418 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Listen(Listen {
419 iss: SeqNum::new(100),
420 timestamp_offset: Timestamp::new(0),
421 buffer_sizes: Default::default(),
422 device_mss: Mss::new(1460).unwrap(),
423 default_mss: Mss::new(536).unwrap(),
424 user_timeout: None,
425 });
426
427 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
428
429 let info = TcpSocketInfo::from_full_state(&state, &counters);
430
431 assert_eq!(
432 info,
433 TcpSocketInfo {
434 state: TcpSocketState::Listen,
435 retransmits: 5,
436 segs_out: 10,
437 segs_in: 20,
438
439 ca_state: CongestionControlState::Open,
441 rto: None,
442 rtt: None,
443 rtt_var: None,
444 snd_ssthresh: 0,
445 snd_cwnd: 0,
446 last_ack_recv: None,
447 snd_mss: None,
448 rcv_mss: None,
449 last_data_sent: None,
450 }
451 );
452 }
453
454 #[test]
455 fn test_from_full_state_established() {
456 let now = FakeInstant::from(Duration::from_secs(10));
457 let mss = Mss::new(1460).unwrap();
458 let effective_mss =
459 EffectiveMss::from_mss(mss, MssSizeLimiters { timestamp_enabled: false });
460
461 let mut rtt_estimator = Estimator::default();
462 let rtt = Duration::from_millis(50);
463 rtt_estimator.sample(rtt);
464
465 let mut congestion_control = CongestionControl::cubic_with_mss(effective_mss);
466 congestion_control.inflate_cwnd(u32::from(mss));
467
468 let send = Send {
469 nxt: SeqNum::new(200),
470 max: SeqNum::new(200),
471 una: SeqNum::new(100),
472 wnd: WindowSize::new(1000).unwrap(),
473 wnd_scale: WindowScale::default(),
474 wnd_max: WindowSize::new(1000).unwrap(),
475 wl1: SeqNum::new(100),
476 wl2: SeqNum::new(100),
477 last_push: SeqNum::new(200),
478 rtt_sampler: RttSampler::default(),
479 rtt_estimator,
480 timer: None,
481 congestion_control,
482 last_data_sent: Some(now - Duration::from_secs(1)),
483 buffer: RingBuffer::new(1000),
484 };
485
486 let recv = Recv {
487 buffer: RecvBufferState::Open {
488 buffer: RingBuffer::new(1000),
489 assembler: Assembler::new(SeqNum::new(50)),
490 },
491 remaining_quickacks: Default::default(),
492 last_segment_at: Some(now - Duration::from_secs(2)),
493 timer: None,
494 mss: effective_mss,
495 wnd_scale: WindowScale::default(),
496 last_window_update: (SeqNum::new(50), WindowSize::new(1000).unwrap()),
497 sack_permitted: false,
498 ts_opt: TimestampOptionState::Disabled,
499 };
500
501 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Established(Established {
502 snd: send.into(),
503 rcv: recv.into(),
504 });
505
506 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
507 let info = TcpSocketInfo::from_full_state(&state, &counters);
508
509 assert_eq!(
510 info,
511 TcpSocketInfo {
512 state: TcpSocketState::Established,
513 ca_state: CongestionControlState::Open,
514 rto: Some(Duration::from_millis(200)),
515 rtt: Some(rtt),
516 rtt_var: Some(rtt / 2),
517 snd_ssthresh: u32::MAX,
518 snd_cwnd: 4380 + 1460,
519 retransmits: 5,
520 last_ack_recv: Some(now - Duration::from_secs(2)),
521 segs_out: 10,
522 segs_in: 20,
523 snd_mss: Some(1460),
524 rcv_mss: Some(1460),
525 last_data_sent: Some(now - Duration::from_secs(1))
526 }
527 );
528 }
529
530 #[test]
531 fn test_from_full_state_recovery() {
532 let mss = Mss::new(1460).unwrap();
533 let effective_mss =
534 EffectiveMss::from_mss(mss, MssSizeLimiters { timestamp_enabled: false });
535
536 let mut congestion_control = CongestionControl::cubic_with_mss(effective_mss);
537 let ack = SeqNum::new(100);
539 let nxt = SeqNum::new(200);
540 let _ = congestion_control.on_dup_ack(ack, nxt);
541 let _ = congestion_control.on_dup_ack(ack, nxt);
542 let _ = congestion_control.on_dup_ack(ack, nxt);
543
544 let send = Send {
545 nxt,
546 max: nxt,
547 una: ack,
548 wnd: WindowSize::new(1000).unwrap(),
549 wnd_scale: WindowScale::default(),
550 wnd_max: WindowSize::new(1000).unwrap(),
551 wl1: ack,
552 wl2: ack,
553 last_push: nxt,
554 rtt_sampler: RttSampler::default(),
555 rtt_estimator: Estimator::default(),
556 timer: None,
557 congestion_control,
558 last_data_sent: None,
559 buffer: RingBuffer::new(1000),
560 };
561
562 let recv = Recv {
563 buffer: RecvBufferState::Open {
564 buffer: RingBuffer::new(1000),
565 assembler: Assembler::new(SeqNum::new(50)),
566 },
567 remaining_quickacks: Default::default(),
568 last_segment_at: None,
569 timer: None,
570 mss: effective_mss,
571 wnd_scale: WindowScale::default(),
572 last_window_update: (SeqNum::new(50), WindowSize::new(1000).unwrap()),
573 sack_permitted: false,
574 ts_opt: TimestampOptionState::Disabled,
575 };
576
577 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Established(Established {
578 snd: send.into(),
579 rcv: recv.into(),
580 });
581
582 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
583 let info = TcpSocketInfo::from_full_state(&state, &counters);
584
585 assert_eq!(
586 info,
587 TcpSocketInfo {
588 state: TcpSocketState::Established,
589 retransmits: 5,
590 segs_out: 10,
591 segs_in: 20,
592 ca_state: CongestionControlState::Recovery,
593 rto: Some(Duration::from_secs(1)),
594 rtt: None,
595 rtt_var: None,
596 snd_ssthresh: 3066,
597 snd_cwnd: 7300,
598 last_ack_recv: None,
599 snd_mss: Some(1460),
600 rcv_mss: Some(1460),
601 last_data_sent: None,
602 }
603 );
604 }
605
606 #[cfg(target_os = "fuchsia")]
607 #[test]
608 fn test_inspect_tcp_socket_info() {
609 use diagnostics_assertions::assert_data_tree;
610 use diagnostics_traits::FuchsiaInspector;
611 use fuchsia_inspect::Inspector;
612
613 let info = TcpSocketInfo::<FakeInstant> {
614 state: TcpSocketState::Established,
615 ca_state: CongestionControlState::Open,
616 rto: Some(Duration::from_millis(200)),
617 rtt: Some(Duration::from_millis(50)),
618 rtt_var: Some(Duration::from_millis(25)),
619 snd_ssthresh: 1000,
620 snd_cwnd: 2000,
621 retransmits: 5,
622 last_ack_recv: None,
623 segs_out: 10,
624 segs_in: 20,
625 snd_mss: Some(1460),
626 rcv_mss: Some(1460),
627 last_data_sent: None,
628 };
629
630 let inspector = Inspector::new(Default::default());
631 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
632 info.record(&mut bindings_inspector);
633
634 let mut exec = fuchsia_async::TestExecutor::new();
635
636 assert_data_tree!(@executor exec, inspector, "root": {
637 "CongestionControlState": "Open",
638 "RtoMs": 200u64,
639 "RttMs": 50u64,
640 "RttVarMs": 25u64,
641 "SndSsthresh": 1000u64,
642 "SndCwnd": 2000u64,
643 "SndMss": 1460u64,
644 "RcvMss": 1460u64,
645 });
646 }
647}