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)]
78#[cfg_attr(test, derive(PartialEq))]
79pub struct TcpCountersWithSocketInner<C = Counter> {
80 pub received_segments_dispatched: C,
83 pub listener_queue_overflow: C,
86 pub segment_send_errors: C,
88 pub segments_sent: C,
90 pub passive_open_no_route_errors: C,
93 pub passive_connection_openings: C,
95 pub active_open_no_route_errors: C,
98 pub active_connection_openings: C,
100 pub failed_connection_attempts: C,
103 pub failed_port_reservations: C,
105 pub resets_received: C,
107 pub resets_sent: C,
109 pub syns_received: C,
111 pub syns_sent: C,
113 pub fins_received: C,
115 pub fins_sent: C,
117 pub timeouts: C,
119 pub retransmits: C,
121 pub slow_start_retransmits: C,
123 pub fast_retransmits: C,
125 pub fast_recovery: C,
127 pub established_closed: C,
129 pub established_resets: C,
132 pub established_timedout: C,
135 pub loss_recovered: C,
137}
138
139pub struct CombinedTcpCounters<'a, I: Ip> {
141 pub with_socket: &'a TcpCountersWithSocket<I>,
143 pub without_socket: Option<&'a TcpCountersWithoutSocket<I>>,
149}
150
151impl<I: Ip> Inspectable for CombinedTcpCounters<'_, I> {
152 fn record<II: Inspector>(&self, inspector: &mut II) {
153 let CombinedTcpCounters { with_socket, without_socket } = self;
154 let TcpCountersWithSocketInner {
155 received_segments_dispatched,
156 listener_queue_overflow,
157 segment_send_errors,
158 segments_sent,
159 passive_open_no_route_errors,
160 passive_connection_openings,
161 active_open_no_route_errors,
162 active_connection_openings,
163 failed_connection_attempts,
164 failed_port_reservations,
165 resets_received,
166 resets_sent,
167 syns_received,
168 syns_sent,
169 fins_received,
170 fins_sent,
171 timeouts,
172 retransmits,
173 slow_start_retransmits,
174 fast_retransmits,
175 fast_recovery,
176 established_closed,
177 established_resets,
178 established_timedout,
179 loss_recovered,
180 } = with_socket.as_ref();
181
182 struct WithoutSocketRx<'a> {
185 valid_segments_received: &'a Counter,
186 }
187 struct WithoutSocketRxError<'a> {
188 invalid_ip_addrs_received: &'a Counter,
189 invalid_segments_received: &'a Counter,
190 received_segments_no_dispatch: &'a Counter,
191 checksum_errors: &'a Counter,
192 }
193 let (without_socket_rx, without_socket_rx_error) = match without_socket.map(AsRef::as_ref) {
194 None => (None, None),
195 Some(TcpCountersWithoutSocketInner {
196 invalid_ip_addrs_received,
197 invalid_segments_received,
198 valid_segments_received,
199 received_segments_no_dispatch,
200 checksum_errors,
201 }) => (
202 Some(WithoutSocketRx { valid_segments_received }),
203 Some(WithoutSocketRxError {
204 invalid_ip_addrs_received,
205 invalid_segments_received,
206 received_segments_no_dispatch,
207 checksum_errors,
208 }),
209 ),
210 };
211
212 inspector.record_child("Rx", |inspector| {
213 if let Some(WithoutSocketRx { valid_segments_received }) = without_socket_rx {
214 inspector.record_counter("ValidSegmentsReceived", valid_segments_received);
215 }
216 inspector.record_counter("ReceivedSegmentsDispatched", received_segments_dispatched);
217 inspector.record_counter("ResetsReceived", resets_received);
218 inspector.record_counter("SynsReceived", syns_received);
219 inspector.record_counter("FinsReceived", fins_received);
220 inspector.record_child("Errors", |inspector| {
221 inspector.record_counter("ListenerQueueOverflow", listener_queue_overflow);
222 inspector.record_counter("PassiveOpenNoRouteErrors", passive_open_no_route_errors);
223 if let Some(WithoutSocketRxError {
224 invalid_ip_addrs_received,
225 invalid_segments_received,
226 received_segments_no_dispatch,
227 checksum_errors,
228 }) = without_socket_rx_error
229 {
230 inspector.record_counter("InvalidIpAddrsReceived", invalid_ip_addrs_received);
231 inspector.record_counter("InvalidSegmentsReceived", invalid_segments_received);
232 inspector.record_counter(
233 "ReceivedSegmentsNoDispatch",
234 received_segments_no_dispatch,
235 );
236 inspector.record_counter("ChecksumErrors", checksum_errors);
237 }
238 })
239 });
240 inspector.record_child("Tx", |inspector| {
241 inspector.record_counter("SegmentsSent", segments_sent);
242 inspector.record_counter("ResetsSent", resets_sent);
243 inspector.record_counter("SynsSent", syns_sent);
244 inspector.record_counter("FinsSent", fins_sent);
245 inspector.record_counter("Timeouts", timeouts);
246 inspector.record_counter("Retransmits", retransmits);
247 inspector.record_counter("SlowStartRetransmits", slow_start_retransmits);
248 inspector.record_counter("FastRetransmits", fast_retransmits);
249 inspector.record_child("Errors", |inspector| {
250 inspector.record_counter("SegmentSendErrors", segment_send_errors);
251 inspector.record_counter("ActiveOpenNoRouteErrors", active_open_no_route_errors);
252 });
253 });
254 inspector.record_counter("PassiveConnectionOpenings", passive_connection_openings);
255 inspector.record_counter("ActiveConnectionOpenings", active_connection_openings);
256 inspector.record_counter("FastRecovery", fast_recovery);
257 inspector.record_counter("LossRecovered", loss_recovered);
258 inspector.record_counter("EstablishedClosed", established_closed);
259 inspector.record_counter("EstablishedResets", established_resets);
260 inspector.record_counter("EstablishedTimedout", established_timedout);
261 inspector.record_child("Errors", |inspector| {
262 inspector.record_counter("FailedConnectionOpenings", failed_connection_attempts);
263 inspector.record_counter("FailedPortReservations", failed_port_reservations);
264 })
265 }
266}
267
268pub(crate) struct TcpCountersRefs<'a> {
275 pub(crate) stack_wide: &'a TcpCountersWithSocketInner,
276 pub(crate) per_socket: &'a TcpCountersWithSocketInner,
277}
278
279impl<'a> TcpCountersRefs<'a> {
280 pub(crate) fn from_ctx<I: Ip, R, CC: ResourceCounterContext<R, TcpCountersWithSocket<I>>>(
281 ctx: &'a CC,
282 resource: &'a R,
283 ) -> Self {
284 TcpCountersRefs {
285 stack_wide: ctx.counters(),
286 per_socket: ctx.per_resource_counters(resource),
287 }
288 }
289
290 pub(crate) fn increment<F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter>(&self, cb: F) {
291 let Self { stack_wide, per_socket } = self;
292 cb(stack_wide).increment();
293 cb(per_socket).increment();
294 }
295}
296
297pub(crate) fn increment_counter_with_optional_socket_id<I, CC, BT, D, F>(
302 core_ctx: &CC,
303 socket_id: Option<&TcpSocketId<I, D, BT>>,
304 cb: F,
305) where
306 I: DualStackIpExt,
307 CC: TcpCounterContext<I, D, BT>,
308 D: WeakDeviceIdentifier,
309 BT: TcpBindingsTypes,
310 F: Fn(&TcpCountersWithSocket<I>) -> &Counter,
311{
312 match socket_id {
313 Some(id) => core_ctx.increment_both(id, cb),
314 None => cb(core_ctx.counters()).increment(),
315 }
316}
317
318pub(crate) fn increment_counter_with_optional_demux_id<I, CC, BT, D, F>(
323 core_ctx: &CC,
324 demux_id: Option<&I::DemuxSocketId<D, BT>>,
325 cb: F,
326) where
327 I: DualStackIpExt,
328 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
329 D: WeakDeviceIdentifier,
330 BT: TcpBindingsTypes,
331 F: Fn(&TcpCountersWithSocketInner) -> &Counter,
332{
333 match demux_id {
334 Some(id) => increment_counter_for_demux_id::<I, _, _, _, _>(core_ctx, &id, cb),
335 None => {
336 cb(CounterContext::<TcpCountersWithSocket<I>>::counters(core_ctx).as_ref()).increment()
337 }
338 }
339}
340
341pub(crate) fn increment_counter_for_demux_id<I, D, BT, CC, F>(
343 core_ctx: &CC,
344 demux_id: &I::DemuxSocketId<D, BT>,
345 cb: F,
346) where
347 I: DualStackIpExt,
348 D: WeakDeviceIdentifier,
349 BT: TcpBindingsTypes,
350 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
351 F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter,
352{
353 match I::as_dual_stack_ip_socket(demux_id) {
354 EitherStack::ThisStack(socket_id) => core_ctx
355 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I>| cb(counters.as_ref())),
356 EitherStack::OtherStack(socket_id) => core_ctx
357 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I::OtherVersion>| {
358 cb(counters.as_ref())
359 }),
360 }
361}
362
363#[cfg(test)]
364pub(crate) mod testutil {
365 use super::*;
366
367 pub(crate) type CounterExpectations = TcpCountersWithSocketInner<u64>;
368
369 impl From<&TcpCountersWithSocketInner> for CounterExpectations {
370 fn from(counters: &TcpCountersWithSocketInner) -> CounterExpectations {
371 let TcpCountersWithSocketInner {
372 received_segments_dispatched,
373 listener_queue_overflow,
374 segment_send_errors,
375 segments_sent,
376 passive_open_no_route_errors,
377 passive_connection_openings,
378 active_open_no_route_errors,
379 active_connection_openings,
380 failed_connection_attempts,
381 failed_port_reservations,
382 resets_received,
383 resets_sent,
384 syns_received,
385 syns_sent,
386 fins_received,
387 fins_sent,
388 timeouts,
389 retransmits,
390 slow_start_retransmits,
391 fast_retransmits,
392 fast_recovery,
393 established_closed,
394 established_resets,
395 established_timedout,
396 loss_recovered,
397 } = counters;
398 TcpCountersWithSocketInner {
399 received_segments_dispatched: received_segments_dispatched.get(),
400 listener_queue_overflow: listener_queue_overflow.get(),
401 segment_send_errors: segment_send_errors.get(),
402 segments_sent: segments_sent.get(),
403 passive_open_no_route_errors: passive_open_no_route_errors.get(),
404 passive_connection_openings: passive_connection_openings.get(),
405 active_open_no_route_errors: active_open_no_route_errors.get(),
406 active_connection_openings: active_connection_openings.get(),
407 failed_connection_attempts: failed_connection_attempts.get(),
408 failed_port_reservations: failed_port_reservations.get(),
409 resets_received: resets_received.get(),
410 resets_sent: resets_sent.get(),
411 syns_received: syns_received.get(),
412 syns_sent: syns_sent.get(),
413 fins_received: fins_received.get(),
414 fins_sent: fins_sent.get(),
415 timeouts: timeouts.get(),
416 retransmits: retransmits.get(),
417 slow_start_retransmits: slow_start_retransmits.get(),
418 fast_retransmits: fast_retransmits.get(),
419 fast_recovery: fast_recovery.get(),
420 established_closed: established_closed.get(),
421 established_resets: established_resets.get(),
422 established_timedout: established_timedout.get(),
423 loss_recovered: loss_recovered.get(),
424 }
425 }
426 }
427
428 pub(crate) type CounterExpectationsWithoutSocket = TcpCountersWithoutSocketInner<u64>;
429
430 impl From<&TcpCountersWithoutSocketInner> for CounterExpectationsWithoutSocket {
431 fn from(counters: &TcpCountersWithoutSocketInner) -> CounterExpectationsWithoutSocket {
432 let TcpCountersWithoutSocketInner {
433 invalid_ip_addrs_received,
434 invalid_segments_received,
435 valid_segments_received,
436 received_segments_no_dispatch,
437 checksum_errors,
438 } = counters;
439 TcpCountersWithoutSocketInner {
440 invalid_ip_addrs_received: invalid_ip_addrs_received.get(),
441 invalid_segments_received: invalid_segments_received.get(),
442 valid_segments_received: valid_segments_received.get(),
443 received_segments_no_dispatch: received_segments_no_dispatch.get(),
444 checksum_errors: checksum_errors.get(),
445 }
446 }
447 }
448}