ntp_proto/
identifiers.rs

1use std::net::IpAddr;
2
3use md5::{Digest, Md5};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct ReferenceId(u32);
8
9impl ReferenceId {
10    // Note: Names chosen to match the identifiers given in rfc5905
11    pub const KISS_DENY: ReferenceId = ReferenceId(u32::from_be_bytes(*b"DENY"));
12    pub const KISS_RATE: ReferenceId = ReferenceId(u32::from_be_bytes(*b"RATE"));
13    pub const KISS_RSTR: ReferenceId = ReferenceId(u32::from_be_bytes(*b"RSTR"));
14    pub const NONE: ReferenceId = ReferenceId(u32::from_be_bytes(*b"XNON"));
15    pub const SOCK: ReferenceId = ReferenceId(u32::from_be_bytes(*b"SOCK"));
16    pub const PPS: ReferenceId = ReferenceId(u32::from_be_bytes(*b"PPS\0"));
17
18    // Network Time Security (NTS) negative-acknowledgment (NAK), from rfc8915
19    pub const KISS_NTSN: ReferenceId = ReferenceId(u32::from_be_bytes(*b"NTSN"));
20
21    pub fn from_ip(addr: IpAddr) -> ReferenceId {
22        match addr {
23            IpAddr::V4(addr) => ReferenceId(u32::from_be_bytes(addr.octets())),
24            IpAddr::V6(addr) => ReferenceId(u32::from_be_bytes(
25                Md5::digest(addr.octets())[0..4].try_into().unwrap(),
26            )),
27        }
28    }
29
30    pub(crate) const fn from_int(value: u32) -> ReferenceId {
31        ReferenceId(value)
32    }
33
34    pub(crate) fn is_deny(&self) -> bool {
35        *self == Self::KISS_DENY
36    }
37
38    pub(crate) fn is_rate(&self) -> bool {
39        *self == Self::KISS_RATE
40    }
41
42    pub(crate) fn is_rstr(&self) -> bool {
43        *self == Self::KISS_RSTR
44    }
45
46    pub(crate) fn is_ntsn(&self) -> bool {
47        *self == Self::KISS_NTSN
48    }
49
50    pub(crate) fn to_bytes(self) -> [u8; 4] {
51        self.0.to_be_bytes()
52    }
53
54    pub(crate) fn from_bytes(bits: [u8; 4]) -> ReferenceId {
55        ReferenceId(u32::from_be_bytes(bits))
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn referenceid_serialization_roundtrip() {
65        let a = [12, 34, 56, 78];
66        let b = ReferenceId::from_bytes(a);
67        let c = b.to_bytes();
68        let d = ReferenceId::from_bytes(c);
69        assert_eq!(a, c);
70        assert_eq!(b, d);
71    }
72
73    #[test]
74    fn referenceid_kiss_codes() {
75        let a = [b'R', b'A', b'T', b'E'];
76        let b = ReferenceId::from_bytes(a);
77        assert!(b.is_rate());
78
79        let a = [b'R', b'S', b'T', b'R'];
80        let b = ReferenceId::from_bytes(a);
81        assert!(b.is_rstr());
82
83        let a = [b'D', b'E', b'N', b'Y'];
84        let b = ReferenceId::from_bytes(a);
85        assert!(b.is_deny());
86    }
87
88    #[test]
89    fn referenceid_from_ipv4() {
90        let ip: IpAddr = "12.34.56.78".parse().unwrap();
91        let rep = [12, 34, 56, 78];
92        let a = ReferenceId::from_ip(ip);
93        let b = ReferenceId::from_bytes(rep);
94        assert_eq!(a, b);
95
96        // TODO: Generate and add a testcase for ipv6 addresses once
97        // we have access to an ipv6 network.
98    }
99}