1use core::fmt::Debug;
8use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6};
9use netstack3_base::{
10 Counter, CounterRepr, Inspectable, Inspector, InspectorExt as _, TestOnlyFrom,
11 TestOnlyPartialEq,
12};
13
14use crate::internal::fragmentation::FragmentationCounters;
15
16pub trait IpCountersIpExt: Ip {
18 type RxCounters<C: CounterRepr>: Default
20 + Debug
21 + Inspectable
22 + TestOnlyPartialEq
23 + for<'a> TestOnlyFrom<&'a Self::RxCounters<Counter>>;
24}
25
26impl IpCountersIpExt for Ipv4 {
27 type RxCounters<C: CounterRepr> = Ipv4RxCounters<C>;
28}
29
30impl IpCountersIpExt for Ipv6 {
31 type RxCounters<C: CounterRepr> = Ipv6RxCounters<C>;
32}
33
34#[derive(Default, Debug, GenericOverIp)]
36#[generic_over_ip(I, Ip)]
37#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq))]
38pub struct IpCounters<I: IpCountersIpExt, C: CounterRepr = Counter> {
39 pub deliver_unicast: C,
41 pub deliver_multicast: C,
43 pub dispatch_receive_ip_packet: C,
45 pub dispatch_receive_ip_packet_other_host: C,
47 pub receive_ip_packet: C,
49 pub send_ip_packet: C,
51 pub forwarding_disabled: C,
54 pub forward: C,
56 pub no_route_to_host: C,
59 pub mtu_exceeded: C,
62 pub ttl_expired: C,
65 pub receive_icmp_error: C,
67 pub fragment_reassembly_error: C,
69 pub need_more_fragments: C,
72 pub invalid_fragment: C,
75 pub fragment_cache_full: C,
78 pub parameter_problem: C,
80 pub unspecified_destination: C,
82 pub unspecified_source: C,
84 pub invalid_source: C,
88 pub dropped: C,
90 pub tx_illegal_loopback_address: C,
93 pub version_rx: I::RxCounters<C>,
95 pub multicast_no_interest: C,
99 pub invalid_cached_conntrack_entry: C,
103 pub fragmentation: FragmentationCounters<C>,
105 pub socket_egress_filter_dropped: C,
107}
108
109impl<I: IpCountersIpExt, C: CounterRepr> Inspectable for IpCounters<I, C> {
110 fn record<II: Inspector>(&self, inspector: &mut II) {
111 let IpCounters {
112 deliver_unicast,
113 deliver_multicast,
114 dispatch_receive_ip_packet,
115 dispatch_receive_ip_packet_other_host,
116 receive_ip_packet,
117 send_ip_packet,
118 forwarding_disabled,
119 forward,
120 no_route_to_host,
121 mtu_exceeded,
122 ttl_expired,
123 receive_icmp_error,
124 fragment_reassembly_error,
125 need_more_fragments,
126 invalid_fragment,
127 fragment_cache_full,
128 parameter_problem,
129 unspecified_destination,
130 unspecified_source,
131 invalid_source,
132 dropped,
133 tx_illegal_loopback_address,
134 version_rx,
135 multicast_no_interest,
136 invalid_cached_conntrack_entry,
137 fragmentation,
138 socket_egress_filter_dropped,
139 } = self;
140 inspector.record_child("PacketTx", |inspector| {
141 inspector.record_counter("Sent", send_ip_packet);
142 inspector.record_counter("IllegalLoopbackAddress", tx_illegal_loopback_address);
143 inspector.record_counter("SocketEgressFilterDropped", socket_egress_filter_dropped);
144 });
145 inspector.record_child("PacketRx", |inspector| {
146 inspector.record_counter("Received", receive_ip_packet);
147 inspector.record_counter("Dispatched", dispatch_receive_ip_packet);
148 inspector.record_counter("OtherHost", dispatch_receive_ip_packet_other_host);
149 inspector.record_counter("ParameterProblem", parameter_problem);
150 inspector.record_counter("UnspecifiedDst", unspecified_destination);
151 inspector.record_counter("UnspecifiedSrc", unspecified_source);
152 inspector.record_counter("InvalidSrc", invalid_source);
153 inspector.record_counter("Dropped", dropped);
154 inspector.record_counter("MulticastNoInterest", multicast_no_interest);
155 inspector.record_counter("DeliveredUnicast", deliver_unicast);
156 inspector.record_counter("DeliveredMulticast", deliver_multicast);
157 inspector.record_counter("InvalidCachedConntrackEntry", invalid_cached_conntrack_entry);
158 inspector.delegate_inspectable(version_rx);
159 });
160 inspector.record_child("Forwarding", |inspector| {
161 inspector.record_counter("Forwarded", forward);
162 inspector.record_counter("ForwardingDisabled", forwarding_disabled);
163 inspector.record_counter("NoRouteToHost", no_route_to_host);
164 inspector.record_counter("MtuExceeded", mtu_exceeded);
165 inspector.record_counter("TtlExpired", ttl_expired);
166 });
167 inspector.record_counter("RxIcmpError", receive_icmp_error);
168 inspector.record_child("FragmentsRx", |inspector| {
169 inspector.record_counter("ReassemblyError", fragment_reassembly_error);
170 inspector.record_counter("NeedMoreFragments", need_more_fragments);
171 inspector.record_counter("InvalidFragment", invalid_fragment);
172 inspector.record_counter("CacheFull", fragment_cache_full);
173 });
174 inspector.record_child("FragmentsTx", |inspector| {
175 let FragmentationCounters {
176 fragmentation_required,
177 fragments,
178 error_not_allowed,
179 error_mtu_too_small,
180 error_body_too_long,
181 error_inner_size_limit_exceeded,
182 error_fragmented_serializer,
183 } = fragmentation;
184 inspector.record_counter("FragmentationRequired", fragmentation_required);
185 inspector.record_counter("Fragments", fragments);
186 inspector.record_counter("ErrorNotAllowed", error_not_allowed);
187 inspector.record_counter("ErrorMtuTooSmall", error_mtu_too_small);
188 inspector.record_counter("ErrorBodyTooLong", error_body_too_long);
189 inspector
190 .record_counter("ErrorInnerSizeLimitExceeded", error_inner_size_limit_exceeded);
191 inspector.record_counter("ErrorFragmentedSerializer", error_fragmented_serializer);
192 });
193 }
194}
195
196#[derive(Default, Debug)]
198#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq))]
199pub struct Ipv4RxCounters<C: CounterRepr = Counter> {
200 pub deliver_broadcast: C,
202}
203
204impl<C: CounterRepr> Inspectable for Ipv4RxCounters<C> {
205 fn record<I: Inspector>(&self, inspector: &mut I) {
206 let Self { deliver_broadcast } = self;
207 inspector.record_counter("DeliveredBroadcast", deliver_broadcast);
208 }
209}
210
211#[derive(Default, Debug)]
213#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq))]
214pub struct Ipv6RxCounters<C: CounterRepr = Counter> {
215 pub drop_for_tentative: C,
218 pub extension_header_discard: C,
221 pub drop_looped_back_dad_probe: C,
224}
225
226impl<C: CounterRepr> Inspectable for Ipv6RxCounters<C> {
227 fn record<I: Inspector>(&self, inspector: &mut I) {
228 let Self { drop_for_tentative, extension_header_discard, drop_looped_back_dad_probe } =
229 self;
230 inspector.record_counter("DroppedTentativeDst", drop_for_tentative);
231 inspector.record_counter("DroppedExtensionHeader", extension_header_discard);
232 inspector.record_counter("DroppedLoopedBackDadProbe", drop_looped_back_dad_probe);
233 }
234}
235
236#[cfg(any(test, feature = "testutils"))]
237pub mod testutil {
238 use super::*;
239
240 use netstack3_base::ResourceCounterContext;
241
242 impl<C: CounterRepr> From<&Ipv4RxCounters> for Ipv4RxCounters<C> {
243 fn from(counters: &Ipv4RxCounters) -> Ipv4RxCounters<C> {
244 let Ipv4RxCounters { deliver_broadcast } = counters;
245 Ipv4RxCounters { deliver_broadcast: deliver_broadcast.into_repr() }
246 }
247 }
248
249 impl<C: CounterRepr> From<&Ipv6RxCounters> for Ipv6RxCounters<C> {
250 fn from(counters: &Ipv6RxCounters) -> Ipv6RxCounters<C> {
251 let Ipv6RxCounters {
252 drop_for_tentative,
253 extension_header_discard,
254 drop_looped_back_dad_probe,
255 } = counters;
256 Ipv6RxCounters {
257 drop_for_tentative: drop_for_tentative.get().into_repr(),
258 extension_header_discard: extension_header_discard.into_repr(),
259 drop_looped_back_dad_probe: drop_looped_back_dad_probe.into_repr(),
260 }
261 }
262 }
263
264 pub type IpCounterExpectations<I> = IpCounters<I, u64>;
266
267 impl<I: IpCountersIpExt> From<&IpCounters<I>> for IpCounterExpectations<I> {
268 fn from(counters: &IpCounters<I>) -> IpCounterExpectations<I> {
269 let IpCounters {
270 deliver_unicast,
271 deliver_multicast,
272 dispatch_receive_ip_packet,
273 dispatch_receive_ip_packet_other_host,
274 receive_ip_packet,
275 send_ip_packet,
276 forwarding_disabled,
277 forward,
278 no_route_to_host,
279 mtu_exceeded,
280 ttl_expired,
281 receive_icmp_error,
282 fragment_reassembly_error,
283 need_more_fragments,
284 invalid_fragment,
285 fragment_cache_full,
286 parameter_problem,
287 unspecified_destination,
288 unspecified_source,
289 invalid_source,
290 dropped,
291 tx_illegal_loopback_address,
292 version_rx,
293 multicast_no_interest,
294 invalid_cached_conntrack_entry,
295 fragmentation,
296 socket_egress_filter_dropped,
297 } = counters;
298 IpCounterExpectations {
299 deliver_unicast: deliver_unicast.get(),
300 deliver_multicast: deliver_multicast.get(),
301 dispatch_receive_ip_packet: dispatch_receive_ip_packet.get(),
302 dispatch_receive_ip_packet_other_host: dispatch_receive_ip_packet_other_host.get(),
303 receive_ip_packet: receive_ip_packet.get(),
304 send_ip_packet: send_ip_packet.get(),
305 forwarding_disabled: forwarding_disabled.get(),
306 forward: forward.get(),
307 no_route_to_host: no_route_to_host.get(),
308 mtu_exceeded: mtu_exceeded.get(),
309 ttl_expired: ttl_expired.get(),
310 receive_icmp_error: receive_icmp_error.get(),
311 fragment_reassembly_error: fragment_reassembly_error.get(),
312 need_more_fragments: need_more_fragments.get(),
313 invalid_fragment: invalid_fragment.get(),
314 fragment_cache_full: fragment_cache_full.get(),
315 parameter_problem: parameter_problem.get(),
316 unspecified_destination: unspecified_destination.get(),
317 unspecified_source: unspecified_source.get(),
318 invalid_source: invalid_source.get(),
319 dropped: dropped.get(),
320 tx_illegal_loopback_address: tx_illegal_loopback_address.get(),
321 version_rx: version_rx.into(),
322 multicast_no_interest: multicast_no_interest.get(),
323 invalid_cached_conntrack_entry: invalid_cached_conntrack_entry.get(),
324 fragmentation: fragmentation.into(),
325 socket_egress_filter_dropped: socket_egress_filter_dropped.get(),
326 }
327 }
328 }
329
330 impl<I: IpCountersIpExt> IpCounterExpectations<I> {
331 pub fn expect_dispatched(count: u64) -> Self {
334 IpCounterExpectations {
335 receive_ip_packet: count,
336 dispatch_receive_ip_packet: count,
337 deliver_unicast: count,
338 ..Default::default()
339 }
340 }
341
342 #[track_caller]
344 pub fn assert_counters<D, CC: ResourceCounterContext<D, IpCounters<I>>>(
345 self,
346 core_ctx: &CC,
347 device: &D,
348 ) {
349 assert_eq!(
350 &IpCounterExpectations::from(core_ctx.counters()),
351 &self,
352 "stack-wide counters"
353 );
354 assert_eq!(
355 &IpCounterExpectations::from(core_ctx.per_resource_counters(device)),
356 &self,
357 "per-device counters"
358 );
359 }
360 }
361}