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