Skip to main content

dhcp_client_core/
inspect.rs

1// Copyright 2025 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
5use std::sync::atomic::{AtomicUsize, Ordering};
6
7use diagnostics_traits::{InspectableValue, Inspector};
8
9use crate::parse::{
10    IncomingResponseToRequestErrorCounters, SelectingIncomingMessageErrorCounters, SoftParseErrors,
11};
12
13/// An incrementing counter.
14#[derive(Default, Debug)]
15pub(crate) struct Counter(AtomicUsize);
16
17impl Counter {
18    pub(crate) fn increment(&self) {
19        let _: usize = self.0.fetch_add(1, Ordering::Relaxed);
20    }
21
22    pub(crate) fn load(&self) -> usize {
23        self.0.load(Ordering::Relaxed)
24    }
25}
26
27impl InspectableValue for Counter {
28    fn record<I: diagnostics_traits::Inspector>(&self, name: &str, inspector: &mut I) {
29        inspector.record_uint(name, u64::try_from(self.load()).unwrap_or(u64::MAX));
30    }
31}
32
33/// Counters relating to sending and receiving messages.
34#[derive(Debug, Default)]
35pub(crate) struct MessagingRelatedCounters {
36    /// A counter for each time the client sent a message.
37    pub(crate) send_message: Counter,
38    /// A counter for each time the client received a message.
39    pub(crate) recv_message: Counter,
40    /// A counter for each time the client observed a fatal socket error while
41    /// trying to receive a message.
42    pub(crate) recv_message_fatal_socket_error: Counter,
43    /// A counter for each time the client observed a non-fatal socket error
44    /// while trying to receive a message.
45    pub(crate) recv_message_non_fatal_socket_error: Counter,
46    /// A counter for each time the client has timed out waiting to receive
47    /// (causing it to retransmit or state-transition).
48    pub(crate) recv_time_out: Counter,
49    /// A counter for each time the client received a message with the wrong
50    /// transaction ID.
51    pub(crate) recv_wrong_xid: Counter,
52    /// A counter for each time the client received a message specifying an
53    /// incorrect chaddr (client hardware address).
54    pub(crate) recv_wrong_chaddr: Counter,
55    /// A counter for each time the client received a UDPv4 packet on the
56    /// dhcp-client port that did not parse as a DHCP message.
57    pub(crate) recv_failed_dhcp_parse: Counter,
58    /// A counter for each time the client received a DHCPACK that omitted the
59    /// IP Address Lease Time option.
60    pub(crate) recv_ack_no_addr_lease_time: Counter,
61    /// A counter for each time the client received a DHCPACK where T1 >= T2.
62    pub(crate) recv_ack_renewal_time_after_rebinding_time: Counter,
63    /// A counter for each time the client received a DHCPACK where T2 >= lease_time.
64    pub(crate) recv_ack_rebinding_time_outlives_lease: Counter,
65    /// A counter for each time the client received a DHCP message that
66    /// contained an illegal option.
67    pub(crate) recv_illegal_option: Counter,
68}
69
70impl MessagingRelatedCounters {
71    fn record(&self, inspector: &mut impl Inspector) {
72        let Self {
73            send_message,
74            recv_message,
75            recv_message_fatal_socket_error,
76            recv_message_non_fatal_socket_error,
77            recv_time_out,
78            recv_wrong_xid,
79            recv_wrong_chaddr,
80            recv_failed_dhcp_parse,
81            recv_ack_no_addr_lease_time,
82            recv_ack_renewal_time_after_rebinding_time,
83            recv_ack_rebinding_time_outlives_lease,
84            recv_illegal_option,
85        } = self;
86        inspector.record_inspectable_value("SendMessage", send_message);
87        inspector.record_inspectable_value("RecvMessage", recv_message);
88        inspector.record_inspectable_value(
89            "RecvMessageFatalSocketError",
90            recv_message_fatal_socket_error,
91        );
92        inspector.record_inspectable_value(
93            "RecvMessageNonFatalSocketError",
94            recv_message_non_fatal_socket_error,
95        );
96        inspector.record_inspectable_value("RecvTimeOut", recv_time_out);
97        inspector.record_inspectable_value("RecvWrongXid", recv_wrong_xid);
98        inspector.record_inspectable_value("RecvWrongChaddr", recv_wrong_chaddr);
99        inspector.record_inspectable_value("RecvFailedDhcpParse", recv_failed_dhcp_parse);
100        inspector.record_inspectable_value("NoLeaseTime", recv_ack_no_addr_lease_time);
101        inspector.record_inspectable_value(
102            "RenewalTimeAfterRebindingTime",
103            recv_ack_renewal_time_after_rebinding_time,
104        );
105        inspector.record_inspectable_value(
106            "RebindingTimeOutlivesLease",
107            recv_ack_rebinding_time_outlives_lease,
108        );
109        inspector.record_inspectable_value("IllegallyIncludedOption", recv_illegal_option);
110    }
111
112    pub(crate) fn increment_soft_errors(&self, soft_errors: SoftParseErrors) {
113        let SoftParseErrors { illegal_option } = soft_errors;
114        if illegal_option {
115            self.recv_illegal_option.increment()
116        }
117    }
118}
119
120/// Counters for the Init state.
121#[derive(Debug, Default)]
122pub(crate) struct InitCounters {
123    /// The number of times the state was entered.
124    pub(crate) entered: Counter,
125}
126
127impl InitCounters {
128    fn record(&self, inspector: &mut impl Inspector) {
129        let Self { entered } = self;
130        inspector.record_inspectable_value("Entered", entered);
131    }
132}
133
134/// Counters for the Selecting state.
135#[derive(Debug, Default)]
136pub(crate) struct SelectingCounters {
137    /// The number of times the state was entered.
138    pub(crate) entered: Counter,
139    /// Counters relating to sending and receiving messages.
140    pub(crate) messaging: MessagingRelatedCounters,
141    /// Counters for each error that could cause the client to reject a message
142    /// while receiving in the Selecting state.
143    pub(crate) recv_error: SelectingIncomingMessageErrorCounters,
144}
145
146impl SelectingCounters {
147    fn record(&self, inspector: &mut impl Inspector) {
148        let Self { entered, messaging, recv_error } = self;
149        inspector.record_inspectable_value("Entered", entered);
150        messaging.record(inspector);
151        recv_error.record(inspector);
152    }
153}
154
155/// Counters for the Requesting state.
156#[derive(Debug, Default)]
157pub(crate) struct RequestingCounters {
158    /// The number of times the state was entered.
159    pub(crate) entered: Counter,
160    /// Counters relating to sending and receiving messages.
161    pub(crate) messaging: MessagingRelatedCounters,
162    /// Counters for each error that could cause a client to reject a message
163    /// while receiving in the Requesting state.
164    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
165    /// Counter for each time the client received a NAK message while in the
166    /// Requesting state.
167    pub(crate) recv_nak: Counter,
168}
169
170impl RequestingCounters {
171    fn record(&self, inspector: &mut impl Inspector) {
172        let Self { entered, messaging, recv_error, recv_nak } = self;
173        inspector.record_inspectable_value("Entered", entered);
174        messaging.record(inspector);
175        recv_error.record(inspector);
176        inspector.record_inspectable_value("RecvNak", recv_nak);
177    }
178}
179
180/// Counters for the Bound state.
181#[derive(Debug, Default)]
182pub(crate) struct BoundCounters {
183    /// The number of times the state was entered.
184    pub(crate) entered: Counter,
185    /// The number of times the address transitions to `Assigned`.
186    pub(crate) assigned: Counter,
187}
188
189impl BoundCounters {
190    fn record(&self, inspector: &mut impl Inspector) {
191        let Self { entered, assigned } = self;
192        inspector.record_inspectable_value("Entered", entered);
193        inspector.record_inspectable_value("Assigned", assigned);
194    }
195}
196
197/// Counters for the Renewing state.
198#[derive(Debug, Default)]
199pub(crate) struct RenewingCounters {
200    /// The number of times the state was entered.
201    pub(crate) entered: Counter,
202    /// Counters relating to sending and receiving messages.
203    pub(crate) messaging: MessagingRelatedCounters,
204    /// Counters for each error that could cause a client to reject a message
205    /// while receiving in the Renewing state.
206    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
207    /// Counter for each time the client received a NAK message while in the
208    /// Renewing state.
209    pub(crate) recv_nak: Counter,
210}
211
212impl RenewingCounters {
213    fn record(&self, inspector: &mut impl Inspector) {
214        let Self { entered, messaging, recv_error, recv_nak } = self;
215        inspector.record_inspectable_value("Entered", entered);
216        messaging.record(inspector);
217        recv_error.record(inspector);
218        inspector.record_inspectable_value("RecvNak", recv_nak);
219    }
220}
221
222/// Counters for the Rebinding state.
223#[derive(Debug, Default)]
224pub(crate) struct RebindingCounters {
225    /// The number of times the state was entered.
226    pub(crate) entered: Counter,
227    /// Counters relating to sending and receiving messages.
228    pub(crate) messaging: MessagingRelatedCounters,
229    /// Counters for each error that could cause a client to reject a message
230    /// while receiving in the Rebinding state.
231    pub(crate) recv_error: IncomingResponseToRequestErrorCounters,
232    /// Counter for each time the client received a NAK message while in the
233    /// Rebinding state.
234    pub(crate) recv_nak: Counter,
235}
236
237impl RebindingCounters {
238    fn record(&self, inspector: &mut impl Inspector) {
239        let Self { entered, messaging, recv_error, recv_nak } = self;
240        inspector.record_inspectable_value("Entered", entered);
241        messaging.record(inspector);
242        recv_error.record(inspector);
243        inspector.record_inspectable_value("RecvNak", recv_nak);
244    }
245}
246
247/// Counters for the WaitingToRestart state.
248#[derive(Debug, Default)]
249pub(crate) struct WaitingToRestartCounters {
250    /// The number of times the state was entered.
251    pub(crate) entered: Counter,
252}
253
254impl WaitingToRestartCounters {
255    fn record(&self, inspector: &mut impl Inspector) {
256        let Self { entered } = self;
257        inspector.record_inspectable_value("Entered", entered);
258    }
259}
260
261/// Debugging counters for the core state machine.
262#[derive(Default, Debug)]
263pub struct Counters {
264    pub(crate) init: InitCounters,
265    pub(crate) selecting: SelectingCounters,
266    pub(crate) requesting: RequestingCounters,
267    pub(crate) bound: BoundCounters,
268    pub(crate) renewing: RenewingCounters,
269    pub(crate) rebinding: RebindingCounters,
270    pub(crate) waiting_to_restart: WaitingToRestartCounters,
271}
272
273impl Counters {
274    /// Records the counters in the given [`Inspector`].
275    pub fn record(&self, inspector: &mut impl Inspector) {
276        let Self { init, selecting, requesting, bound, renewing, rebinding, waiting_to_restart } =
277            self;
278        inspector.record_child("Init", |inspector| {
279            init.record(inspector);
280        });
281        inspector.record_child("Selecting", |inspector| {
282            selecting.record(inspector);
283        });
284        inspector.record_child("Requesting", |inspector| {
285            requesting.record(inspector);
286        });
287        inspector.record_child("Bound", |inspector| {
288            bound.record(inspector);
289        });
290        inspector.record_child("Renewing", |inspector| {
291            renewing.record(inspector);
292        });
293        inspector.record_child("Rebinding", |inspector| {
294            rebinding.record(inspector);
295        });
296        inspector.record_child("WaitingToRestart", |inspector| {
297            waiting_to_restart.record(inspector);
298        });
299    }
300}