1use core::fmt::Debug;
8use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6};
9use netstack3_base::{
10 Counter, CounterCollectionSpec, CounterRepr, Inspectable, Inspector, InspectorExt as _,
11 TestOnlyPartialEq,
12};
13
14use crate::internal::fragmentation::FragmentationCounters;
15use crate::internal::gso::{GsoCounters, GsoSourceCounters};
16
17pub trait IpCountersIpExt: Ip {
19 type RxCounters: CounterCollectionSpec<CounterCollection<Counter>: Inspectable>
21 + Default
22 + Debug
23 + TestOnlyPartialEq;
24}
25
26impl IpCountersIpExt for Ipv4 {
27 type RxCounters = Ipv4RxCounters;
28}
29
30impl IpCountersIpExt for Ipv6 {
31 type RxCounters = Ipv6RxCounters;
32}
33
34#[derive(Default, Debug, GenericOverIp)]
36#[generic_over_ip(I, Ip)]
37#[cfg_attr(
38 any(test, feature = "testutils"),
39 derive(PartialEq, netstack3_macros::CounterCollection)
40)]
41pub struct IpCounters<I: IpCountersIpExt, C: CounterRepr = Counter> {
42 pub deliver_unicast: C,
44 pub deliver_multicast: C,
46 pub dispatch_receive_ip_packet: C,
48 pub drop_ip_packet_other_host: C,
50 pub receive_ip_packet: C,
52 pub send_ip_packet: C,
54 pub forwarding_disabled: C,
57 pub forward: C,
59 pub no_route_to_host: C,
62 pub mtu_exceeded: C,
65 pub ttl_expired: C,
68 pub receive_icmp_error: C,
70 pub fragment_reassembly_error: C,
72 pub need_more_fragments: C,
75 pub invalid_fragment: C,
78 pub fragment_cache_full: C,
81 pub parameter_problem: C,
83 pub unspecified_destination: C,
85 pub unspecified_source: C,
87 pub invalid_source: C,
91 pub dropped: C,
93 pub drop_for_tentative: C,
96 pub tx_illegal_loopback_address: C,
99 pub version_rx: <I::RxCounters as CounterCollectionSpec>::CounterCollection<C>,
101 pub multicast_no_interest: C,
105 pub invalid_cached_conntrack_entry: C,
109 pub fragmentation: FragmentationCounters<C>,
111 pub gso: GsoCounters<C>,
113 pub socket_egress_filter_dropped: C,
115 pub unparsable_packet: C,
118}
119
120impl<I: IpCountersIpExt> Inspectable for IpCounters<I> {
121 fn record<II: Inspector>(&self, inspector: &mut II) {
122 let IpCounters {
123 deliver_unicast,
124 deliver_multicast,
125 dispatch_receive_ip_packet,
126 drop_ip_packet_other_host,
127 receive_ip_packet,
128 send_ip_packet,
129 forwarding_disabled,
130 forward,
131 no_route_to_host,
132 mtu_exceeded,
133 ttl_expired,
134 receive_icmp_error,
135 fragment_reassembly_error,
136 need_more_fragments,
137 invalid_fragment,
138 fragment_cache_full,
139 parameter_problem,
140 unspecified_destination,
141 unspecified_source,
142 invalid_source,
143 dropped,
144 drop_for_tentative,
145 tx_illegal_loopback_address,
146 version_rx,
147 multicast_no_interest,
148 invalid_cached_conntrack_entry,
149 fragmentation,
150 gso,
151 socket_egress_filter_dropped,
152 unparsable_packet,
153 } = self;
154 inspector.record_child("PacketTx", |inspector| {
155 inspector.record_counter("Sent", send_ip_packet);
156 inspector.record_counter("IllegalLoopbackAddress", tx_illegal_loopback_address);
157 inspector.record_counter("SocketEgressFilterDropped", socket_egress_filter_dropped);
158 });
159 inspector.record_child("PacketRx", |inspector| {
160 inspector.record_counter("Received", receive_ip_packet);
161 inspector.record_counter("Dispatched", dispatch_receive_ip_packet);
162 inspector.record_counter("OtherHost", drop_ip_packet_other_host);
163 inspector.record_counter("ParameterProblem", parameter_problem);
164 inspector.record_counter("UnspecifiedDst", unspecified_destination);
165 inspector.record_counter("UnspecifiedSrc", unspecified_source);
166 inspector.record_counter("InvalidSrc", invalid_source);
167 inspector.record_counter("Dropped", dropped);
168 inspector.record_counter("DroppedTentativeDst", drop_for_tentative);
169 inspector.record_counter("MulticastNoInterest", multicast_no_interest);
170 inspector.record_counter("DeliveredUnicast", deliver_unicast);
171 inspector.record_counter("DeliveredMulticast", deliver_multicast);
172 inspector.record_counter("InvalidCachedConntrackEntry", invalid_cached_conntrack_entry);
173 inspector.record_counter("UnparsablePacket", unparsable_packet);
174 inspector.delegate_inspectable(version_rx);
175 });
176 inspector.record_child("Forwarding", |inspector| {
177 inspector.record_counter("Forwarded", forward);
178 inspector.record_counter("ForwardingDisabled", forwarding_disabled);
179 inspector.record_counter("NoRouteToHost", no_route_to_host);
180 inspector.record_counter("MtuExceeded", mtu_exceeded);
181 inspector.record_counter("TtlExpired", ttl_expired);
182 });
183 inspector.record_counter("RxIcmpError", receive_icmp_error);
184 inspector.record_child("FragmentsRx", |inspector| {
185 inspector.record_counter("ReassemblyError", fragment_reassembly_error);
186 inspector.record_counter("NeedMoreFragments", need_more_fragments);
187 inspector.record_counter("InvalidFragment", invalid_fragment);
188 inspector.record_counter("CacheFull", fragment_cache_full);
189 });
190 inspector.record_child("FragmentsTx", |inspector| {
191 let FragmentationCounters {
192 fragmentation_required,
193 fragments,
194 error_not_allowed,
195 error_mtu_too_small,
196 error_body_too_long,
197 error_inner_size_limit_exceeded,
198 error_fragmented_serializer,
199 } = fragmentation;
200 inspector.record_counter("FragmentationRequired", fragmentation_required);
201 inspector.record_counter("Fragments", fragments);
202 inspector.record_counter("ErrorNotAllowed", error_not_allowed);
203 inspector.record_counter("ErrorMtuTooSmall", error_mtu_too_small);
204 inspector.record_counter("ErrorBodyTooLong", error_body_too_long);
205 inspector
206 .record_counter("ErrorInnerSizeLimitExceeded", error_inner_size_limit_exceeded);
207 inspector.record_counter("ErrorFragmentedSerializer", error_fragmented_serializer);
208 });
209 inspector.record_child("SegmentsTx", |inspector| {
210 let GsoCounters { forwarded, local } = gso;
211 inspector.record_child("Forwarded", |inspector| {
212 inspector.delegate_inspectable(forwarded);
213 });
214 inspector.record_child("Local", |inspector| {
215 inspector.delegate_inspectable(local);
216 });
217 });
218 }
219}
220
221impl<C: CounterRepr> Inspectable for GsoSourceCounters<C> {
222 fn record<I: Inspector>(&self, inspector: &mut I) {
223 let Self {
224 segmentation_required,
225 segments,
226 error_parse,
227 error_not_segmentable,
228 error_unsupported_headers,
229 error_payload_too_long,
230 } = self;
231 inspector.record_counter("SegmentationRequired", segmentation_required);
232 inspector.record_counter("Segments", segments);
233 inspector.record_counter("ErrorParse", error_parse);
234 inspector.record_counter("ErrorNotSegmentable", error_not_segmentable);
235 inspector.record_counter("ErrorUnsupportedHeaders", error_unsupported_headers);
236 inspector.record_counter("ErrorPayloadTooLong", error_payload_too_long);
237 }
238}
239
240#[derive(Default, Debug, netstack3_macros::CounterCollection)]
242#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
243pub struct Ipv4RxCounters<C = Counter> {
244 pub deliver_broadcast: C,
246}
247
248impl<C: CounterRepr> Inspectable for Ipv4RxCounters<C> {
249 fn record<I: Inspector>(&self, inspector: &mut I) {
250 let Self { deliver_broadcast } = self;
251 inspector.record_counter("DeliveredBroadcast", deliver_broadcast);
252 }
253}
254
255#[derive(Default, Debug, netstack3_macros::CounterCollection)]
257#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
258pub struct Ipv6RxCounters<C = Counter> {
259 pub extension_header_discard: C,
262 pub drop_looped_back_dad_probe: C,
265}
266
267impl<C: CounterRepr> Inspectable for Ipv6RxCounters<C> {
268 fn record<I: Inspector>(&self, inspector: &mut I) {
269 let Self { extension_header_discard, drop_looped_back_dad_probe } = self;
270 inspector.record_counter("DroppedExtensionHeader", extension_header_discard);
271 inspector.record_counter("DroppedLoopedBackDadProbe", drop_looped_back_dad_probe);
272 }
273}
274
275#[cfg(any(test, feature = "testutils"))]
276pub mod testutil {
277 use super::*;
278
279 use netstack3_base::{CounterCollection, ResourceCounterContext};
280
281 pub type IpCounterExpectations<I> = IpCounters<I, u64>;
283
284 impl<I: IpCountersIpExt> IpCounterExpectations<I> {
285 pub fn expect_dispatched(count: u64) -> Self {
288 IpCounterExpectations {
289 receive_ip_packet: count,
290 dispatch_receive_ip_packet: count,
291 deliver_unicast: count,
292 ..Default::default()
293 }
294 }
295
296 #[track_caller]
298 pub fn assert_counters<D, CC: ResourceCounterContext<D, IpCounters<I>>>(
299 self,
300 core_ctx: &CC,
301 device: &D,
302 ) {
303 assert_eq!(core_ctx.counters().cast::<u64>(), self, "stack-wide counters");
304 assert_eq!(
305 core_ctx.per_resource_counters(device).cast::<u64>(),
306 self,
307 "per-device counters"
308 );
309 }
310 }
311}