chrony_candm/
reply.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Portions derived from Chrony copyright Richard P. Curnow 1997-2003
3// and Miroslav Lichvar 2009, 2012-2020
4// SPDX-License-Identifier: GPL-2.0-only
5
6//! Data structures representing replies
7
8use bitflags::bitflags;
9use bytes::{Buf, BufMut};
10use chrony_candm_derive::{ChronyMessage, ChronySerialize};
11use num_enum::{IntoPrimitive, TryFromPrimitive};
12use std::time::{SystemTime, UNIX_EPOCH};
13
14use crate::common::*;
15
16#[repr(u16)]
17#[derive(
18    Debug,
19    Copy,
20    Clone,
21    PartialEq,
22    Eq,
23    Ord,
24    PartialOrd,
25    Hash,
26    IntoPrimitive,
27    TryFromPrimitive,
28    ChronySerialize,
29)]
30pub enum Status {
31    Success,
32    Failed,
33    Unauth,
34    Invalid,
35    NoSuchSource,
36    InvalidTs,
37    NotEnabled,
38    BadSubnet,
39    AccessAllowed,
40    AccessDenied,
41    NoHostAccess,
42    SourceAlreadyKnown,
43    TooManySources,
44    NoRtc,
45    BadRtcFile,
46    Inactive,
47    BadSample,
48    InvalidAf,
49    BadPktVersion,
50    BadPktLength,
51    InvalidName,
52}
53#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
54pub struct NSources {
55    pub n_sources: u32,
56}
57
58#[repr(u16)]
59#[derive(
60    Debug,
61    Copy,
62    Clone,
63    PartialEq,
64    Eq,
65    Ord,
66    PartialOrd,
67    Hash,
68    IntoPrimitive,
69    TryFromPrimitive,
70    ChronySerialize,
71)]
72pub enum SourceMode {
73    Client,
74    Peer,
75    Ref,
76}
77
78#[repr(u16)]
79#[derive(
80    Debug,
81    Copy,
82    Clone,
83    PartialEq,
84    Eq,
85    Ord,
86    PartialOrd,
87    Hash,
88    IntoPrimitive,
89    TryFromPrimitive,
90    ChronySerialize,
91)]
92pub enum SourceState {
93    Selected,
94    NonSelectable,
95    Falseticker,
96    Jittery,
97    Unselected,
98    Selectable,
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
102pub struct SourceData {
103    pub ip_addr: ChronyAddr,
104    pub poll: i16,
105    pub stratum: u16,
106    pub state: SourceState,
107    pub mode: SourceMode,
108    pub flags: SourceFlags,
109    pub reachability: u16,
110    pub since_sample: u32,
111    pub orig_latest_meas: ChronyFloat,
112    pub latest_meas: ChronyFloat,
113    pub latest_meas_err: ChronyFloat,
114}
115
116#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
117pub struct Tracking {
118    pub ref_id: u32,
119    pub ip_addr: ChronyAddr,
120    pub stratum: u16,
121    pub leap_status: u16,
122    pub ref_time: SystemTime,
123    pub current_correction: ChronyFloat,
124    pub last_offset: ChronyFloat,
125    pub rms_offset: ChronyFloat,
126    pub freq_ppm: ChronyFloat,
127    pub resid_freq_ppm: ChronyFloat,
128    pub skew_ppm: ChronyFloat,
129    pub root_delay: ChronyFloat,
130    pub root_dispersion: ChronyFloat,
131    pub last_update_interval: ChronyFloat,
132}
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
135pub struct SourceStats {
136    pub ref_id: u32,
137    pub ip_addr: ChronyAddr,
138    pub n_samples: u32,
139    pub n_runs: u32,
140    pub span_seconds: u32,
141    pub sd: ChronyFloat,
142    pub resid_freq_ppm: ChronyFloat,
143    pub skew_ppm: ChronyFloat,
144    pub est_offset: ChronyFloat,
145    pub est_offset_err: ChronyFloat,
146}
147
148#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
149pub struct Rtc {
150    pub ref_time: SystemTime,
151    pub n_samples: u16,
152    pub n_runs: u16,
153    pub span_seconds: u32,
154    pub rtc_seconds_fast: ChronyFloat,
155    pub rtc_gain_rate_ppm: ChronyFloat,
156}
157
158#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
159pub struct ManualTimestamp {
160    pub offset: ChronyFloat,
161    pub dfreq_ppm: ChronyFloat,
162    pub new_afreq_ppm: ChronyFloat,
163}
164
165#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize, Default)]
166pub struct ClientAccessesClient {
167    pub ip: ChronyAddr,
168    pub ntp_hits: u32,
169    pub nke_hits: u32,
170    pub cmd_hits: u32,
171    pub ntp_drops: u32,
172    pub nke_drops: u32,
173    pub cmd_drops: u32,
174    pub ntp_interval: u8,
175    pub nke_interval: u8,
176    pub cmd_interval: u8,
177    pub ntp_timeout_interval: u8,
178    pub last_ntp_hit_ago: u32,
179    pub last_nke_hit_ago: u32,
180    pub last_cmd_hit_ago: u32,
181}
182
183#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
184pub struct ClientAccessesByIndex {
185    pub n_indices: u32,
186    pub next_index: u32,
187    pub n_clients: u32,
188    pub clients: [ClientAccessesClient; 8],
189}
190
191#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
192pub struct ServerStats {
193    pub ntp_hits: u32,
194    pub nke_hits: u32,
195    pub cmd_hits: u32,
196    pub ntp_drops: u32,
197    pub nke_drops: u32,
198    pub cmd_drops: u32,
199    pub log_drops: u32,
200    pub ntp_auth_hits: u32,
201}
202
203#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
204pub struct ServerStats4 {
205    pub ntp_hits: u64,
206    pub nke_hits: u64,
207    pub cmd_hits: u64,
208    pub ntp_drops: u64,
209    pub nke_drops: u64,
210    pub cmd_drops: u64,
211    pub log_drops: u64,
212    pub ntp_auth_hits: u64,
213    pub ntp_interleaved_hits: u64,
214    pub ntp_timestamps: u64,
215    pub ntp_span_seconds: u64,
216    pub ntp_daemon_rx_timestamps: u64,
217    pub ntp_daemon_tx_timestamps: u64,
218    pub ntp_kernel_rx_timestamps: u64,
219    pub ntp_kernel_tx_timestamps: u64,
220    pub ntp_hw_rx_timestamps: u64,
221    pub ntp_hw_tx_timestamps: u64,
222    reserved: [u64; 4],
223}
224
225#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
226pub struct ManualListSample {
227    pub when: SystemTime,
228    pub slewed_offset: ChronyFloat,
229    pub orig_offset: ChronyFloat,
230    pub residual: ChronyFloat,
231}
232
233impl Default for ManualListSample {
234    fn default() -> Self {
235        Self {
236            when: UNIX_EPOCH,
237            slewed_offset: Default::default(),
238            orig_offset: Default::default(),
239            residual: Default::default(),
240        }
241    }
242}
243
244#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
245pub struct ManualList {
246    pub n_samples: u32,
247    pub samples: [ManualListSample; 16],
248}
249
250#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
251pub struct Activity {
252    pub online: u32,
253    pub offline: u32,
254    pub burst_online: u32,
255    pub burst_offline: u32,
256    pub unresolved: u32,
257}
258
259bitflags! {
260    #[derive(ChronySerialize)]
261    pub struct SmoothingFlags : u32 {
262        const ACTIVE = 0x1;
263        const LEAPONLY = 0x2;
264    }
265}
266
267#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
268pub struct Smoothing {
269    pub flags: SmoothingFlags,
270    pub offset: ChronyFloat,
271    pub freq_ppm: ChronyFloat,
272    pub wander_ppm: ChronyFloat,
273    pub last_update_ago: ChronyFloat,
274    pub remaining_time: ChronyFloat,
275}
276
277bitflags! {
278    #[derive(ChronySerialize)]
279    pub struct NtpFlags : u16 {
280        const TESTS = 0x3ff;
281        const INTERLEAVED = 0x4000;
282        const AUTHENTICATED = 0x8000;
283    }
284}
285
286#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
287pub struct NtpData {
288    pub remote_addr: ChronyAddr,
289    pub local_addr: ChronyAddr,
290    pub remote_port: u16,
291    pub leap: u8,
292    pub version: u8,
293    pub mode: u8,
294    pub stratum: u8,
295    pub poll: i8,
296    pub precision: i8,
297    pub root_delay: ChronyFloat,
298    pub root_dispersion: ChronyFloat,
299    pub ref_id: u32,
300    pub ref_time: SystemTime,
301    pub offset: ChronyFloat,
302    pub peer_delay: ChronyFloat,
303    pub peer_dispersion: ChronyFloat,
304    pub response_time: ChronyFloat,
305    pub jitter_asymmetry: ChronyFloat,
306    pub flags: NtpFlags,
307    pub tx_tss_char: u8,
308    pub rx_tss_char: u8,
309    pub total_tx_count: u32,
310    pub total_rx_count: u32,
311    #[pad = 16]
312    pub total_valid_count: u32,
313}
314
315#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
316pub struct NtpSourceName {
317    pub name: [u8; 256],
318}
319
320#[repr(u16)]
321#[derive(
322    Debug,
323    Copy,
324    Clone,
325    PartialEq,
326    Eq,
327    Ord,
328    PartialOrd,
329    Hash,
330    IntoPrimitive,
331    TryFromPrimitive,
332    ChronySerialize,
333)]
334pub enum AuthDataMode {
335    None,
336    Symmetric,
337    Nts,
338}
339
340#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
341pub struct AuthData {
342    pub mode: AuthDataMode,
343    pub key_type: u16,
344    pub key_id: u32,
345    pub key_length: u16,
346    pub ke_attempts: u16,
347    pub last_ke_ago: u32,
348    pub cookies: u16,
349    pub cookies_length: u16,
350    #[pad = 2]
351    pub nak: u16,
352}
353
354bitflags! {
355    #[derive(ChronySerialize)]
356    pub struct SelectDataOptions : u16 {
357        const NOSELECT = 0x1;
358        const PREFER = 0x2;
359        const TRUST = 0x4;
360        const REQUIRE = 0x8;
361    }
362}
363
364#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
365pub struct SelectData {
366    pub ref_id: u32,
367    pub ip_addr: ChronyAddr,
368    pub state_char: u8,
369    pub authentication: u8,
370    pub leap: u8,
371    pub pad: u8,
372    pub conf_options: SelectDataOptions,
373    pub eff_options: SelectDataOptions,
374    pub last_sample_ago: u32,
375    pub score: ChronyFloat,
376    pub lo_limit: ChronyFloat,
377    pub hi_limit: ChronyFloat,
378}
379
380#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronyMessage)]
381pub enum ReplyBody {
382    #[cmd = 1]
383    Null,
384    NSources(NSources),
385    SourceData(SourceData),
386    ManualTimestamp,
387    Tracking(Tracking),
388    SourceStats(SourceStats),
389    Rtc(Rtc),
390    #[cmd = 12]
391    Activity(Activity),
392    Smoothing(Smoothing),
393    #[cmd = 16]
394    NtpData(NtpData),
395    ManualTimestamp2(ManualTimestamp),
396    ManualList2(ManualList),
397    NtpSourceName(NtpSourceName),
398    AuthData(AuthData),
399    ClientAccessesByIndex3(ClientAccessesByIndex),
400    ServerStats2(ServerStats),
401    SelectData(SelectData),
402    #[cmd = 25]
403    ServerStats4(ServerStats4),
404}
405
406/// A reply from Chrony
407#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
408pub struct Reply {
409    /// Status code associated with the reply
410    pub status: Status,
411    /// Command number associated with the request this is replying to
412    pub cmd: u16,
413    /// Sequence number associated with the request this is replying to
414    pub sequence: u32,
415    /// Body of the reply
416    pub body: ReplyBody,
417}
418
419impl Reply {
420    /// Returns the length in bytes of this reply when serialized.
421    pub fn length(&self) -> usize {
422        REPLY_HEADER_LENGTH + self.body.body_length()
423    }
424
425    /// Serializes this reply into `buf`. May panic if `buf` does not
426    /// have a capacity of at least `self.length()` and its implementation
427    /// does not support automatic resizing.
428    pub fn serialize<B: BufMut>(&self, buf: &mut B) {
429        let reply = self.body.cmd();
430
431        buf.put_u8(6); //Version
432        buf.put_u8(2); //Packet type = reply
433        buf.put_u8(0); //Reserved
434        buf.put_u8(0); //Reserved
435        buf.put_u16(self.cmd); //Requested command, set to 0 since it's just ignored
436        buf.put_u16(reply);
437        self.status.serialize(buf);
438        buf.put_u16(0); //Padding
439        buf.put_u16(0); //Padding
440        buf.put_u16(0); //Padding
441        buf.put_u32(self.sequence);
442        buf.put_u32(0); //Padding
443        buf.put_u32(0); //Padding
444        self.body.serialize_body(buf);
445    }
446
447    /// Deserializes a reply from `buf`.
448    pub fn deserialize<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
449        if buf.remaining() < REPLY_HEADER_LENGTH {
450            return Err(DeserializationError::new("message too short"));
451        }
452        if buf.get_u8() != 6 {
453            //Version
454            return Err(DeserializationError::new("unsupported version"));
455        }
456
457        if buf.get_u8() != 2 {
458            //Packet type
459            return Err(DeserializationError::new("wrong packet type"));
460        }
461
462        buf.get_u8(); //Reserved
463        buf.get_u8(); //Reserved
464        let cmd = buf.get_u16();
465        let reply = buf.get_u16();
466        let status = Status::deserialize_unchecked(buf)?;
467        buf.get_u16(); //Padding
468        buf.get_u16(); //Padding
469        buf.get_u16(); //Padding
470        let sequence = buf.get_u32();
471        buf.get_u32(); //Padding
472        buf.get_u32(); //Padding
473        let body = ReplyBody::deserialize_body(reply, buf)?;
474        Ok(Self {
475            status,
476            cmd,
477            sequence,
478            body,
479        })
480    }
481}