1use net_types::ip::{Ip, IpMarked};
8use netstack3_base::socket::EitherStack;
9use netstack3_base::{
10 Counter, CounterContext, Inspectable, Inspector, InspectorExt as _, ResourceCounterContext,
11 WeakDeviceIdentifier,
12};
13
14use crate::internal::socket::{DualStackIpExt, TcpBindingsTypes, TcpSocketId};
15
16pub trait TcpCounterContext<I: DualStackIpExt, D: WeakDeviceIdentifier, BT: TcpBindingsTypes>:
18 ResourceCounterContext<TcpSocketId<I, D, BT>, TcpCountersWithSocket<I>>
19 + CounterContext<TcpCountersWithoutSocket<I>>
20{
21}
22
23impl<I, D, BT, CC> TcpCounterContext<I, D, BT> for CC
24where
25 I: DualStackIpExt,
26 D: WeakDeviceIdentifier,
27 BT: TcpBindingsTypes,
28 CC: ResourceCounterContext<TcpSocketId<I, D, BT>, TcpCountersWithSocket<I>>
29 + CounterContext<TcpCountersWithoutSocket<I>>,
30{
31}
32
33pub type TcpCountersWithoutSocket<I> = IpMarked<I, TcpCountersWithoutSocketInner>;
39
40#[derive(Default, Debug)]
44#[cfg_attr(test, derive(PartialEq))]
45pub struct TcpCountersWithoutSocketInner<C = Counter> {
46 pub invalid_ip_addrs_received: C,
49 pub invalid_segments_received: C,
52 pub valid_segments_received: C,
54 pub received_segments_no_dispatch: C,
57 pub checksum_errors: C,
59}
60
61pub type TcpCountersWithSocket<I> = IpMarked<I, TcpCountersWithSocketInner<Counter>>;
71
72#[derive(Default, Debug)]
77#[cfg_attr(test, derive(PartialEq))]
78pub struct TcpCountersWithSocketInner<C = Counter> {
79 pub received_segments_dispatched: C,
82 pub listener_queue_overflow: C,
85 pub segment_send_errors: C,
87 pub segments_sent: C,
89 pub passive_open_no_route_errors: C,
92 pub passive_connection_openings: C,
94 pub active_open_no_route_errors: C,
97 pub active_connection_openings: C,
99 pub failed_connection_attempts: C,
102 pub failed_port_reservations: C,
104 pub resets_received: C,
106 pub resets_sent: C,
108 pub syns_received: C,
110 pub syns_sent: C,
112 pub fins_received: C,
114 pub fins_sent: C,
116 pub timeouts: C,
118 pub retransmits: C,
120 pub slow_start_retransmits: C,
122 pub fast_retransmits: C,
124 pub sack_retransmits: C,
126 pub fast_recovery: C,
128 pub sack_recovery: C,
130 pub established_closed: C,
132 pub established_resets: C,
135 pub established_timedout: C,
138 pub loss_recovered: C,
140 pub dup_acks: C,
142}
143
144pub struct CombinedTcpCounters<'a, I: Ip> {
146 pub with_socket: &'a TcpCountersWithSocket<I>,
148 pub without_socket: Option<&'a TcpCountersWithoutSocket<I>>,
154}
155
156impl<I: Ip> Inspectable for CombinedTcpCounters<'_, I> {
157 fn record<II: Inspector>(&self, inspector: &mut II) {
158 let CombinedTcpCounters { with_socket, without_socket } = self;
159 let TcpCountersWithSocketInner {
160 received_segments_dispatched,
161 listener_queue_overflow,
162 segment_send_errors,
163 segments_sent,
164 passive_open_no_route_errors,
165 passive_connection_openings,
166 active_open_no_route_errors,
167 active_connection_openings,
168 failed_connection_attempts,
169 failed_port_reservations,
170 resets_received,
171 resets_sent,
172 syns_received,
173 syns_sent,
174 fins_received,
175 fins_sent,
176 timeouts,
177 retransmits,
178 slow_start_retransmits,
179 fast_retransmits,
180 sack_retransmits,
181 fast_recovery,
182 sack_recovery,
183 established_closed,
184 established_resets,
185 established_timedout,
186 loss_recovered,
187 dup_acks,
188 } = with_socket.as_ref();
189
190 struct WithoutSocketRx<'a> {
193 valid_segments_received: &'a Counter,
194 }
195 struct WithoutSocketRxError<'a> {
196 invalid_ip_addrs_received: &'a Counter,
197 invalid_segments_received: &'a Counter,
198 received_segments_no_dispatch: &'a Counter,
199 checksum_errors: &'a Counter,
200 }
201 let (without_socket_rx, without_socket_rx_error) = match without_socket.map(AsRef::as_ref) {
202 None => (None, None),
203 Some(TcpCountersWithoutSocketInner {
204 invalid_ip_addrs_received,
205 invalid_segments_received,
206 valid_segments_received,
207 received_segments_no_dispatch,
208 checksum_errors,
209 }) => (
210 Some(WithoutSocketRx { valid_segments_received }),
211 Some(WithoutSocketRxError {
212 invalid_ip_addrs_received,
213 invalid_segments_received,
214 received_segments_no_dispatch,
215 checksum_errors,
216 }),
217 ),
218 };
219
220 inspector.record_child("Rx", |inspector| {
221 if let Some(WithoutSocketRx { valid_segments_received }) = without_socket_rx {
222 inspector.record_counter("ValidSegmentsReceived", valid_segments_received);
223 }
224 inspector.record_counter("ReceivedSegmentsDispatched", received_segments_dispatched);
225 inspector.record_counter("ResetsReceived", resets_received);
226 inspector.record_counter("SynsReceived", syns_received);
227 inspector.record_counter("FinsReceived", fins_received);
228 inspector.record_counter("DupAcks", dup_acks);
229 inspector.record_child("Errors", |inspector| {
230 inspector.record_counter("ListenerQueueOverflow", listener_queue_overflow);
231 inspector.record_counter("PassiveOpenNoRouteErrors", passive_open_no_route_errors);
232 if let Some(WithoutSocketRxError {
233 invalid_ip_addrs_received,
234 invalid_segments_received,
235 received_segments_no_dispatch,
236 checksum_errors,
237 }) = without_socket_rx_error
238 {
239 inspector.record_counter("InvalidIpAddrsReceived", invalid_ip_addrs_received);
240 inspector.record_counter("InvalidSegmentsReceived", invalid_segments_received);
241 inspector.record_counter(
242 "ReceivedSegmentsNoDispatch",
243 received_segments_no_dispatch,
244 );
245 inspector.record_counter("ChecksumErrors", checksum_errors);
246 }
247 })
248 });
249 inspector.record_child("Tx", |inspector| {
250 inspector.record_counter("SegmentsSent", segments_sent);
251 inspector.record_counter("ResetsSent", resets_sent);
252 inspector.record_counter("SynsSent", syns_sent);
253 inspector.record_counter("FinsSent", fins_sent);
254 inspector.record_counter("Timeouts", timeouts);
255 inspector.record_counter("Retransmits", retransmits);
256 inspector.record_counter("SlowStartRetransmits", slow_start_retransmits);
257 inspector.record_counter("FastRetransmits", fast_retransmits);
258 inspector.record_counter("SackRetransmits", sack_retransmits);
259 inspector.record_child("Errors", |inspector| {
260 inspector.record_counter("SegmentSendErrors", segment_send_errors);
261 inspector.record_counter("ActiveOpenNoRouteErrors", active_open_no_route_errors);
262 });
263 });
264 inspector.record_counter("PassiveConnectionOpenings", passive_connection_openings);
265 inspector.record_counter("ActiveConnectionOpenings", active_connection_openings);
266 inspector.record_counter("FastRecovery", fast_recovery);
267 inspector.record_counter("SackRecovery", sack_recovery);
268 inspector.record_counter("LossRecovered", loss_recovered);
269 inspector.record_counter("EstablishedClosed", established_closed);
270 inspector.record_counter("EstablishedResets", established_resets);
271 inspector.record_counter("EstablishedTimedout", established_timedout);
272 inspector.record_child("Errors", |inspector| {
273 inspector.record_counter("FailedConnectionOpenings", failed_connection_attempts);
274 inspector.record_counter("FailedPortReservations", failed_port_reservations);
275 })
276 }
277}
278
279pub(crate) struct TcpCountersRefs<'a> {
286 pub(crate) stack_wide: &'a TcpCountersWithSocketInner,
287 pub(crate) per_socket: &'a TcpCountersWithSocketInner,
288}
289
290impl<'a> TcpCountersRefs<'a> {
291 pub(crate) fn from_ctx<I: Ip, R, CC: ResourceCounterContext<R, TcpCountersWithSocket<I>>>(
292 ctx: &'a CC,
293 resource: &'a R,
294 ) -> Self {
295 TcpCountersRefs {
296 stack_wide: ctx.counters(),
297 per_socket: ctx.per_resource_counters(resource),
298 }
299 }
300
301 pub(crate) fn increment<F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter>(&self, cb: F) {
302 let Self { stack_wide, per_socket } = self;
303 cb(stack_wide).increment();
304 cb(per_socket).increment();
305 }
306}
307
308pub(crate) fn increment_counter_with_optional_socket_id<I, CC, BT, D, F>(
313 core_ctx: &CC,
314 socket_id: Option<&TcpSocketId<I, D, BT>>,
315 cb: F,
316) where
317 I: DualStackIpExt,
318 CC: TcpCounterContext<I, D, BT>,
319 D: WeakDeviceIdentifier,
320 BT: TcpBindingsTypes,
321 F: Fn(&TcpCountersWithSocket<I>) -> &Counter,
322{
323 match socket_id {
324 Some(id) => core_ctx.increment_both(id, cb),
325 None => cb(core_ctx.counters()).increment(),
326 }
327}
328
329pub(crate) fn increment_counter_with_optional_demux_id<I, CC, BT, D, F>(
334 core_ctx: &CC,
335 demux_id: Option<&I::DemuxSocketId<D, BT>>,
336 cb: F,
337) where
338 I: DualStackIpExt,
339 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
340 D: WeakDeviceIdentifier,
341 BT: TcpBindingsTypes,
342 F: Fn(&TcpCountersWithSocketInner) -> &Counter,
343{
344 match demux_id {
345 Some(id) => increment_counter_for_demux_id::<I, _, _, _, _>(core_ctx, &id, cb),
346 None => {
347 cb(CounterContext::<TcpCountersWithSocket<I>>::counters(core_ctx).as_ref()).increment()
348 }
349 }
350}
351
352pub(crate) fn increment_counter_for_demux_id<I, D, BT, CC, F>(
354 core_ctx: &CC,
355 demux_id: &I::DemuxSocketId<D, BT>,
356 cb: F,
357) where
358 I: DualStackIpExt,
359 D: WeakDeviceIdentifier,
360 BT: TcpBindingsTypes,
361 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
362 F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter,
363{
364 match I::as_dual_stack_ip_socket(demux_id) {
365 EitherStack::ThisStack(socket_id) => core_ctx
366 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I>| cb(counters.as_ref())),
367 EitherStack::OtherStack(socket_id) => core_ctx
368 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I::OtherVersion>| {
369 cb(counters.as_ref())
370 }),
371 }
372}
373
374#[cfg(test)]
375pub(crate) mod testutil {
376 use super::*;
377
378 pub(crate) type CounterExpectations = TcpCountersWithSocketInner<u64>;
379
380 impl From<&TcpCountersWithSocketInner> for CounterExpectations {
381 fn from(counters: &TcpCountersWithSocketInner) -> CounterExpectations {
382 let TcpCountersWithSocketInner {
383 received_segments_dispatched,
384 listener_queue_overflow,
385 segment_send_errors,
386 segments_sent,
387 passive_open_no_route_errors,
388 passive_connection_openings,
389 active_open_no_route_errors,
390 active_connection_openings,
391 failed_connection_attempts,
392 failed_port_reservations,
393 resets_received,
394 resets_sent,
395 syns_received,
396 syns_sent,
397 fins_received,
398 fins_sent,
399 timeouts,
400 retransmits,
401 slow_start_retransmits,
402 fast_retransmits,
403 sack_retransmits,
404 fast_recovery,
405 sack_recovery,
406 established_closed,
407 established_resets,
408 established_timedout,
409 loss_recovered,
410 dup_acks,
411 } = counters;
412 TcpCountersWithSocketInner {
413 received_segments_dispatched: received_segments_dispatched.get(),
414 listener_queue_overflow: listener_queue_overflow.get(),
415 segment_send_errors: segment_send_errors.get(),
416 segments_sent: segments_sent.get(),
417 passive_open_no_route_errors: passive_open_no_route_errors.get(),
418 passive_connection_openings: passive_connection_openings.get(),
419 active_open_no_route_errors: active_open_no_route_errors.get(),
420 active_connection_openings: active_connection_openings.get(),
421 failed_connection_attempts: failed_connection_attempts.get(),
422 failed_port_reservations: failed_port_reservations.get(),
423 resets_received: resets_received.get(),
424 resets_sent: resets_sent.get(),
425 syns_received: syns_received.get(),
426 syns_sent: syns_sent.get(),
427 fins_received: fins_received.get(),
428 fins_sent: fins_sent.get(),
429 timeouts: timeouts.get(),
430 retransmits: retransmits.get(),
431 slow_start_retransmits: slow_start_retransmits.get(),
432 fast_retransmits: fast_retransmits.get(),
433 sack_retransmits: sack_retransmits.get(),
434 fast_recovery: fast_recovery.get(),
435 sack_recovery: sack_recovery.get(),
436 established_closed: established_closed.get(),
437 established_resets: established_resets.get(),
438 established_timedout: established_timedout.get(),
439 loss_recovered: loss_recovered.get(),
440 dup_acks: dup_acks.get(),
441 }
442 }
443 }
444
445 pub(crate) type CounterExpectationsWithoutSocket = TcpCountersWithoutSocketInner<u64>;
446
447 impl From<&TcpCountersWithoutSocketInner> for CounterExpectationsWithoutSocket {
448 fn from(counters: &TcpCountersWithoutSocketInner) -> CounterExpectationsWithoutSocket {
449 let TcpCountersWithoutSocketInner {
450 invalid_ip_addrs_received,
451 invalid_segments_received,
452 valid_segments_received,
453 received_segments_no_dispatch,
454 checksum_errors,
455 } = counters;
456 TcpCountersWithoutSocketInner {
457 invalid_ip_addrs_received: invalid_ip_addrs_received.get(),
458 invalid_segments_received: invalid_segments_received.get(),
459 valid_segments_received: valid_segments_received.get(),
460 received_segments_no_dispatch: received_segments_no_dispatch.get(),
461 checksum_errors: checksum_errors.get(),
462 }
463 }
464 }
465}