1use core::time::Duration;
6
7use derivative::Derivative;
8use netstack3_base::Instant;
9
10use crate::internal::buffer::{ReceiveBuffer, SendBuffer};
11use crate::internal::congestion::LossRecoveryMode;
12use crate::internal::counters::TcpCountersWithSocketInner;
13use crate::internal::state::{
14 CloseWait, Closed, Closing, Established, FinWait1, FinWait2, LastAck, Listen, Recv, RecvParams,
15 Send, State, SynRcvd, SynSent, TimeWait,
16};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct TcpSocketInfo<I> {
21 pub state: netstack3_base::TcpSocketState,
23 pub ca_state: CongestionControlState,
25 pub rto: Option<Duration>,
27 pub rtt: Option<Duration>,
29 pub rtt_var: Option<Duration>,
31 pub snd_ssthresh: u32,
33 pub snd_cwnd: u32,
35 pub retransmits: u64,
37 pub last_ack_recv: Option<I>,
39 pub segs_out: u64,
41 pub segs_in: u64,
43 pub last_data_sent: Option<I>,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
49pub enum CongestionControlState {
50 #[default]
52 Open,
53 Disorder,
55 CongestionWindowReduced,
57 Recovery,
59 Loss,
61}
62
63#[derive(Debug, Clone, Derivative)]
66#[derivative(Default(bound = ""))]
67#[cfg_attr(test, derive(PartialEq, Eq))]
68pub(crate) struct SendInfo<I> {
69 rto: Option<Duration>,
70 rtt: Option<Duration>,
71 rtt_var: Option<Duration>,
72 snd_ssthresh: u32,
73 snd_cwnd: u32,
74 last_data_sent: Option<I>,
75 ca_state: CongestionControlState,
76}
77
78impl<I: Instant> SendInfo<I> {
79 pub(super) fn from_send<S: SendBuffer, const FIN: bool>(snd: &Send<I, S, FIN>) -> Self {
80 let cc = &snd.congestion_control;
81 let est = &snd.rtt_estimator;
82
83 Self {
84 snd_cwnd: cc.inspect_cwnd().cwnd(),
85 snd_ssthresh: cc.slow_start_threshold(),
86 ca_state: match cc.inspect_loss_recovery_mode() {
87 Some(LossRecoveryMode::FastRecovery) => CongestionControlState::Recovery,
88 Some(LossRecoveryMode::SackRecovery) => CongestionControlState::Recovery,
89 None => CongestionControlState::Open,
90 },
91 rtt: est.srtt(),
92 rtt_var: est.rtt_var(),
93 rto: Some(est.rto().into()),
94 last_data_sent: snd.last_data_sent,
95 }
96 }
97}
98
99#[derive(Debug, Clone, Derivative)]
102#[derivative(Default(bound = ""))]
103#[cfg_attr(test, derive(PartialEq, Eq))]
104struct RecvInfo<I> {
105 last_ack_recv: Option<I>,
106}
107
108impl<I: Instant> RecvInfo<I> {
109 fn from_recv<R: ReceiveBuffer>(rcv: &Recv<I, R>) -> Self {
110 Self { last_ack_recv: rcv.last_segment_at }
111 }
112
113 fn from_recv_params(rcv: &RecvParams<I>) -> Self {
114 let RecvParams { last_ack_recv, ack: _, wnd_scale: _, wnd: _, ts_opt: _ } = rcv;
115 Self { last_ack_recv: *last_ack_recv }
116 }
117}
118
119struct CounterParams {
122 retransmits: u64,
123 segs_out: u64,
124 segs_in: u64,
125}
126
127impl CounterParams {
128 fn from_counters(counters: &TcpCountersWithSocketInner) -> Self {
129 Self {
130 retransmits: counters.retransmits.get(),
131 segs_out: counters.segments_sent.get(),
132 segs_in: counters.received_segments_dispatched.get(),
133 }
134 }
135}
136
137impl<I: Instant> TcpSocketInfo<I> {
138 pub(crate) fn from_partial_state(
141 state: netstack3_base::TcpSocketState,
142 counters: &TcpCountersWithSocketInner,
143 ) -> Self {
144 let SendInfo { rto, rtt, rtt_var, snd_ssthresh, snd_cwnd, last_data_sent, ca_state } =
145 SendInfo::default();
146 let RecvInfo { last_ack_recv } = RecvInfo::default();
147 let CounterParams { retransmits, segs_out, segs_in } =
148 CounterParams::from_counters(counters);
149
150 Self {
151 state,
152 retransmits,
153 segs_out,
154 ca_state,
155 rto,
156 rtt,
157 rtt_var,
158 snd_ssthresh,
159 snd_cwnd,
160 last_ack_recv,
161 segs_in,
162 last_data_sent,
163 }
164 }
165
166 pub(crate) fn from_full_state<R, S, ActiveOpen>(
169 state: &State<I, R, S, ActiveOpen>,
170 counters: &TcpCountersWithSocketInner,
171 ) -> Self
172 where
173 R: ReceiveBuffer,
174 S: SendBuffer,
175 {
176 let (state, send_params, recv_params) = match state {
177 State::Closed(Closed { reason: _ }) => {
178 (netstack3_base::TcpSocketState::Close, SendInfo::default(), RecvInfo::default())
179 }
180 State::Listen(Listen {
181 iss: _,
182 timestamp_offset: _,
183 buffer_sizes: _,
184 device_mss: _,
185 default_mss: _,
186 user_timeout: _,
187 }) => {
188 (netstack3_base::TcpSocketState::Listen, SendInfo::default(), RecvInfo::default())
189 }
190 State::SynSent(SynSent {
191 iss: _,
192 timestamp: _,
193 retrans_timer: _,
194 active_open: _,
195 buffer_sizes: _,
196 device_mss: _,
197 default_mss: _,
198 rcv_wnd_scale: _,
199 ts_opt: _,
200 }) => {
201 (netstack3_base::TcpSocketState::SynSent, SendInfo::default(), RecvInfo::default())
202 }
203 State::SynRcvd(SynRcvd {
204 iss: _,
205 irs: _,
206 timestamp: _,
207 retrans_timer: _,
208 simultaneous_open: _,
209 buffer_sizes: _,
210 smss: _,
211 rcv_wnd_scale: _,
212 snd_wnd_scale: _,
213 sack_permitted: _,
214 rcv: _,
216 }) => {
217 (netstack3_base::TcpSocketState::SynRecv, SendInfo::default(), RecvInfo::default())
218 }
219 State::Established(Established { snd, rcv }) => (
220 netstack3_base::TcpSocketState::Established,
221 SendInfo::from_send(snd.get()),
222 RecvInfo::from_recv(rcv.get()),
223 ),
224 State::FinWait1(FinWait1 { snd, rcv }) => (
225 netstack3_base::TcpSocketState::FinWait1,
226 SendInfo::from_send(snd.get()),
227 RecvInfo::from_recv(rcv.get()),
228 ),
229 State::FinWait2(FinWait2 { last_seq: _, rcv, timeout_at: _, snd_info }) => (
230 netstack3_base::TcpSocketState::FinWait2,
231 snd_info.clone(),
232 RecvInfo::from_recv(rcv),
233 ),
234 State::CloseWait(CloseWait { snd, closed_rcv }) => (
235 netstack3_base::TcpSocketState::CloseWait,
236 SendInfo::from_send(snd.get()),
237 RecvInfo::from_recv_params(closed_rcv),
238 ),
239 State::Closing(Closing { snd, closed_rcv }) => (
240 netstack3_base::TcpSocketState::Closing,
241 SendInfo::from_send(snd),
242 RecvInfo::from_recv_params(closed_rcv),
243 ),
244 State::LastAck(LastAck { snd, closed_rcv }) => (
245 netstack3_base::TcpSocketState::LastAck,
246 SendInfo::from_send(snd),
247 RecvInfo::from_recv_params(closed_rcv),
248 ),
249 State::TimeWait(TimeWait { last_seq: _, expiry: _, closed_rcv, snd_info }) => (
250 netstack3_base::TcpSocketState::TimeWait,
251 snd_info.clone(),
252 RecvInfo::from_recv_params(closed_rcv),
253 ),
254 };
255
256 let SendInfo { snd_cwnd, snd_ssthresh, ca_state, rtt, rtt_var, rto, last_data_sent } =
257 send_params;
258 let RecvInfo { last_ack_recv } = recv_params;
259 let CounterParams { retransmits, segs_out, segs_in } =
260 CounterParams::from_counters(counters);
261
262 Self {
263 state,
264 ca_state,
265 rto,
266 rtt,
267 rtt_var,
268 snd_ssthresh,
269 snd_cwnd,
270 retransmits,
271 last_ack_recv,
272 segs_out,
273 segs_in,
274 last_data_sent,
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 use core::time::Duration;
284
285 use netstack3_base::testutil::FakeInstant;
286 use netstack3_base::{
287 EffectiveMss, Mss, MssSizeLimiters, SeqNum, TcpSocketState, Timestamp, WindowScale,
288 WindowSize,
289 };
290
291 use crate::internal::buffer::Assembler;
292 use crate::internal::congestion::CongestionControl;
293 use crate::internal::rtt::{Estimator, RttSampler};
294 use crate::internal::state::{Established, Listen, Recv, RecvBufferState, Send, State};
295 use crate::internal::timestamp::TimestampOptionState;
296 use crate::testutil::RingBuffer;
297
298 impl TcpCountersWithSocketInner {
299 fn new_for_test(retransmits: u64, segs_out: u64, segs_in: u64) -> Self {
300 let counters = Self::default();
301 counters.retransmits.add(retransmits);
302 counters.segments_sent.add(segs_out);
303 counters.received_segments_dispatched.add(segs_in);
304 counters
305 }
306 }
307
308 #[test]
309 fn test_from_partial_state() {
310 let state = TcpSocketState::Listen;
311 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
312
313 let info = TcpSocketInfo::<FakeInstant>::from_partial_state(state, &counters);
314
315 assert_eq!(
316 info,
317 TcpSocketInfo {
318 state: TcpSocketState::Listen,
319 retransmits: 5,
320 segs_out: 10,
321 segs_in: 20,
322
323 ca_state: CongestionControlState::Open,
325 rto: None,
326 rtt: None,
327 rtt_var: None,
328 snd_ssthresh: 0,
329 snd_cwnd: 0,
330 last_ack_recv: None,
331 last_data_sent: None,
332 }
333 );
334 }
335
336 #[test]
337 fn test_from_full_state_listen() {
338 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Listen(Listen {
339 iss: SeqNum::new(100),
340 timestamp_offset: Timestamp::new(0),
341 buffer_sizes: Default::default(),
342 device_mss: Mss::new(1460).unwrap(),
343 default_mss: Mss::new(536).unwrap(),
344 user_timeout: None,
345 });
346
347 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
348
349 let info = TcpSocketInfo::from_full_state(&state, &counters);
350
351 assert_eq!(
352 info,
353 TcpSocketInfo {
354 state: TcpSocketState::Listen,
355 retransmits: 5,
356 segs_out: 10,
357 segs_in: 20,
358
359 ca_state: CongestionControlState::Open,
361 rto: None,
362 rtt: None,
363 rtt_var: None,
364 snd_ssthresh: 0,
365 snd_cwnd: 0,
366 last_ack_recv: None,
367 last_data_sent: None,
368 }
369 );
370 }
371
372 #[test]
373 fn test_from_full_state_established() {
374 let now = FakeInstant::from(Duration::from_secs(10));
375 let mss = Mss::new(1460).unwrap();
376 let effective_mss =
377 EffectiveMss::from_mss(mss, MssSizeLimiters { timestamp_enabled: false });
378
379 let mut rtt_estimator = Estimator::default();
380 let rtt = Duration::from_millis(50);
381 rtt_estimator.sample(rtt);
382
383 let mut congestion_control = CongestionControl::cubic_with_mss(effective_mss);
384 congestion_control.inflate_cwnd(u32::from(mss));
385
386 let send = Send {
387 nxt: SeqNum::new(200),
388 max: SeqNum::new(200),
389 una: SeqNum::new(100),
390 wnd: WindowSize::new(1000).unwrap(),
391 wnd_scale: WindowScale::default(),
392 wnd_max: WindowSize::new(1000).unwrap(),
393 wl1: SeqNum::new(100),
394 wl2: SeqNum::new(100),
395 last_push: SeqNum::new(200),
396 rtt_sampler: RttSampler::default(),
397 rtt_estimator,
398 timer: None,
399 congestion_control,
400 last_data_sent: Some(now - Duration::from_secs(1)),
401 buffer: RingBuffer::new(1000),
402 };
403
404 let recv = Recv {
405 buffer: RecvBufferState::Open {
406 buffer: RingBuffer::new(1000),
407 assembler: Assembler::new(SeqNum::new(50)),
408 },
409 remaining_quickacks: Default::default(),
410 last_segment_at: Some(now - Duration::from_secs(2)),
411 timer: None,
412 mss: effective_mss,
413 wnd_scale: WindowScale::default(),
414 last_window_update: (SeqNum::new(50), WindowSize::new(1000).unwrap()),
415 sack_permitted: false,
416 ts_opt: TimestampOptionState::Disabled,
417 };
418
419 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Established(Established {
420 snd: send.into(),
421 rcv: recv.into(),
422 });
423
424 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
425 let info = TcpSocketInfo::from_full_state(&state, &counters);
426
427 assert_eq!(
428 info,
429 TcpSocketInfo {
430 state: TcpSocketState::Established,
431 ca_state: CongestionControlState::Open,
432 rto: Some(Duration::from_millis(200)),
433 rtt: Some(rtt),
434 rtt_var: Some(rtt / 2),
435 snd_ssthresh: u32::MAX,
436 snd_cwnd: 4380 + 1460,
437 retransmits: 5,
438 last_ack_recv: Some(now - Duration::from_secs(2)),
439 segs_out: 10,
440 segs_in: 20,
441 last_data_sent: Some(now - Duration::from_secs(1))
442 }
443 );
444 }
445
446 #[test]
447 fn test_from_full_state_recovery() {
448 let mss = Mss::new(1460).unwrap();
449 let effective_mss =
450 EffectiveMss::from_mss(mss, MssSizeLimiters { timestamp_enabled: false });
451
452 let mut congestion_control = CongestionControl::cubic_with_mss(effective_mss);
453 let ack = SeqNum::new(100);
455 let nxt = SeqNum::new(200);
456 let _ = congestion_control.on_dup_ack(ack, nxt);
457 let _ = congestion_control.on_dup_ack(ack, nxt);
458 let _ = congestion_control.on_dup_ack(ack, nxt);
459
460 let send = Send {
461 nxt,
462 max: nxt,
463 una: ack,
464 wnd: WindowSize::new(1000).unwrap(),
465 wnd_scale: WindowScale::default(),
466 wnd_max: WindowSize::new(1000).unwrap(),
467 wl1: ack,
468 wl2: ack,
469 last_push: nxt,
470 rtt_sampler: RttSampler::default(),
471 rtt_estimator: Estimator::default(),
472 timer: None,
473 congestion_control,
474 last_data_sent: None,
475 buffer: RingBuffer::new(1000),
476 };
477
478 let recv = Recv {
479 buffer: RecvBufferState::Open {
480 buffer: RingBuffer::new(1000),
481 assembler: Assembler::new(SeqNum::new(50)),
482 },
483 remaining_quickacks: Default::default(),
484 last_segment_at: None,
485 timer: None,
486 mss: effective_mss,
487 wnd_scale: WindowScale::default(),
488 last_window_update: (SeqNum::new(50), WindowSize::new(1000).unwrap()),
489 sack_permitted: false,
490 ts_opt: TimestampOptionState::Disabled,
491 };
492
493 let state = State::<FakeInstant, RingBuffer, RingBuffer, ()>::Established(Established {
494 snd: send.into(),
495 rcv: recv.into(),
496 });
497
498 let counters = TcpCountersWithSocketInner::new_for_test(5, 10, 20);
499 let info = TcpSocketInfo::from_full_state(&state, &counters);
500
501 assert_eq!(
502 info,
503 TcpSocketInfo {
504 state: TcpSocketState::Established,
505 retransmits: 5,
506 segs_out: 10,
507 segs_in: 20,
508 ca_state: CongestionControlState::Recovery,
509 rto: Some(Duration::from_secs(1)),
510 rtt: None,
511 rtt_var: None,
512 snd_ssthresh: 3066,
513 snd_cwnd: 7300,
514 last_ack_recv: None,
515 last_data_sent: None,
516 }
517 );
518 }
519}