ntp_parser/
ntp.rs

1use nom::bytes::streaming::take;
2use nom::combinator::{complete, map, map_parser, opt};
3use nom::error::{make_error, ErrorKind};
4use nom::multi::many1;
5use nom::number::streaming::be_u8;
6pub use nom::{Err, IResult};
7use nom_derive::*;
8
9#[derive(Debug, PartialEq)]
10pub enum NtpPacket<'a> {
11    V3(NtpV3Packet<'a>),
12    V4(NtpV4Packet<'a>),
13}
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq, NomBE)]
16pub struct NtpMode(pub u8);
17
18#[allow(non_upper_case_globals)]
19impl NtpMode {
20    pub const Reserved: NtpMode = NtpMode(0);
21    pub const SymmetricActive: NtpMode = NtpMode(1);
22    pub const SymmetricPassive: NtpMode = NtpMode(2);
23    pub const Client: NtpMode = NtpMode(3);
24    pub const Server: NtpMode = NtpMode(4);
25    pub const Broadcast: NtpMode = NtpMode(5);
26    pub const NtpControlMessage: NtpMode = NtpMode(6);
27    pub const Private: NtpMode = NtpMode(7);
28}
29
30/// An NTP version 3 packet
31#[derive(Debug, PartialEq, NomBE)]
32pub struct NtpV3Packet<'a> {
33    #[nom(PreExec = "let (i, b0) = be_u8(i)?;")]
34    #[nom(Value(b0 >> 6))]
35    pub li: u8,
36    #[nom(Value((b0 >> 3) & 0b111))]
37    pub version: u8,
38    #[nom(Value(NtpMode(b0 & 0b111)))]
39    pub mode: NtpMode,
40    pub stratum: u8,
41    pub poll: i8,
42    pub precision: i8,
43    pub root_delay: u32,
44    pub root_dispersion: u32,
45    pub ref_id: u32,
46    pub ts_ref: u64,
47    pub ts_orig: u64,
48    pub ts_recv: u64,
49    pub ts_xmit: u64,
50
51    #[nom(Parse = "opt(complete(take(12usize)))")]
52    pub authenticator: Option<&'a [u8]>,
53}
54
55/// An NTP version 4 packet
56#[derive(Debug, PartialEq, NomBE)]
57pub struct NtpV4Packet<'a> {
58    #[nom(PreExec = "let (i, b0) = be_u8(i)?;")]
59    #[nom(Value(b0 >> 6))]
60    pub li: u8,
61    #[nom(Value((b0 >> 3) & 0b111))]
62    pub version: u8,
63    #[nom(Value(NtpMode(b0 & 0b111)))]
64    pub mode: NtpMode,
65    pub stratum: u8,
66    pub poll: i8,
67    pub precision: i8,
68    pub root_delay: u32,
69    pub root_dispersion: u32,
70    pub ref_id: u32,
71    pub ts_ref: u64,
72    pub ts_orig: u64,
73    pub ts_recv: u64,
74    pub ts_xmit: u64,
75
76    #[nom(Parse = "try_parse_extensions")]
77    pub extensions: Vec<NtpExtension<'a>>,
78    #[nom(Cond(!i.is_empty()))]
79    pub auth: Option<NtpMac<'a>>,
80}
81
82impl<'a> NtpV4Packet<'a> {
83    pub fn get_precision(&self) -> f32 {
84        2.0_f32.powf(self.precision as f32)
85    }
86}
87
88#[derive(Debug, PartialEq, NomBE)]
89pub struct NtpExtension<'a> {
90    pub field_type: u16,
91    pub length: u16,
92    #[nom(Parse = "take(length)")]
93    pub value: &'a [u8],
94    /*padding*/
95}
96
97#[derive(Debug, PartialEq, NomBE)]
98pub struct NtpMac<'a> {
99    pub key_id: u32,
100    #[nom(Parse = "take(16usize)")]
101    pub mac: &'a [u8],
102}
103
104#[inline]
105pub fn parse_ntp_extension(i: &[u8]) -> IResult<&[u8], NtpExtension> {
106    NtpExtension::parse(i)
107}
108
109// Attempt to parse extensions.
110//
111// See section 7.5 of [RFC5905] and [RFC7822]:
112// In NTPv4, one or more extension fields can be inserted after the
113//    header and before the MAC, which is always present when an extension
114//    field is present.
115//
116// So:
117//  if == 20, only MAC
118//  if >  20, ext + MAC
119//  if ==  0, nothing
120//  else      error
121fn try_parse_extensions(i: &[u8]) -> IResult<&[u8], Vec<NtpExtension>> {
122    if i.is_empty() || i.len() == 20 {
123        // if empty, or if remaining length is exactly the MAC length (20), assume we do not have
124        // extensions
125        return Ok((i, Vec::new()));
126    }
127    if i.len() < 20 {
128        return Err(Err::Error(make_error(i, ErrorKind::Eof)));
129    }
130    map_parser(take(i.len() - 20), many1(complete(parse_ntp_extension)))(i)
131}
132
133/// Parse an NTP version 3 packet (RFC 1305)
134#[inline]
135pub fn parse_ntpv3(i: &[u8]) -> IResult<&[u8], NtpV3Packet> {
136    NtpV3Packet::parse(i)
137}
138
139/// Parse an NTP version 4 packet (RFC 1305)
140#[inline]
141pub fn parse_ntpv4(i: &[u8]) -> IResult<&[u8], NtpV4Packet> {
142    NtpV4Packet::parse(i)
143}
144
145/// Parse an NTP packet, version 3 or 4
146#[inline]
147pub fn parse_ntp(i: &[u8]) -> IResult<&[u8], NtpPacket> {
148    let (_, b0) = be_u8(i)?;
149    match (b0 >> 3) & 0b111 {
150        3 => map(NtpV3Packet::parse, NtpPacket::V3)(i),
151        4 => map(NtpV4Packet::parse, NtpPacket::V4)(i),
152        _ => Err(Err::Error(make_error(i, ErrorKind::Tag))),
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use crate::ntp::*;
159
160    static NTP_REQ1: &[u8] = &[
161        0xd9, 0x00, 0x0a, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x90, 0x00, 0x00, 0x00,
162        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x02, 0x04, 0xec, 0xec,
164        0x42, 0xee, 0x92,
165    ];
166
167    #[test]
168    fn test_ntp_packet_simple() {
169        let empty = &b""[..];
170        let bytes = NTP_REQ1;
171        let expected = NtpV4Packet {
172            li: 3,
173            version: 3,
174            mode: NtpMode::SymmetricActive,
175            stratum: 0,
176            poll: 10,
177            precision: -6,
178            root_delay: 0,
179            root_dispersion: 0x010290,
180            ref_id: 0,
181            ts_ref: 0,
182            ts_orig: 0,
183            ts_recv: 0,
184            ts_xmit: 14195914391047827090u64,
185            extensions: Vec::new(),
186            auth: None,
187        };
188        let res = parse_ntpv4(&bytes);
189        assert_eq!(res, Ok((empty, expected)));
190    }
191
192    static NTP_REQ2: &[u8] = &[
193        0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x25, 0xcc, 0x13, 0x2b,
196        0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x52, 0x80, 0x0c, 0x2b, 0x59, 0x00, 0x64, 0x66,
197        0x84, 0xf4, 0x4c, 0xa4, 0xee, 0xce, 0x12, 0xb8,
198    ];
199
200    #[test]
201    fn test_ntp_packet_mac() {
202        let empty = &b""[..];
203        let bytes = NTP_REQ2;
204        let expected = NtpV4Packet {
205            li: 0,
206            version: 4,
207            mode: NtpMode::Client,
208            stratum: 0,
209            poll: 0,
210            precision: 0,
211            root_delay: 12,
212            root_dispersion: 0,
213            ref_id: 0,
214            ts_ref: 0,
215            ts_orig: 0,
216            ts_recv: 0,
217            ts_xmit: 14710388140573593600,
218            extensions: Vec::new(),
219            auth: Some(NtpMac {
220                key_id: 1,
221                mac: &bytes[52..],
222            }),
223        };
224        let res = parse_ntpv4(&bytes);
225        assert_eq!(res, Ok((empty, expected)));
226    }
227
228    static NTP_REQ2B: &[u8] = &[
229        0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x25, 0xcc, 0x13, 0x2b,
232        0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x52, 0x80, 0x0c, 0x2b,
233        0x59, 0x00, 0x64, 0x66, 0x84, 0xf4, 0x4c, 0xa4, 0xee, 0xce, 0x12, 0xb8,
234    ];
235
236    #[test]
237    fn test_ntp_packet_extension() {
238        let empty = &b""[..];
239        let bytes = NTP_REQ2B;
240        let expected = NtpV4Packet {
241            li: 0,
242            version: 4,
243            mode: NtpMode::Client,
244            stratum: 0,
245            poll: 0,
246            precision: 0,
247            root_delay: 12,
248            root_dispersion: 0,
249            ref_id: 0,
250            ts_ref: 0,
251            ts_orig: 0,
252            ts_recv: 0,
253            ts_xmit: 14710388140573593600,
254            extensions: vec![NtpExtension {
255                field_type: 0,
256                length: 0,
257                value: empty,
258            }],
259            auth: Some(NtpMac {
260                key_id: 1,
261                mac: &bytes[56..],
262            }),
263        };
264        let res = parse_ntpv4(&bytes);
265        assert_eq!(res, Ok((empty, expected)));
266    }
267
268    // from wireshark test captures 'ntp.pcap'
269    static NTPV3_REQ: &[u8] = &[
270        0x1b, 0x04, 0x06, 0xf5, 0x00, 0x00, 0x10, 0x0d, 0x00, 0x00, 0x05, 0x57, 0x82, 0xdc, 0x18,
271        0x18, 0xba, 0x29, 0x66, 0x36, 0x7d, 0xd0, 0x00, 0x00, 0xba, 0x29, 0x66, 0x36, 0x7d, 0x58,
272        0x40, 0x00, 0xba, 0x29, 0x66, 0x36, 0x7d, 0xd0, 0x00, 0x00, 0xba, 0x29, 0x66, 0x76, 0x7d,
273        0x50, 0x50, 0x00,
274    ];
275
276    #[test]
277    fn test_ntp_packet_v3() {
278        let empty = &b""[..];
279        let bytes = NTPV3_REQ;
280        let expected = NtpV3Packet {
281            li: 0,
282            version: 3,
283            mode: NtpMode::Client,
284            stratum: 4,
285            poll: 6,
286            precision: -11,
287            root_delay: 4109,
288            root_dispersion: 0x0557,
289            ref_id: 0x82dc1818,
290            ts_ref: 0xba296636_7dd00000,
291            ts_orig: 0xba296636_7d584000,
292            ts_recv: 0xba296636_7dd00000,
293            ts_xmit: 0xba296676_7d505000,
294            authenticator: None,
295        };
296        let res = NtpV3Packet::parse(&bytes);
297        assert_eq!(res, Ok((empty, expected)));
298    }
299}