Skip to main content

netstack3_ip/icmp/
counters.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Counters for the Internet Control Message Protocol (ICMP).
6
7use core::fmt::Debug;
8use net_types::ip::{Ip, Ipv4, Ipv6};
9use netstack3_base::{
10    Counter, CounterCollectionSpec, CounterRepr, Inspectable, Inspector, InspectorExt,
11    TestOnlyPartialEq,
12};
13use packet_formats::icmp::{
14    Icmpv4DestUnreachableCode, Icmpv4ParameterProblemCode, Icmpv4TimeExceededCode,
15    Icmpv6DestUnreachableCode, Icmpv6ParameterProblemCode, Icmpv6TimeExceededCode,
16};
17
18/// An IP Extension trait for ICMP Counters.
19pub trait IcmpCountersIpExt: Ip {
20    /// Counters for the ICMP Dest Unreachable message type.
21    type DestUnreachableCounters: CounterCollectionSpec<CounterCollection<Counter>: Inspectable>
22        + Default
23        + Debug
24        + TestOnlyPartialEq;
25
26    /// Counters for the ICMP Time Exceeded message type.
27    type TimeExceededCounters: CounterCollectionSpec<CounterCollection<Counter>: Inspectable>
28        + Default
29        + Debug
30        + TestOnlyPartialEq;
31
32    /// Counters for the ICMP Parameter Problem message type.
33    type ParameterProblemCounters: CounterCollectionSpec<CounterCollection<Counter>: Inspectable>
34        + Default
35        + Debug
36        + TestOnlyPartialEq;
37}
38
39impl IcmpCountersIpExt for Ipv4 {
40    type DestUnreachableCounters = Icmpv4DestUnreachableCounters;
41    type TimeExceededCounters = Icmpv4TimeExceededCounters;
42    type ParameterProblemCounters = Icmpv4ParameterProblemCounters;
43}
44
45impl IcmpCountersIpExt for Ipv6 {
46    type DestUnreachableCounters = Icmpv6DestUnreachableCounters;
47    type TimeExceededCounters = Icmpv6TimeExceededCounters;
48    type ParameterProblemCounters = Icmpv6ParameterProblemCounters;
49}
50
51/// ICMP tx path counters.
52#[derive(Default, Debug)]
53#[cfg_attr(
54    any(test, feature = "testutils"),
55    derive(PartialEq, netstack3_macros::CounterCollection)
56)]
57pub struct IcmpTxCounters<I: IcmpCountersIpExt, C: CounterRepr = Counter> {
58    /// Count of reply messages sent.
59    pub reply: C,
60    /// Count of time exceeded messages sent.
61    pub time_exceeded: <I::TimeExceededCounters as CounterCollectionSpec>::CounterCollection<C>,
62    /// Count of packet too big messages sent.
63    pub packet_too_big: C,
64    /// Count of parameter problem messages sent.
65    pub parameter_problem:
66        <I::ParameterProblemCounters as CounterCollectionSpec>::CounterCollection<C>,
67    /// Count of destination unreachable messages sent.
68    pub dest_unreachable:
69        <I::DestUnreachableCounters as CounterCollectionSpec>::CounterCollection<C>,
70    /// Count of error messages sent.
71    pub error: C,
72}
73
74impl<I: IcmpCountersIpExt> Inspectable for IcmpTxCounters<I> {
75    fn record<II: Inspector>(&self, inspector: &mut II) {
76        let IcmpTxCounters {
77            reply,
78            time_exceeded,
79            packet_too_big,
80            parameter_problem,
81            dest_unreachable,
82            error,
83        } = self;
84        inspector.record_counter("Reply", reply);
85        inspector.record_inspectable("TimeExceeded", time_exceeded);
86        inspector.record_counter("PacketTooBig", packet_too_big);
87        inspector.record_inspectable("ParameterProblem", parameter_problem);
88        inspector.record_inspectable("DestUnreachable", dest_unreachable);
89        inspector.record_counter("Error", error);
90    }
91}
92
93/// ICMP rx path counters.
94#[derive(Default, Debug)]
95#[cfg_attr(
96    any(test, feature = "testutils"),
97    derive(PartialEq, netstack3_macros::CounterCollection)
98)]
99pub struct IcmpRxCounters<I: IcmpCountersIpExt, C: CounterRepr = Counter> {
100    /// Count of error messages received.
101    pub error: C,
102    /// Count of error messages delivered to the transport layer.
103    pub error_delivered_to_transport_layer: C,
104    /// Count of error messages delivered to a socket.
105    pub error_delivered_to_socket: C,
106    /// Count of echo request messages received.
107    pub echo_request: C,
108    /// Count of echo reply messages received.
109    pub echo_reply: C,
110    /// Count of timestamp request messages received.
111    pub timestamp_request: C,
112    /// Count of destination unreachable messages received.
113    pub dest_unreachable:
114        <I::DestUnreachableCounters as CounterCollectionSpec>::CounterCollection<C>,
115    /// Count of time exceeded messages received.
116    pub time_exceeded: <I::TimeExceededCounters as CounterCollectionSpec>::CounterCollection<C>,
117    /// Count of parameter problem messages received.
118    pub parameter_problem:
119        <I::ParameterProblemCounters as CounterCollectionSpec>::CounterCollection<C>,
120    /// Count of packet too big messages received.
121    pub packet_too_big: C,
122    /// Count of ICMP Echo datagrams that could not be delivered to the socket
123    /// because its receive buffer was full.
124    pub queue_full: C,
125}
126
127impl<I: IcmpCountersIpExt> Inspectable for IcmpRxCounters<I> {
128    fn record<II: Inspector>(&self, inspector: &mut II) {
129        let IcmpRxCounters {
130            error,
131            error_delivered_to_transport_layer,
132            error_delivered_to_socket,
133            echo_request,
134            echo_reply,
135            timestamp_request,
136            dest_unreachable,
137            time_exceeded,
138            parameter_problem,
139            packet_too_big,
140            queue_full,
141        } = self;
142        inspector.record_counter("EchoRequest", echo_request);
143        inspector.record_counter("EchoReply", echo_reply);
144        inspector.record_counter("TimestampRequest", timestamp_request);
145        inspector.record_inspectable("DestUnreachable", dest_unreachable);
146        inspector.record_inspectable("TimeExceeded", time_exceeded);
147        inspector.record_inspectable("ParameterProblem", parameter_problem);
148        inspector.record_counter("PacketTooBig", packet_too_big);
149        inspector.record_counter("Error", error);
150        inspector
151            .record_counter("ErrorDeliveredToTransportLayer", error_delivered_to_transport_layer);
152        inspector.record_counter("ErrorDeliveredToSocket", error_delivered_to_socket);
153        inspector.record_counter("DroppedQueueFull", queue_full);
154    }
155}
156
157/// ICMPv4 counters for Dest Unreachable messages.
158///
159/// As defined by IANA:
160/// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3
161#[derive(Default, Debug, netstack3_macros::CounterCollection)]
162#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
163pub struct Icmpv4DestUnreachableCounters<C: CounterRepr = Counter> {
164    /// Network Unreachable, code 0
165    pub dest_network_unreachable: C,
166    /// Host Unreachable, code 1
167    pub dest_host_unreachable: C,
168    /// Protocol Unreachable, code 2
169    pub dest_protocol_unreachable: C,
170    /// Port Unreachable , code 3
171    pub dest_port_unreachable: C,
172    /// Fragmentation Needed and Don't Fragment was set, code 4
173    pub fragmentation_required: C,
174    /// Source Route Failed, code 5
175    pub source_route_failed: C,
176    /// Destination Network Unknown, code 6
177    pub dest_network_unknown: C,
178    /// Destination Host Unknown, code 7
179    pub dest_host_unknown: C,
180    /// Source Host Isolated, code 8
181    pub source_host_isolated: C,
182    /// Communication with Destination Network is Administratively Prohibited, code 9
183    pub network_administratively_prohibited: C,
184    /// Communication with Destination Host is Administratively Prohibited, code 10
185    pub host_administratively_prohibited: C,
186    /// Destination Network Unreachable for Type of Service, code 11
187    pub network_unreachable_for_tos: C,
188    /// Destination Host Unreachable for Type of Service, code 12
189    pub host_unreachable_for_tos: C,
190    /// Communicaation Administratively Prohibited, code 13
191    pub comm_administratively_prohibited: C,
192    /// Host Precedence Violation, code 14
193    pub host_precedence_violation: C,
194    /// Precedence Cutoff in Effect, code 15
195    pub precedence_cutoff_in_effect: C,
196}
197
198impl Icmpv4DestUnreachableCounters<Counter> {
199    pub(crate) fn increment_code(&self, code: Icmpv4DestUnreachableCode) {
200        use Icmpv4DestUnreachableCode::*;
201        let counter = match code {
202            DestNetworkUnreachable => &self.dest_network_unreachable,
203            DestHostUnreachable => &self.dest_host_unreachable,
204            DestProtocolUnreachable => &self.dest_protocol_unreachable,
205            DestPortUnreachable => &self.dest_port_unreachable,
206            FragmentationRequired => &self.fragmentation_required,
207            SourceRouteFailed => &self.source_route_failed,
208            DestNetworkUnknown => &self.dest_network_unknown,
209            DestHostUnknown => &self.dest_host_unknown,
210            SourceHostIsolated => &self.source_host_isolated,
211            NetworkAdministrativelyProhibited => &self.network_administratively_prohibited,
212            HostAdministrativelyProhibited => &self.host_administratively_prohibited,
213            NetworkUnreachableForToS => &self.network_unreachable_for_tos,
214            HostUnreachableForToS => &self.host_unreachable_for_tos,
215            CommAdministrativelyProhibited => &self.comm_administratively_prohibited,
216            HostPrecedenceViolation => &self.host_precedence_violation,
217            PrecedenceCutoffInEffect => &self.precedence_cutoff_in_effect,
218        };
219        counter.increment()
220    }
221}
222
223impl<C: CounterRepr> Inspectable for Icmpv4DestUnreachableCounters<C> {
224    fn record<I: Inspector>(&self, inspector: &mut I) {
225        let Icmpv4DestUnreachableCounters {
226            dest_network_unreachable,
227            dest_host_unreachable,
228            dest_protocol_unreachable,
229            dest_port_unreachable,
230            fragmentation_required,
231            source_route_failed,
232            dest_network_unknown,
233            dest_host_unknown,
234            source_host_isolated,
235            network_administratively_prohibited,
236            host_administratively_prohibited,
237            network_unreachable_for_tos,
238            host_unreachable_for_tos,
239            comm_administratively_prohibited,
240            host_precedence_violation,
241            precedence_cutoff_in_effect,
242        } = self;
243        inspector.record_counter("DestNetworkUnreachable", dest_network_unreachable);
244        inspector.record_counter("DestHostUnreachable", dest_host_unreachable);
245        inspector.record_counter("DestProtocolUnreachable", dest_protocol_unreachable);
246        inspector.record_counter("DestPortUnreachable", dest_port_unreachable);
247        inspector.record_counter("FragmentationRequired", fragmentation_required);
248        inspector.record_counter("SourceRouteFailed", source_route_failed);
249        inspector.record_counter("DestNetworkUnknown", dest_network_unknown);
250        inspector.record_counter("DestHostUnknown", dest_host_unknown);
251        inspector.record_counter("SourceHostIsolated", source_host_isolated);
252        inspector.record_counter(
253            "NetworkAdministrativelyProhibited",
254            network_administratively_prohibited,
255        );
256        inspector
257            .record_counter("HostAdministrativelyProhibited", host_administratively_prohibited);
258        inspector.record_counter("NetworkUnreachableForTos", network_unreachable_for_tos);
259        inspector.record_counter("HostUnreachableForTos", host_unreachable_for_tos);
260        inspector
261            .record_counter("CommAdministrativelyProhibited", comm_administratively_prohibited);
262        inspector.record_counter("HostPrecedenceViolation", host_precedence_violation);
263        inspector.record_counter("PrecedenceCutoffInEffect", precedence_cutoff_in_effect);
264    }
265}
266
267/// ICMPv6 counters for Dest Unreachable messages.
268///
269/// As defined by IANA:
270/// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-2
271#[derive(Default, Debug, netstack3_macros::CounterCollection)]
272#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
273pub struct Icmpv6DestUnreachableCounters<C: CounterRepr = Counter> {
274    /// No route to destination, code 0
275    pub no_route: C,
276    /// Communication with destination administratively prohibited, code 1
277    pub comm_administratively_prohibited: C,
278    /// Beyond scope of source address, code 2
279    pub beyond_scope: C,
280    /// Address unreachable, code 3
281    pub addr_unreachable: C,
282    /// Port unreachable, code 4
283    pub port_unreachable: C,
284    /// Source address failed ingress/egress policy, code 5
285    pub src_addr_failed_policy: C,
286    /// Reject route to destination, code 6
287    pub reject_route: C,
288}
289
290impl Icmpv6DestUnreachableCounters<Counter> {
291    pub(crate) fn increment_code(&self, code: Icmpv6DestUnreachableCode) {
292        use Icmpv6DestUnreachableCode::*;
293        let counter = match code {
294            NoRoute => &self.no_route,
295            CommAdministrativelyProhibited => &self.comm_administratively_prohibited,
296            BeyondScope => &self.beyond_scope,
297            AddrUnreachable => &self.addr_unreachable,
298            PortUnreachable => &self.port_unreachable,
299            SrcAddrFailedPolicy => &self.src_addr_failed_policy,
300            RejectRoute => &self.reject_route,
301        };
302        counter.increment();
303    }
304}
305
306impl<C: CounterRepr> Inspectable for Icmpv6DestUnreachableCounters<C> {
307    fn record<I: Inspector>(&self, inspector: &mut I) {
308        let Icmpv6DestUnreachableCounters {
309            no_route,
310            comm_administratively_prohibited,
311            beyond_scope,
312            addr_unreachable,
313            port_unreachable,
314            src_addr_failed_policy,
315            reject_route,
316        } = self;
317        inspector.record_counter("NoRoute", no_route);
318        inspector.record_counter(
319            "CommunicationAdministrativelyProhibited",
320            comm_administratively_prohibited,
321        );
322        inspector.record_counter("BeyondScope", beyond_scope);
323        inspector.record_counter("AddrUnreachable", addr_unreachable);
324        inspector.record_counter("PortUnreachable", port_unreachable);
325        inspector.record_counter("SrcAddrFailedPolicy", src_addr_failed_policy);
326        inspector.record_counter("RejectRoute", reject_route);
327    }
328}
329
330/// ICMPv4 counters for the Time Exceeded messages.
331///
332/// As defined by IANA:
333/// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-11
334#[derive(Default, Debug, netstack3_macros::CounterCollection)]
335#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
336pub struct Icmpv4TimeExceededCounters<C: CounterRepr = Counter> {
337    /// Time to Live exceeded in Transit, code 0
338    pub ttl_expired: C,
339    /// Fragment Reassembly Time Exceeded, code 1
340    pub fragment_reassembly_time_exceeded: C,
341}
342
343impl Icmpv4TimeExceededCounters<Counter> {
344    pub(crate) fn increment_code(&self, code: Icmpv4TimeExceededCode) {
345        use Icmpv4TimeExceededCode::*;
346        let counter = match code {
347            TtlExpired => &self.ttl_expired,
348            FragmentReassemblyTimeExceeded => &self.fragment_reassembly_time_exceeded,
349        };
350        counter.increment();
351    }
352}
353
354impl<C: CounterRepr> Inspectable for Icmpv4TimeExceededCounters<C> {
355    fn record<I: Inspector>(&self, inspector: &mut I) {
356        let Icmpv4TimeExceededCounters { ttl_expired, fragment_reassembly_time_exceeded } = self;
357        inspector.record_counter("TtlExpired", ttl_expired);
358        inspector
359            .record_counter("FragmentReassemblyTimeExceeded", fragment_reassembly_time_exceeded);
360    }
361}
362
363/// ICMPv6 counters for the Time Exceeded messages.
364///
365/// As defined by IANA:
366/// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-4
367#[derive(Default, Debug, netstack3_macros::CounterCollection)]
368#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
369pub struct Icmpv6TimeExceededCounters<C: CounterRepr = Counter> {
370    /// Hop limit exceeded in transit, code 0
371    pub hop_limit_exceeded: C,
372    /// Fragment Reassembly Time Exceeded, code 1
373    pub fragment_reassembly_time_exceeded: C,
374}
375
376impl Icmpv6TimeExceededCounters<Counter> {
377    pub(crate) fn increment_code(&self, code: Icmpv6TimeExceededCode) {
378        use Icmpv6TimeExceededCode::*;
379        let counter = match code {
380            HopLimitExceeded => &self.hop_limit_exceeded,
381            FragmentReassemblyTimeExceeded => &self.fragment_reassembly_time_exceeded,
382        };
383        counter.increment();
384    }
385}
386
387impl<C: CounterRepr> Inspectable for Icmpv6TimeExceededCounters<C> {
388    fn record<I: Inspector>(&self, inspector: &mut I) {
389        let Icmpv6TimeExceededCounters { hop_limit_exceeded, fragment_reassembly_time_exceeded } =
390            self;
391        inspector.record_counter("HopLimitExceeded", hop_limit_exceeded);
392        inspector
393            .record_counter("FragmentReassemblyTimeExceeded", fragment_reassembly_time_exceeded);
394    }
395}
396
397/// ICMPv4 Counters for the Parameter Problem messages.
398///
399/// As defined by IANA:
400/// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-12
401#[derive(Default, Debug, netstack3_macros::CounterCollection)]
402#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
403pub struct Icmpv4ParameterProblemCounters<C: CounterRepr = Counter> {
404    /// Pointer indicates the error, code 0
405    pub pointer_indicates_error: C,
406    /// Missing a Required Option, code 1
407    pub missing_required_option: C,
408    /// Bad Length, code 2
409    pub bad_length: C,
410}
411
412impl Icmpv4ParameterProblemCounters<Counter> {
413    pub(crate) fn increment_code(&self, code: Icmpv4ParameterProblemCode) {
414        use Icmpv4ParameterProblemCode::*;
415        let counter = match code {
416            PointerIndicatesError => &self.pointer_indicates_error,
417            MissingRequiredOption => &self.missing_required_option,
418            BadLength => &self.bad_length,
419        };
420        counter.increment()
421    }
422}
423
424impl<C: CounterRepr> Inspectable for Icmpv4ParameterProblemCounters<C> {
425    fn record<I: Inspector>(&self, inspector: &mut I) {
426        let Icmpv4ParameterProblemCounters {
427            pointer_indicates_error,
428            missing_required_option,
429            bad_length,
430        } = self;
431        inspector.record_counter("PointerIndicatesError", pointer_indicates_error);
432        inspector.record_counter("MissingRequiredOption", missing_required_option);
433        inspector.record_counter("BadLength", bad_length);
434    }
435}
436
437/// ICMPv6 Counters for the Parameter Problem messages.
438///
439/// As defined by IANA:
440/// https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-5
441#[derive(Default, Debug, netstack3_macros::CounterCollection)]
442#[cfg_attr(any(test, feature = "testutils"), derive(PartialEq,))]
443pub struct Icmpv6ParameterProblemCounters<C: CounterRepr = Counter> {
444    /// Erroneous header field encountered, code 0
445    pub erroneous_header_field: C,
446    /// Unrecognized Next Header type encountered, code 1
447    pub unrecognized_next_header_type: C,
448    /// Unrecognized IPv6 option encountered, code 2
449    pub unrecognized_ipv6_option: C,
450}
451
452impl Icmpv6ParameterProblemCounters<Counter> {
453    pub(crate) fn increment_code(&self, code: Icmpv6ParameterProblemCode) {
454        use Icmpv6ParameterProblemCode::*;
455        let counter = match code {
456            ErroneousHeaderField => &self.erroneous_header_field,
457            UnrecognizedNextHeaderType => &self.unrecognized_next_header_type,
458            UnrecognizedIpv6Option => &self.unrecognized_ipv6_option,
459        };
460        counter.increment()
461    }
462}
463
464impl<C: CounterRepr> Inspectable for Icmpv6ParameterProblemCounters<C> {
465    fn record<I: Inspector>(&self, inspector: &mut I) {
466        let Icmpv6ParameterProblemCounters {
467            erroneous_header_field,
468            unrecognized_next_header_type,
469            unrecognized_ipv6_option,
470        } = self;
471        inspector.record_counter("ErroneousHeaderField", erroneous_header_field);
472        inspector.record_counter("UnrecognizedNextHeaderType", unrecognized_next_header_type);
473        inspector.record_counter("UnrecognizedIpv6Option", unrecognized_ipv6_option);
474    }
475}