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