chrony_candm/
request.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 requests
7
8use bytes::{Buf, BufMut};
9use chrony_candm_derive::{ChronyMessage, ChronySerialize};
10use num_enum::{IntoPrimitive, TryFromPrimitive};
11use std::{convert::TryInto, time::SystemTime};
12
13use crate::common::*;
14use crate::reply;
15
16#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
17pub struct Online {
18    pub mask: ChronyAddr,
19    pub address: ChronyAddr,
20}
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
23pub struct Offline {
24    pub mask: ChronyAddr,
25    pub address: ChronyAddr,
26}
27
28#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
29pub struct Burst {
30    pub mask: ChronyAddr,
31    pub address: ChronyAddr,
32    pub n_good_samples: i32,
33    pub n_total_samples: i32,
34}
35#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
36pub struct ModifyMinPoll {
37    pub address: ChronyAddr,
38    pub new_minpoll: i32,
39}
40#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
41pub struct ModifyMaxPoll {
42    pub address: ChronyAddr,
43    pub new_maxpoll: i32,
44}
45
46#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
47pub struct ModifyMaxDelay {
48    pub address: ChronyAddr,
49    pub new_max_delay: ChronyFloat,
50}
51
52#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
53pub struct ModifyMaxDelayRatio {
54    pub address: ChronyAddr,
55    pub new_max_delay_ratio: ChronyFloat,
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
59pub struct ModifyMaxDelayDevRatio {
60    pub address: ChronyAddr,
61    pub new_max_delay_dev_ratio: ChronyFloat,
62}
63
64#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
65pub struct ModifyMinStratum {
66    pub address: ChronyAddr,
67    pub new_min_stratum: i32,
68}
69
70#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
71pub struct ModifyPollTarget {
72    pub address: ChronyAddr,
73    pub new_poll_target: i32,
74}
75
76#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
77pub struct ModifyMaxUpdateSkew {
78    pub new_max_update_skew: ChronyFloat,
79}
80
81#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
82pub struct ModifyMakeStep {
83    pub limit: i32,
84    pub threshold: ChronyFloat,
85}
86
87#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
88pub struct Logon {
89    pub ts: SystemTime,
90}
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
93pub struct SetTime {
94    pub ts: SystemTime,
95}
96
97#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
98pub struct Local {
99    pub on_off: i32,
100    pub stratum: i32,
101    pub distance: ChronyFloat,
102    pub orphan: i32,
103}
104
105#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
106pub struct Manual {
107    pub option: i32,
108}
109
110#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
111pub struct SourceData {
112    pub index: i32,
113}
114
115#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
116pub struct AllowDeny {
117    pub ip: ChronyAddr,
118    pub subnet_bits: i32,
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
122pub struct AcCheck {
123    pub ip: ChronyAddr,
124}
125
126#[repr(u32)]
127#[derive(
128    Debug,
129    Copy,
130    Clone,
131    PartialEq,
132    Eq,
133    Ord,
134    PartialOrd,
135    Hash,
136    IntoPrimitive,
137    TryFromPrimitive,
138    ChronySerialize,
139)]
140pub enum AddSrcType {
141    Server = 1,
142    Peer = 2,
143    Pool = 3,
144}
145
146#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
147pub struct NtpSource {
148    pub src_type: AddSrcType,
149    pub name: [u8; 256],
150    pub port: u32,
151    pub minpoll: i32,
152    pub maxpoll: i32,
153    pub presend_minpoll: i32,
154    pub min_stratum: i32,
155    pub poll_target: u32,
156    pub version: u32,
157    pub max_sources: u32,
158    pub min_samples: u32,
159    pub max_samples: u32,
160    pub authkey: u32,
161    pub nts_port: u32,
162    pub max_delay: ChronyFloat,
163    pub max_delay_ratio: ChronyFloat,
164    pub max_delay_dev_ratio: ChronyFloat,
165    pub min_delay: ChronyFloat,
166    pub asymmetry: ChronyFloat,
167    // In the chrony sources, `flags` is 32 bits in requests and 16 bits in replies.
168    // So that we can use the same data type in both messages, we make it 16 bits
169    // everywhere and treat the two unused high bytes in the request as padding.
170    #[pad = 2]
171    pub offset: ChronyFloat,
172    pub flags: SourceFlags,
173    pub filter_length: i32,
174    #[pad = 8]
175    pub cert_set: i32,
176}
177
178#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
179pub struct DelSource {
180    pub ip_addr: ChronyAddr,
181}
182
183#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
184pub struct DFreq {
185    pub dfreq: ChronyFloat,
186}
187
188#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
189pub struct DOffset {
190    pub doffset: ChronyFloat,
191}
192
193#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
194pub struct SourceStats {
195    pub index: u32,
196}
197
198#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
199pub struct ClientAccessesByIndex {
200    pub first_index: u32,
201    pub n_clients: u32,
202    pub min_hits: u32,
203    pub reset: u32,
204}
205
206#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
207pub struct ManualDelete {
208    pub index: u32,
209}
210
211#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
212pub struct ReselectDistance {
213    pub distance: ChronyFloat,
214}
215
216#[repr(i32)]
217#[derive(
218    Debug,
219    Copy,
220    Clone,
221    PartialEq,
222    Eq,
223    Ord,
224    PartialOrd,
225    Hash,
226    IntoPrimitive,
227    TryFromPrimitive,
228    ChronySerialize,
229)]
230pub enum SmoothTimeOption {
231    Reset,
232    Activate,
233}
234
235#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
236pub struct SmoothTime {
237    pub option: SmoothTimeOption,
238}
239
240#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
241pub struct NtpData {
242    pub ip_addr: ChronyAddr,
243}
244
245#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
246pub struct NtpSourceName {
247    pub ip_addr: ChronyAddr,
248}
249
250#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
251pub struct AuthData {
252    pub ip_addr: ChronyAddr,
253}
254
255#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronySerialize)]
256pub struct SelectData {
257    pub index: u32,
258}
259
260#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, ChronyMessage)]
261pub enum RequestBody {
262    Null,
263    Online(Online),
264    Offline(Offline),
265    Burst(Burst),
266    ModifyMinPoll(ModifyMinPoll),
267    ModifyMaxPoll(ModifyMaxPoll),
268    #[pad = 4]
269    Dump,
270    ModifyMaxDelay(ModifyMaxDelay),
271    ModifyMaxDelayRatio(ModifyMaxDelayRatio),
272    ModifyMaxUpdateSkew(ModifyMaxUpdateSkew),
273    Logon(Logon),
274    SetTime(SetTime),
275    #[cmd = 13]
276    Manual(Manual),
277    NSources,
278    SourceData(SourceData),
279    Rekey,
280    Allow(AllowDeny),
281    AllowAll(AllowDeny),
282    Deny(AllowDeny),
283    DenyAll(AllowDeny),
284    CmdAllow(AllowDeny),
285    CmdAllowAll(AllowDeny),
286    CmdDeny(AllowDeny),
287    CmdDenyAll(AllowDeny),
288    AcCheck(AcCheck),
289    CmdAcCheck(AcCheck),
290    #[cmd = 29]
291    DelSource(DelSource),
292    WriteRtc,
293    DFreq(DFreq),
294    #[cmd = 33]
295    Tracking,
296    SourceStats(SourceStats),
297    RtcReport,
298    TrimRtc,
299    CycleLogs,
300    #[cmd = 41]
301    ManualList,
302    ManualDelete(ManualDelete),
303    MakeStep,
304    Activity,
305    ModifyMinStratum(ModifyMinStratum),
306    ModifyPollTarget(ModifyPollTarget),
307    ModifyMaxDelayDevRatio(ModifyMaxDelayDevRatio),
308    Reselect,
309    ReselectDistance(ReselectDistance),
310    ModifyMakeStep(ModifyMakeStep),
311    Smoothing,
312    SmoothTime(SmoothTime),
313    Refresh,
314    ServerStats,
315    #[cmd = 56]
316    Local2(Local),
317    NtpData(NtpData),
318    #[cmd = 62]
319    Shutdown,
320    OnOffline,
321    AddSource(NtpSource),
322    NtpSourceName(NtpSourceName),
323    ResetSources,
324    AuthData(AuthData),
325    ClientAccessesByIndex3(ClientAccessesByIndex),
326    SelectData(SelectData),
327    ReloadSources,
328    DOffset2(DOffset),
329}
330
331impl RequestBody {
332    pub(crate) fn reply_body_length(&self) -> usize {
333        match self {
334            Self::Null => 0,
335            Self::Online(_) => 0,
336            Self::Offline(_) => 0,
337            Self::Burst(_) => 0,
338            Self::ModifyMinPoll(_) => 0,
339            Self::ModifyMaxPoll(_) => 0,
340            Self::Dump => 0,
341            Self::ModifyMaxDelay(_) => 0,
342            Self::ModifyMaxDelayRatio(_) => 0,
343            Self::ModifyMaxUpdateSkew(_) => 0,
344            Self::Logon(_) => 0,
345            Self::SetTime(_) => reply::ManualTimestamp::length(),
346            Self::Manual(_) => 0,
347            Self::NSources => reply::NSources::length(),
348            Self::SourceData(_) => reply::SourceData::length(),
349            Self::Rekey => 0,
350            Self::Allow(_) => 0,
351            Self::AllowAll(_) => 0,
352            Self::Deny(_) => 0,
353            Self::DenyAll(_) => 0,
354            Self::CmdAllow(_) => 0,
355            Self::CmdAllowAll(_) => 0,
356            Self::CmdDeny(_) => 0,
357            Self::CmdDenyAll(_) => 0,
358            Self::AcCheck(_) => 0,
359            Self::CmdAcCheck(_) => 0,
360            Self::DelSource(_) => 0,
361            Self::WriteRtc => 0,
362            Self::DFreq(_) => 0,
363            Self::Tracking => reply::Tracking::length(),
364            Self::SourceStats(_) => reply::SourceStats::length(),
365            Self::RtcReport => reply::Rtc::length(),
366            Self::TrimRtc => 0,
367            Self::CycleLogs => 0,
368            Self::ManualList => reply::ManualList::length(),
369            Self::ManualDelete(_) => 0,
370            Self::MakeStep => 0,
371            Self::Activity => reply::Activity::length(),
372            Self::ModifyMinStratum(_) => 0,
373            Self::ModifyPollTarget(_) => 0,
374            Self::ModifyMaxDelayDevRatio(_) => 0,
375            Self::Reselect => 0,
376            Self::ReselectDistance(_) => 0,
377            Self::ModifyMakeStep(_) => 0,
378            Self::Smoothing => reply::Smoothing::length(),
379            Self::SmoothTime(_) => 0,
380            Self::Refresh => 0,
381            Self::ServerStats => reply::ServerStats4::length(),
382            Self::Local2(_) => 0,
383            Self::NtpData(_) => reply::NtpData::length(),
384            Self::Shutdown => 0,
385            Self::OnOffline => 0,
386            Self::AddSource(_) => 0,
387            Self::NtpSourceName(_) => reply::NtpSourceName::length(),
388            Self::ResetSources => 0,
389            Self::AuthData(_) => reply::AuthData::length(),
390            Self::ClientAccessesByIndex3(_) => reply::ClientAccessesByIndex::length(),
391            Self::SelectData(_) => reply::SelectData::length(),
392            Self::ReloadSources => 0,
393            Self::DOffset2(_) => 0,
394        }
395    }
396}
397
398/// A request to Chrony
399#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
400pub struct Request {
401    /// Sequence number
402    pub sequence: u32,
403    /// Attempt number
404    pub attempt: u16,
405    /// Body of the request
406    pub body: RequestBody,
407}
408
409impl Request {
410    pub fn length(&self) -> usize {
411        std::cmp::max(
412            self.body.body_length() + REQUEST_HEADER_LENGTH,
413            self.body.reply_body_length() + REPLY_HEADER_LENGTH,
414        )
415    }
416
417    pub fn serialize<B: BufMut>(&self, buf: &mut B) {
418        let cmd = self.body.cmd();
419        let body_length = self.body.body_length();
420        let reply_body_length = self.body.reply_body_length();
421        let padding_length = std::cmp::max(
422            body_length + REQUEST_HEADER_LENGTH,
423            reply_body_length + REPLY_HEADER_LENGTH,
424        ) - REQUEST_HEADER_LENGTH
425            - body_length;
426
427        buf.put_u8(6); //Version
428        buf.put_u8(1); //Packet type = request
429        buf.put_u8(0); //Reserved
430        buf.put_u8(0); //Reserved
431        buf.put_u16(cmd);
432        buf.put_u16(self.attempt);
433        buf.put_u32(self.sequence);
434        buf.put_u32(0); //Padding
435        buf.put_u32(0); //Padding
436        self.body.serialize_body(buf);
437        buf.put_slice(vec![0u8; padding_length].as_slice());
438    }
439
440    pub fn deserialize<B: Buf>(buf: &mut B) -> Result<Self, DeserializationError> {
441        if buf.remaining() < REQUEST_HEADER_LENGTH {
442            return Err(DeserializationError::new("message too short"));
443        }
444        if buf.get_u8() != 6 {
445            //Version
446            return Err(DeserializationError::new("unsupported version"));
447        }
448
449        if buf.get_u8() != 1 {
450            //Packet type
451            return Err(DeserializationError::new("wrong packet type"));
452        }
453
454        buf.get_u8(); //Reserved
455        buf.get_u8(); //Reserved
456        let cmd = buf.get_u16();
457        let attempt = buf.get_u16();
458        let sequence = buf.get_u32();
459        buf.get_u32(); //Padding
460        buf.get_u32(); //Padding
461        let body = RequestBody::deserialize_body(cmd, buf)?;
462
463        let body_length = body.body_length();
464        let reply_body_length = body.reply_body_length();
465        let padding_length = std::cmp::max(body_length, reply_body_length) - body_length;
466        if buf.remaining() < padding_length {
467            return Err(DeserializationError::new("insufficient padding"));
468        }
469        buf.advance(padding_length);
470        Ok(Self {
471            sequence,
472            attempt,
473            body,
474        })
475    }
476
477    pub fn cmd(&self) -> u16 {
478        self.body.cmd()
479    }
480}
481
482pub fn increment_attempt(buf: &mut [u8]) {
483    let old_attempt = u16::from_be_bytes(buf[6..8].try_into().unwrap());
484    let new_attempt = old_attempt + 1;
485    buf[6..8].copy_from_slice(&new_attempt.to_be_bytes());
486}