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 dropped: C,
86 pub tx_illegal_loopback_address: C,
89 pub version_rx: I::RxCounters<C>,
91 pub multicast_no_interest: C,
95 pub invalid_cached_conntrack_entry: C,
99 pub fragmentation: FragmentationCounters<C>,
101}
102
103impl<I: IpCountersIpExt, C: CounterRepr> Inspectable for IpCounters<I, C> {
104 fn record<II: Inspector>(&self, inspector: &mut II) {
105 let IpCounters {
106 deliver_unicast,
107 deliver_multicast,
108 dispatch_receive_ip_packet,
109 dispatch_receive_ip_packet_other_host,
110 receive_ip_packet,
111 send_ip_packet,
112 forwarding_disabled,
113 forward,
114 no_route_to_host,
115 mtu_exceeded,
116 ttl_expired,
117 receive_icmp_error,
118 fragment_reassembly_error,
119 need_more_fragments,
120 invalid_fragment,
121 fragment_cache_full,
122 parameter_problem,
123 unspecified_destination,
124 unspecified_source,
125 dropped,
126 tx_illegal_loopback_address,
127 version_rx,
128 multicast_no_interest,
129 invalid_cached_conntrack_entry,
130 fragmentation,
131 } = self;
132 inspector.record_child("PacketTx", |inspector| {
133 inspector.record_counter("Sent", send_ip_packet);
134 inspector.record_counter("IllegalLoopbackAddress", tx_illegal_loopback_address);
135 });
136 inspector.record_child("PacketRx", |inspector| {
137 inspector.record_counter("Received", receive_ip_packet);
138 inspector.record_counter("Dispatched", dispatch_receive_ip_packet);
139 inspector.record_counter("OtherHost", dispatch_receive_ip_packet_other_host);
140 inspector.record_counter("ParameterProblem", parameter_problem);
141 inspector.record_counter("UnspecifiedDst", unspecified_destination);
142 inspector.record_counter("UnspecifiedSrc", unspecified_source);
143 inspector.record_counter("Dropped", dropped);
144 inspector.record_counter("MulticastNoInterest", multicast_no_interest);
145 inspector.record_counter("DeliveredUnicast", deliver_unicast);
146 inspector.record_counter("DeliveredMulticast", deliver_multicast);
147 inspector.record_counter("InvalidCachedConntrackEntry", invalid_cached_conntrack_entry);
148 inspector.delegate_inspectable(version_rx);
149 });
150 inspector.record_child("Forwarding", |inspector| {
151 inspector.record_counter("Forwarded", forward);
152 inspector.record_counter("ForwardingDisabled", forwarding_disabled);
153 inspector.record_counter("NoRouteToHost", no_route_to_host);
154 inspector.record_counter("MtuExceeded", mtu_exceeded);
155 inspector.record_counter("TtlExpired", ttl_expired);
156 });
157 inspector.record_counter("RxIcmpError", receive_icmp_error);
158 inspector.record_child("FragmentsRx", |inspector| {
159 inspector.record_counter("ReassemblyError", fragment_reassembly_error);
160 inspector.record_counter("NeedMoreFragments", need_more_fragments);
161 inspector.record_counter("InvalidFragment", invalid_fragment);
162 inspector.record_counter("CacheFull", fragment_cache_full);
163 });
164 inspector.record_child("FragmentsTx", |inspector| {
165 let FragmentationCounters {
166 fragmentation_required,
167 fragments,
168 error_not_allowed,
169 error_mtu_too_small,
170 error_body_too_long,
171 error_inner_size_limit_exceeded,
172 error_fragmented_serializer,
173 } = fragmentation;
174 inspector.record_counter("FragmentationRequired", fragmentation_required);
175 inspector.record_counter("Fragments", fragments);
176 inspector.record_counter("ErrorNotAllowed", error_not_allowed);
177 inspector.record_counter("ErrorMtuTooSmall", error_mtu_too_small);
178 inspector.record_counter("ErrorBodyTooLong", error_body_too_long);
179 inspector
180 .record_counter("ErrorInnerSizeLimitExceeded", error_inner_size_limit_exceeded);
181 inspector.record_counter("ErrorFragmentedSerializer", error_fragmented_serializer);
182 });
183 }
184}
185
186#[derive(Default, Debug)]
188#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq))]
189pub struct Ipv4RxCounters<C: CounterRepr = Counter> {
190 pub deliver_broadcast: C,
192}
193
194impl<C: CounterRepr> Inspectable for Ipv4RxCounters<C> {
195 fn record<I: Inspector>(&self, inspector: &mut I) {
196 let Self { deliver_broadcast } = self;
197 inspector.record_counter("DeliveredBroadcast", deliver_broadcast);
198 }
199}
200
201#[derive(Default, Debug)]
203#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq))]
204pub struct Ipv6RxCounters<C: CounterRepr = Counter> {
205 pub drop_for_tentative: C,
208 pub non_unicast_source: C,
210 pub extension_header_discard: C,
213 pub drop_looped_back_dad_probe: C,
216}
217
218impl<C: CounterRepr> Inspectable for Ipv6RxCounters<C> {
219 fn record<I: Inspector>(&self, inspector: &mut I) {
220 let Self {
221 drop_for_tentative,
222 non_unicast_source,
223 extension_header_discard,
224 drop_looped_back_dad_probe,
225 } = self;
226 inspector.record_counter("DroppedTentativeDst", drop_for_tentative);
227 inspector.record_counter("DroppedNonUnicastSrc", non_unicast_source);
228 inspector.record_counter("DroppedExtensionHeader", extension_header_discard);
229 inspector.record_counter("DroppedLoopedBackDadProbe", drop_looped_back_dad_probe);
230 }
231}
232
233#[cfg(any(test, feature = "testutils"))]
234pub mod testutil {
235 use super::*;
236
237 use netstack3_base::ResourceCounterContext;
238
239 impl<C: CounterRepr> From<&Ipv4RxCounters> for Ipv4RxCounters<C> {
240 fn from(counters: &Ipv4RxCounters) -> Ipv4RxCounters<C> {
241 let Ipv4RxCounters { deliver_broadcast } = counters;
242 Ipv4RxCounters { deliver_broadcast: deliver_broadcast.into_repr() }
243 }
244 }
245
246 impl<C: CounterRepr> From<&Ipv6RxCounters> for Ipv6RxCounters<C> {
247 fn from(counters: &Ipv6RxCounters) -> Ipv6RxCounters<C> {
248 let Ipv6RxCounters {
249 drop_for_tentative,
250 non_unicast_source,
251 extension_header_discard,
252 drop_looped_back_dad_probe,
253 } = counters;
254 Ipv6RxCounters {
255 drop_for_tentative: drop_for_tentative.get().into_repr(),
256 non_unicast_source: non_unicast_source.into_repr(),
257 extension_header_discard: extension_header_discard.into_repr(),
258 drop_looped_back_dad_probe: drop_looped_back_dad_probe.into_repr(),
259 }
260 }
261 }
262
263 pub type IpCounterExpectations<I> = IpCounters<I, u64>;
265
266 impl<I: IpCountersIpExt> From<&IpCounters<I>> for IpCounterExpectations<I> {
267 fn from(counters: &IpCounters<I>) -> IpCounterExpectations<I> {
268 let IpCounters {
269 deliver_unicast,
270 deliver_multicast,
271 dispatch_receive_ip_packet,
272 dispatch_receive_ip_packet_other_host,
273 receive_ip_packet,
274 send_ip_packet,
275 forwarding_disabled,
276 forward,
277 no_route_to_host,
278 mtu_exceeded,
279 ttl_expired,
280 receive_icmp_error,
281 fragment_reassembly_error,
282 need_more_fragments,
283 invalid_fragment,
284 fragment_cache_full,
285 parameter_problem,
286 unspecified_destination,
287 unspecified_source,
288 dropped,
289 tx_illegal_loopback_address,
290 version_rx,
291 multicast_no_interest,
292 invalid_cached_conntrack_entry,
293 fragmentation,
294 } = counters;
295 IpCounterExpectations {
296 deliver_unicast: deliver_unicast.get(),
297 deliver_multicast: deliver_multicast.get(),
298 dispatch_receive_ip_packet: dispatch_receive_ip_packet.get(),
299 dispatch_receive_ip_packet_other_host: dispatch_receive_ip_packet_other_host.get(),
300 receive_ip_packet: receive_ip_packet.get(),
301 send_ip_packet: send_ip_packet.get(),
302 forwarding_disabled: forwarding_disabled.get(),
303 forward: forward.get(),
304 no_route_to_host: no_route_to_host.get(),
305 mtu_exceeded: mtu_exceeded.get(),
306 ttl_expired: ttl_expired.get(),
307 receive_icmp_error: receive_icmp_error.get(),
308 fragment_reassembly_error: fragment_reassembly_error.get(),
309 need_more_fragments: need_more_fragments.get(),
310 invalid_fragment: invalid_fragment.get(),
311 fragment_cache_full: fragment_cache_full.get(),
312 parameter_problem: parameter_problem.get(),
313 unspecified_destination: unspecified_destination.get(),
314 unspecified_source: unspecified_source.get(),
315 dropped: dropped.get(),
316 tx_illegal_loopback_address: tx_illegal_loopback_address.get(),
317 version_rx: version_rx.into(),
318 multicast_no_interest: multicast_no_interest.get(),
319 invalid_cached_conntrack_entry: invalid_cached_conntrack_entry.get(),
320 fragmentation: fragmentation.into(),
321 }
322 }
323 }
324
325 impl<I: IpCountersIpExt> IpCounterExpectations<I> {
326 pub fn expect_dispatched(count: u64) -> Self {
329 IpCounterExpectations {
330 receive_ip_packet: count,
331 dispatch_receive_ip_packet: count,
332 deliver_unicast: count,
333 ..Default::default()
334 }
335 }
336
337 #[track_caller]
339 pub fn assert_counters<D, CC: ResourceCounterContext<D, IpCounters<I>>>(
340 self,
341 core_ctx: &CC,
342 device: &D,
343 ) {
344 assert_eq!(
345 &IpCounterExpectations::from(core_ctx.counters()),
346 &self,
347 "stack-wide counters"
348 );
349 assert_eq!(
350 &IpCounterExpectations::from(core_ctx.per_resource_counters(device)),
351 &self,
352 "per-device counters"
353 );
354 }
355 }
356}