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;
15
16pub trait IpCountersIpExt: Ip {
18 type RxCounters: CounterCollectionSpec<CounterCollection<Counter>: Inspectable>
20 + Default
21 + Debug
22 + TestOnlyPartialEq;
23}
24
25impl IpCountersIpExt for Ipv4 {
26 type RxCounters = Ipv4RxCounters;
27}
28
29impl IpCountersIpExt for Ipv6 {
30 type RxCounters = Ipv6RxCounters;
31}
32
33#[derive(Default, Debug, GenericOverIp)]
35#[generic_over_ip(I, Ip)]
36#[cfg_attr(
37 any(test, feature = "testutils"),
38 derive(PartialEq, netstack3_macros::CounterCollection)
39)]
40pub struct IpCounters<I: IpCountersIpExt, C: CounterRepr = Counter> {
41 pub deliver_unicast: C,
43 pub deliver_multicast: C,
45 pub dispatch_receive_ip_packet: C,
47 pub dispatch_receive_ip_packet_other_host: C,
49 pub receive_ip_packet: C,
51 pub send_ip_packet: C,
53 pub forwarding_disabled: C,
56 pub forward: C,
58 pub no_route_to_host: C,
61 pub mtu_exceeded: C,
64 pub ttl_expired: C,
67 pub receive_icmp_error: C,
69 pub fragment_reassembly_error: C,
71 pub need_more_fragments: C,
74 pub invalid_fragment: C,
77 pub fragment_cache_full: C,
80 pub parameter_problem: C,
82 pub unspecified_destination: C,
84 pub unspecified_source: C,
86 pub invalid_source: C,
90 pub dropped: C,
92 pub drop_for_tentative: C,
95 pub tx_illegal_loopback_address: C,
98 pub version_rx: <I::RxCounters as CounterCollectionSpec>::CounterCollection<C>,
100 pub multicast_no_interest: C,
104 pub invalid_cached_conntrack_entry: C,
108 pub fragmentation: FragmentationCounters<C>,
110 pub socket_egress_filter_dropped: C,
112}
113
114impl<I: IpCountersIpExt> Inspectable for IpCounters<I> {
115 fn record<II: Inspector>(&self, inspector: &mut II) {
116 let IpCounters {
117 deliver_unicast,
118 deliver_multicast,
119 dispatch_receive_ip_packet,
120 dispatch_receive_ip_packet_other_host,
121 receive_ip_packet,
122 send_ip_packet,
123 forwarding_disabled,
124 forward,
125 no_route_to_host,
126 mtu_exceeded,
127 ttl_expired,
128 receive_icmp_error,
129 fragment_reassembly_error,
130 need_more_fragments,
131 invalid_fragment,
132 fragment_cache_full,
133 parameter_problem,
134 unspecified_destination,
135 unspecified_source,
136 invalid_source,
137 dropped,
138 drop_for_tentative,
139 tx_illegal_loopback_address,
140 version_rx,
141 multicast_no_interest,
142 invalid_cached_conntrack_entry,
143 fragmentation,
144 socket_egress_filter_dropped,
145 } = self;
146 inspector.record_child("PacketTx", |inspector| {
147 inspector.record_counter("Sent", send_ip_packet);
148 inspector.record_counter("IllegalLoopbackAddress", tx_illegal_loopback_address);
149 inspector.record_counter("SocketEgressFilterDropped", socket_egress_filter_dropped);
150 });
151 inspector.record_child("PacketRx", |inspector| {
152 inspector.record_counter("Received", receive_ip_packet);
153 inspector.record_counter("Dispatched", dispatch_receive_ip_packet);
154 inspector.record_counter("OtherHost", dispatch_receive_ip_packet_other_host);
155 inspector.record_counter("ParameterProblem", parameter_problem);
156 inspector.record_counter("UnspecifiedDst", unspecified_destination);
157 inspector.record_counter("UnspecifiedSrc", unspecified_source);
158 inspector.record_counter("InvalidSrc", invalid_source);
159 inspector.record_counter("Dropped", dropped);
160 inspector.record_counter("DroppedTentativeDst", drop_for_tentative);
161 inspector.record_counter("MulticastNoInterest", multicast_no_interest);
162 inspector.record_counter("DeliveredUnicast", deliver_unicast);
163 inspector.record_counter("DeliveredMulticast", deliver_multicast);
164 inspector.record_counter("InvalidCachedConntrackEntry", invalid_cached_conntrack_entry);
165 inspector.delegate_inspectable(version_rx);
166 });
167 inspector.record_child("Forwarding", |inspector| {
168 inspector.record_counter("Forwarded", forward);
169 inspector.record_counter("ForwardingDisabled", forwarding_disabled);
170 inspector.record_counter("NoRouteToHost", no_route_to_host);
171 inspector.record_counter("MtuExceeded", mtu_exceeded);
172 inspector.record_counter("TtlExpired", ttl_expired);
173 });
174 inspector.record_counter("RxIcmpError", receive_icmp_error);
175 inspector.record_child("FragmentsRx", |inspector| {
176 inspector.record_counter("ReassemblyError", fragment_reassembly_error);
177 inspector.record_counter("NeedMoreFragments", need_more_fragments);
178 inspector.record_counter("InvalidFragment", invalid_fragment);
179 inspector.record_counter("CacheFull", fragment_cache_full);
180 });
181 inspector.record_child("FragmentsTx", |inspector| {
182 let FragmentationCounters {
183 fragmentation_required,
184 fragments,
185 error_not_allowed,
186 error_mtu_too_small,
187 error_body_too_long,
188 error_inner_size_limit_exceeded,
189 error_fragmented_serializer,
190 } = fragmentation;
191 inspector.record_counter("FragmentationRequired", fragmentation_required);
192 inspector.record_counter("Fragments", fragments);
193 inspector.record_counter("ErrorNotAllowed", error_not_allowed);
194 inspector.record_counter("ErrorMtuTooSmall", error_mtu_too_small);
195 inspector.record_counter("ErrorBodyTooLong", error_body_too_long);
196 inspector
197 .record_counter("ErrorInnerSizeLimitExceeded", error_inner_size_limit_exceeded);
198 inspector.record_counter("ErrorFragmentedSerializer", error_fragmented_serializer);
199 });
200 }
201}
202
203#[derive(Default, Debug, netstack3_macros::CounterCollection)]
205#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
206pub struct Ipv4RxCounters<C = Counter> {
207 pub deliver_broadcast: C,
209}
210
211impl<C: CounterRepr> Inspectable for Ipv4RxCounters<C> {
212 fn record<I: Inspector>(&self, inspector: &mut I) {
213 let Self { deliver_broadcast } = self;
214 inspector.record_counter("DeliveredBroadcast", deliver_broadcast);
215 }
216}
217
218#[derive(Default, Debug, netstack3_macros::CounterCollection)]
220#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
221pub struct Ipv6RxCounters<C = Counter> {
222 pub extension_header_discard: C,
225 pub drop_looped_back_dad_probe: C,
228}
229
230impl<C: CounterRepr> Inspectable for Ipv6RxCounters<C> {
231 fn record<I: Inspector>(&self, inspector: &mut I) {
232 let Self { extension_header_discard, drop_looped_back_dad_probe } = self;
233 inspector.record_counter("DroppedExtensionHeader", extension_header_discard);
234 inspector.record_counter("DroppedLoopedBackDadProbe", drop_looped_back_dad_probe);
235 }
236}
237
238#[cfg(any(test, feature = "testutils"))]
239pub mod testutil {
240 use super::*;
241
242 use netstack3_base::{CounterCollection, ResourceCounterContext};
243
244 pub type IpCounterExpectations<I> = IpCounters<I, u64>;
246
247 impl<I: IpCountersIpExt> IpCounterExpectations<I> {
248 pub fn expect_dispatched(count: u64) -> Self {
251 IpCounterExpectations {
252 receive_ip_packet: count,
253 dispatch_receive_ip_packet: count,
254 deliver_unicast: count,
255 ..Default::default()
256 }
257 }
258
259 #[track_caller]
261 pub fn assert_counters<D, CC: ResourceCounterContext<D, IpCounters<I>>>(
262 self,
263 core_ctx: &CC,
264 device: &D,
265 ) {
266 assert_eq!(core_ctx.counters().cast::<u64>(), self, "stack-wide counters");
267 assert_eq!(
268 core_ctx.per_resource_counters(device).cast::<u64>(),
269 self,
270 "per-device counters"
271 );
272 }
273 }
274}