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(
45 any(test, feature = "testutils"),
46 derive(PartialEq, netstack3_macros::CounterCollection)
47)]
48pub struct TcpCountersWithoutSocketInner<C = Counter> {
49 pub invalid_ip_addrs_received: C,
52 pub invalid_segments_received: C,
55 pub valid_segments_received: C,
57 pub received_segments_no_dispatch: C,
60 pub checksum_errors: C,
62}
63
64pub type TcpCountersWithSocket<I> = IpMarked<I, TcpCountersWithSocketInner<Counter>>;
74
75#[derive(Default, Debug)]
80#[cfg_attr(
81 any(test, feature = "testutils"),
82 derive(PartialEq, netstack3_macros::CounterCollection)
83)]
84pub struct TcpCountersWithSocketInner<C = Counter> {
85 pub received_segments_dispatched: C,
88 pub listener_queue_overflow: C,
91 pub segment_send_errors: C,
93 pub segments_sent: C,
95 pub passive_open_no_route_errors: C,
98 pub passive_connection_openings: C,
100 pub active_open_no_route_errors: C,
103 pub active_connection_openings: C,
105 pub failed_connection_attempts: C,
108 pub failed_port_reservations: C,
110 pub resets_received: C,
112 pub resets_sent: C,
114 pub syns_received: C,
116 pub syns_sent: C,
118 pub fins_received: C,
120 pub fins_sent: C,
122 pub timeouts: C,
124 pub retransmits: C,
126 pub slow_start_retransmits: C,
128 pub fast_retransmits: C,
130 pub sack_retransmits: C,
132 pub fast_recovery: C,
134 pub sack_recovery: C,
136 pub established_closed: C,
138 pub established_resets: C,
141 pub established_timedout: C,
144 pub loss_recovered: C,
146 pub dup_acks: C,
148}
149
150pub struct CombinedTcpCounters<'a, I: Ip> {
152 pub with_socket: &'a TcpCountersWithSocket<I>,
154 pub without_socket: Option<&'a TcpCountersWithoutSocket<I>>,
160}
161
162impl<I: Ip> Inspectable for CombinedTcpCounters<'_, I> {
163 fn record<II: Inspector>(&self, inspector: &mut II) {
164 let CombinedTcpCounters { with_socket, without_socket } = self;
165 let TcpCountersWithSocketInner {
166 received_segments_dispatched,
167 listener_queue_overflow,
168 segment_send_errors,
169 segments_sent,
170 passive_open_no_route_errors,
171 passive_connection_openings,
172 active_open_no_route_errors,
173 active_connection_openings,
174 failed_connection_attempts,
175 failed_port_reservations,
176 resets_received,
177 resets_sent,
178 syns_received,
179 syns_sent,
180 fins_received,
181 fins_sent,
182 timeouts,
183 retransmits,
184 slow_start_retransmits,
185 fast_retransmits,
186 sack_retransmits,
187 fast_recovery,
188 sack_recovery,
189 established_closed,
190 established_resets,
191 established_timedout,
192 loss_recovered,
193 dup_acks,
194 } = with_socket.as_ref();
195
196 struct WithoutSocketRx<'a> {
199 valid_segments_received: &'a Counter,
200 }
201 struct WithoutSocketRxError<'a> {
202 invalid_ip_addrs_received: &'a Counter,
203 invalid_segments_received: &'a Counter,
204 received_segments_no_dispatch: &'a Counter,
205 checksum_errors: &'a Counter,
206 }
207 let (without_socket_rx, without_socket_rx_error) = match without_socket.map(AsRef::as_ref) {
208 None => (None, None),
209 Some(TcpCountersWithoutSocketInner {
210 invalid_ip_addrs_received,
211 invalid_segments_received,
212 valid_segments_received,
213 received_segments_no_dispatch,
214 checksum_errors,
215 }) => (
216 Some(WithoutSocketRx { valid_segments_received }),
217 Some(WithoutSocketRxError {
218 invalid_ip_addrs_received,
219 invalid_segments_received,
220 received_segments_no_dispatch,
221 checksum_errors,
222 }),
223 ),
224 };
225
226 inspector.record_child("Rx", |inspector| {
227 if let Some(WithoutSocketRx { valid_segments_received }) = without_socket_rx {
228 inspector.record_counter("ValidSegmentsReceived", valid_segments_received);
229 }
230 inspector.record_counter("ReceivedSegmentsDispatched", received_segments_dispatched);
231 inspector.record_counter("ResetsReceived", resets_received);
232 inspector.record_counter("SynsReceived", syns_received);
233 inspector.record_counter("FinsReceived", fins_received);
234 inspector.record_counter("DupAcks", dup_acks);
235 inspector.record_child("Errors", |inspector| {
236 inspector.record_counter("ListenerQueueOverflow", listener_queue_overflow);
237 inspector.record_counter("PassiveOpenNoRouteErrors", passive_open_no_route_errors);
238 if let Some(WithoutSocketRxError {
239 invalid_ip_addrs_received,
240 invalid_segments_received,
241 received_segments_no_dispatch,
242 checksum_errors,
243 }) = without_socket_rx_error
244 {
245 inspector.record_counter("InvalidIpAddrsReceived", invalid_ip_addrs_received);
246 inspector.record_counter("InvalidSegmentsReceived", invalid_segments_received);
247 inspector.record_counter(
248 "ReceivedSegmentsNoDispatch",
249 received_segments_no_dispatch,
250 );
251 inspector.record_counter("ChecksumErrors", checksum_errors);
252 }
253 })
254 });
255 inspector.record_child("Tx", |inspector| {
256 inspector.record_counter("SegmentsSent", segments_sent);
257 inspector.record_counter("ResetsSent", resets_sent);
258 inspector.record_counter("SynsSent", syns_sent);
259 inspector.record_counter("FinsSent", fins_sent);
260 inspector.record_counter("Timeouts", timeouts);
261 inspector.record_counter("Retransmits", retransmits);
262 inspector.record_counter("SlowStartRetransmits", slow_start_retransmits);
263 inspector.record_counter("FastRetransmits", fast_retransmits);
264 inspector.record_counter("SackRetransmits", sack_retransmits);
265 inspector.record_child("Errors", |inspector| {
266 inspector.record_counter("SegmentSendErrors", segment_send_errors);
267 inspector.record_counter("ActiveOpenNoRouteErrors", active_open_no_route_errors);
268 });
269 });
270 inspector.record_counter("PassiveConnectionOpenings", passive_connection_openings);
271 inspector.record_counter("ActiveConnectionOpenings", active_connection_openings);
272 inspector.record_counter("FastRecovery", fast_recovery);
273 inspector.record_counter("SackRecovery", sack_recovery);
274 inspector.record_counter("LossRecovered", loss_recovered);
275 inspector.record_counter("EstablishedClosed", established_closed);
276 inspector.record_counter("EstablishedResets", established_resets);
277 inspector.record_counter("EstablishedTimedout", established_timedout);
278 inspector.record_child("Errors", |inspector| {
279 inspector.record_counter("FailedConnectionOpenings", failed_connection_attempts);
280 inspector.record_counter("FailedPortReservations", failed_port_reservations);
281 })
282 }
283}
284
285pub(crate) struct TcpCountersRefs<'a> {
292 pub(crate) stack_wide: &'a TcpCountersWithSocketInner,
293 pub(crate) per_socket: &'a TcpCountersWithSocketInner,
294}
295
296impl<'a> TcpCountersRefs<'a> {
297 pub(crate) fn from_ctx<I: Ip, R, CC: ResourceCounterContext<R, TcpCountersWithSocket<I>>>(
298 ctx: &'a CC,
299 resource: &'a R,
300 ) -> Self {
301 TcpCountersRefs {
302 stack_wide: ctx.counters(),
303 per_socket: ctx.per_resource_counters(resource),
304 }
305 }
306
307 pub(crate) fn increment<F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter>(&self, cb: F) {
308 let Self { stack_wide, per_socket } = self;
309 cb(stack_wide).increment();
310 cb(per_socket).increment();
311 }
312}
313
314pub(crate) fn increment_counter_with_optional_socket_id<I, CC, BT, D, F>(
319 core_ctx: &CC,
320 socket_id: Option<&TcpSocketId<I, D, BT>>,
321 cb: F,
322) where
323 I: DualStackIpExt,
324 CC: TcpCounterContext<I, D, BT>,
325 D: WeakDeviceIdentifier,
326 BT: TcpBindingsTypes,
327 F: Fn(&TcpCountersWithSocket<I>) -> &Counter,
328{
329 match socket_id {
330 Some(id) => core_ctx.increment_both(id, cb),
331 None => cb(core_ctx.counters()).increment(),
332 }
333}
334
335pub(crate) fn increment_counter_with_optional_demux_id<I, CC, BT, D, F>(
340 core_ctx: &CC,
341 demux_id: Option<&I::DemuxSocketId<D, BT>>,
342 cb: F,
343) where
344 I: DualStackIpExt,
345 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
346 D: WeakDeviceIdentifier,
347 BT: TcpBindingsTypes,
348 F: Fn(&TcpCountersWithSocketInner) -> &Counter,
349{
350 match demux_id {
351 Some(id) => increment_counter_for_demux_id::<I, _, _, _, _>(core_ctx, &id, cb),
352 None => {
353 cb(CounterContext::<TcpCountersWithSocket<I>>::counters(core_ctx).as_ref()).increment()
354 }
355 }
356}
357
358pub(crate) fn increment_counter_for_demux_id<I, D, BT, CC, F>(
360 core_ctx: &CC,
361 demux_id: &I::DemuxSocketId<D, BT>,
362 cb: F,
363) where
364 I: DualStackIpExt,
365 D: WeakDeviceIdentifier,
366 BT: TcpBindingsTypes,
367 CC: TcpCounterContext<I, D, BT> + TcpCounterContext<I::OtherVersion, D, BT>,
368 F: Fn(&TcpCountersWithSocketInner<Counter>) -> &Counter,
369{
370 match I::as_dual_stack_ip_socket(demux_id) {
371 EitherStack::ThisStack(socket_id) => core_ctx
372 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I>| cb(counters.as_ref())),
373 EitherStack::OtherStack(socket_id) => core_ctx
374 .increment_both(socket_id, |counters: &TcpCountersWithSocket<I::OtherVersion>| {
375 cb(counters.as_ref())
376 }),
377 }
378}
379
380#[cfg(test)]
381pub(crate) mod testutil {
382 use super::*;
383
384 pub(crate) type CounterExpectations = TcpCountersWithSocketInner<u64>;
385
386 pub(crate) type CounterExpectationsWithoutSocket = TcpCountersWithoutSocketInner<u64>;
387}