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 pub unparsable_packet: C,
115}
116
117impl<I: IpCountersIpExt> Inspectable for IpCounters<I> {
118 fn record<II: Inspector>(&self, inspector: &mut II) {
119 let IpCounters {
120 deliver_unicast,
121 deliver_multicast,
122 dispatch_receive_ip_packet,
123 dispatch_receive_ip_packet_other_host,
124 receive_ip_packet,
125 send_ip_packet,
126 forwarding_disabled,
127 forward,
128 no_route_to_host,
129 mtu_exceeded,
130 ttl_expired,
131 receive_icmp_error,
132 fragment_reassembly_error,
133 need_more_fragments,
134 invalid_fragment,
135 fragment_cache_full,
136 parameter_problem,
137 unspecified_destination,
138 unspecified_source,
139 invalid_source,
140 dropped,
141 drop_for_tentative,
142 tx_illegal_loopback_address,
143 version_rx,
144 multicast_no_interest,
145 invalid_cached_conntrack_entry,
146 fragmentation,
147 socket_egress_filter_dropped,
148 unparsable_packet,
149 } = self;
150 inspector.record_child("PacketTx", |inspector| {
151 inspector.record_counter("Sent", send_ip_packet);
152 inspector.record_counter("IllegalLoopbackAddress", tx_illegal_loopback_address);
153 inspector.record_counter("SocketEgressFilterDropped", socket_egress_filter_dropped);
154 });
155 inspector.record_child("PacketRx", |inspector| {
156 inspector.record_counter("Received", receive_ip_packet);
157 inspector.record_counter("Dispatched", dispatch_receive_ip_packet);
158 inspector.record_counter("OtherHost", dispatch_receive_ip_packet_other_host);
159 inspector.record_counter("ParameterProblem", parameter_problem);
160 inspector.record_counter("UnspecifiedDst", unspecified_destination);
161 inspector.record_counter("UnspecifiedSrc", unspecified_source);
162 inspector.record_counter("InvalidSrc", invalid_source);
163 inspector.record_counter("Dropped", dropped);
164 inspector.record_counter("DroppedTentativeDst", drop_for_tentative);
165 inspector.record_counter("MulticastNoInterest", multicast_no_interest);
166 inspector.record_counter("DeliveredUnicast", deliver_unicast);
167 inspector.record_counter("DeliveredMulticast", deliver_multicast);
168 inspector.record_counter("InvalidCachedConntrackEntry", invalid_cached_conntrack_entry);
169 inspector.record_counter("UnparsablePacket", unparsable_packet);
170 inspector.delegate_inspectable(version_rx);
171 });
172 inspector.record_child("Forwarding", |inspector| {
173 inspector.record_counter("Forwarded", forward);
174 inspector.record_counter("ForwardingDisabled", forwarding_disabled);
175 inspector.record_counter("NoRouteToHost", no_route_to_host);
176 inspector.record_counter("MtuExceeded", mtu_exceeded);
177 inspector.record_counter("TtlExpired", ttl_expired);
178 });
179 inspector.record_counter("RxIcmpError", receive_icmp_error);
180 inspector.record_child("FragmentsRx", |inspector| {
181 inspector.record_counter("ReassemblyError", fragment_reassembly_error);
182 inspector.record_counter("NeedMoreFragments", need_more_fragments);
183 inspector.record_counter("InvalidFragment", invalid_fragment);
184 inspector.record_counter("CacheFull", fragment_cache_full);
185 });
186 inspector.record_child("FragmentsTx", |inspector| {
187 let FragmentationCounters {
188 fragmentation_required,
189 fragments,
190 error_not_allowed,
191 error_mtu_too_small,
192 error_body_too_long,
193 error_inner_size_limit_exceeded,
194 error_fragmented_serializer,
195 } = fragmentation;
196 inspector.record_counter("FragmentationRequired", fragmentation_required);
197 inspector.record_counter("Fragments", fragments);
198 inspector.record_counter("ErrorNotAllowed", error_not_allowed);
199 inspector.record_counter("ErrorMtuTooSmall", error_mtu_too_small);
200 inspector.record_counter("ErrorBodyTooLong", error_body_too_long);
201 inspector
202 .record_counter("ErrorInnerSizeLimitExceeded", error_inner_size_limit_exceeded);
203 inspector.record_counter("ErrorFragmentedSerializer", error_fragmented_serializer);
204 });
205 }
206}
207
208#[derive(Default, Debug, netstack3_macros::CounterCollection)]
210#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
211pub struct Ipv4RxCounters<C = Counter> {
212 pub deliver_broadcast: C,
214}
215
216impl<C: CounterRepr> Inspectable for Ipv4RxCounters<C> {
217 fn record<I: Inspector>(&self, inspector: &mut I) {
218 let Self { deliver_broadcast } = self;
219 inspector.record_counter("DeliveredBroadcast", deliver_broadcast);
220 }
221}
222
223#[derive(Default, Debug, netstack3_macros::CounterCollection)]
225#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
226pub struct Ipv6RxCounters<C = Counter> {
227 pub extension_header_discard: C,
230 pub drop_looped_back_dad_probe: C,
233}
234
235impl<C: CounterRepr> Inspectable for Ipv6RxCounters<C> {
236 fn record<I: Inspector>(&self, inspector: &mut I) {
237 let Self { extension_header_discard, drop_looped_back_dad_probe } = self;
238 inspector.record_counter("DroppedExtensionHeader", extension_header_discard);
239 inspector.record_counter("DroppedLoopedBackDadProbe", drop_looped_back_dad_probe);
240 }
241}
242
243#[cfg(any(test, feature = "testutils"))]
244pub mod testutil {
245 use super::*;
246
247 use netstack3_base::{CounterCollection, ResourceCounterContext};
248
249 pub type IpCounterExpectations<I> = IpCounters<I, u64>;
251
252 impl<I: IpCountersIpExt> IpCounterExpectations<I> {
253 pub fn expect_dispatched(count: u64) -> Self {
256 IpCounterExpectations {
257 receive_ip_packet: count,
258 dispatch_receive_ip_packet: count,
259 deliver_unicast: count,
260 ..Default::default()
261 }
262 }
263
264 #[track_caller]
266 pub fn assert_counters<D, CC: ResourceCounterContext<D, IpCounters<I>>>(
267 self,
268 core_ctx: &CC,
269 device: &D,
270 ) {
271 assert_eq!(core_ctx.counters().cast::<u64>(), self, "stack-wide counters");
272 assert_eq!(
273 core_ctx.per_resource_counters(device).cast::<u64>(),
274 self,
275 "per-device counters"
276 );
277 }
278 }
279}