use byteorder::{ByteOrder, NetworkEndian};
use core::convert;
use smoltcp::{Error, Result};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LeapIndicator {
NoWarning,
LastMinute61Sec,
LastMinute59Sec,
AlarmCondition,
Unknown(u8),
}
impl convert::From<u8> for LeapIndicator {
fn from(value: u8) -> Self {
match value {
0 => Self::NoWarning,
1 => Self::LastMinute61Sec,
2 => Self::LastMinute59Sec,
3 => Self::AlarmCondition,
_ => Self::Unknown(value),
}
}
}
impl convert::From<LeapIndicator> for u8 {
fn from(value: LeapIndicator) -> Self {
match value {
LeapIndicator::NoWarning => 0,
LeapIndicator::LastMinute61Sec => 1,
LeapIndicator::LastMinute59Sec => 2,
LeapIndicator::AlarmCondition => 3,
LeapIndicator::Unknown(value) => value,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ProtocolMode {
Reserved,
SymmetricActive,
SymmetricPassive,
Client,
Server,
Broadcast,
NtpControlMessage,
Private,
Unknown(u8),
}
impl convert::From<u8> for ProtocolMode {
fn from(value: u8) -> Self {
match value {
0 => Self::Reserved,
1 => Self::SymmetricActive,
2 => Self::SymmetricPassive,
3 => Self::Client,
4 => Self::Server,
5 => Self::Broadcast,
6 => Self::NtpControlMessage,
7 => Self::Private,
_ => Self::Unknown(value),
}
}
}
impl convert::From<ProtocolMode> for u8 {
fn from(value: ProtocolMode) -> Self {
match value {
ProtocolMode::Reserved => 0,
ProtocolMode::SymmetricActive => 1,
ProtocolMode::SymmetricPassive => 2,
ProtocolMode::Client => 3,
ProtocolMode::Server => 4,
ProtocolMode::Broadcast => 5,
ProtocolMode::NtpControlMessage => 6,
ProtocolMode::Private => 7,
ProtocolMode::Unknown(value) => value,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Stratum {
KissOfDeath,
Primary,
Secondary(u8),
Reserved(u8),
}
impl From<u8> for Stratum {
fn from(s: u8) -> Self {
match s {
0 => Stratum::KissOfDeath,
1 => Stratum::Primary,
2..=15 => Stratum::Secondary(s),
_ => Stratum::Reserved(s),
}
}
}
impl Into<u8> for Stratum {
fn into(self) -> u8 {
match self {
Stratum::KissOfDeath => 0,
Stratum::Primary => 1,
Stratum::Secondary(s) | Stratum::Reserved(s) => s,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct Timestamp {
pub(crate) sec: u32,
pub(crate) frac: u32,
}
impl Timestamp {
fn parse(buffer: &[u8]) -> Result<Timestamp> {
let sec = NetworkEndian::read_u32(buffer.get(0..4).ok_or(Error::Truncated)?);
let frac = NetworkEndian::read_u32(buffer.get(4..8).ok_or(Error::Truncated)?);
Ok(Timestamp { sec, frac })
}
fn emit(&self, buffer: &mut [u8]) {
NetworkEndian::write_u32(&mut buffer[0..4], self.sec);
NetworkEndian::write_u32(&mut buffer[4..8], self.frac);
}
}
#[derive(Debug, PartialEq)]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
}
pub(crate) mod field {
#![allow(non_snake_case)]
#![allow(unused)]
use core::ops;
type Field = ops::Range<usize>;
pub const LI_VN_MODE: usize = 0;
pub const STRATUM: usize = 1;
pub const POLL: usize = 2;
pub const PRECISION: usize = 3;
pub const ROOT_DELAY: Field = 4..8;
pub const ROOT_DISPERSION: Field = 8..12;
pub const REFERENCE_IDENTIFIER: Field = 12..16;
pub const REFERENCE_TIMESTAMP: Field = 16..24;
pub const ORIGINATE_TIMESTAMP: Field = 24..32;
pub const RECEIVE_TIMESTAMP: Field = 32..40;
pub const TRANSMIT_TIMESTAMP: Field = 40..48;
pub const KEY_IDENTIFIER: Field = 48..52;
pub const MESSAGE_DIGEST: Field = 52..68;
pub const LI_MASK: u8 = 0xc0;
pub const LI_SHIFT: u8 = 6;
pub const VN_MASK: u8 = 0x38;
pub const VN_SHIFT: u8 = 3;
pub const MODE_MASK: u8 = 0x07;
pub const MODE_SHIFT: u8 = 0x00;
}
impl<T: AsRef<[u8]>> Packet<T> {
pub fn new_unchecked(buffer: T) -> Packet<T> {
Packet { buffer }
}
pub fn new_checked(buffer: T) -> Result<Packet<T>> {
let packet = Self::new_unchecked(buffer);
packet.check_len()?;
Ok(packet)
}
pub fn check_len(&self) -> Result<()> {
let len = self.buffer.as_ref().len();
if len < field::TRANSMIT_TIMESTAMP.end {
Err(Error::Truncated)
} else {
Ok(())
}
}
pub fn leap_indicator(&self) -> LeapIndicator {
let data = self.buffer.as_ref();
LeapIndicator::from((data[field::LI_VN_MODE] & field::LI_MASK) >> field::LI_SHIFT)
}
pub fn version(&self) -> u8 {
let data = self.buffer.as_ref();
(data[field::LI_VN_MODE] & field::VN_MASK) >> field::VN_SHIFT
}
pub fn protocol_mode(&self) -> ProtocolMode {
let data = self.buffer.as_ref();
ProtocolMode::from((data[field::LI_VN_MODE] & field::MODE_MASK) >> field::MODE_SHIFT)
}
pub fn stratum(&self) -> Stratum {
self.buffer.as_ref()[field::STRATUM].into()
}
pub fn poll_interval(&self) -> u8 {
self.buffer.as_ref()[field::POLL]
}
pub fn precision(&self) -> i8 {
self.buffer.as_ref()[field::PRECISION] as i8
}
pub fn root_delay(&self) -> i32 {
let data = self.buffer.as_ref();
NetworkEndian::read_i32(&data[field::ROOT_DELAY])
}
pub fn root_dispersion(&self) -> u32 {
let data = self.buffer.as_ref();
NetworkEndian::read_u32(&data[field::ROOT_DISPERSION])
}
pub fn ref_identifier(&self) -> [u8; 4] {
let d = &self.buffer.as_ref()[field::REFERENCE_IDENTIFIER];
[d[0], d[1], d[2], d[3]]
}
pub fn ref_timestamp(&self) -> Result<Timestamp> {
let data = self.buffer.as_ref();
Timestamp::parse(&data[field::REFERENCE_TIMESTAMP])
}
pub fn orig_timestamp(&self) -> Result<Timestamp> {
let data = self.buffer.as_ref();
Timestamp::parse(&data[field::ORIGINATE_TIMESTAMP])
}
pub fn recv_timestamp(&self) -> Result<Timestamp> {
let data = self.buffer.as_ref();
Timestamp::parse(&data[field::RECEIVE_TIMESTAMP])
}
pub fn xmit_timestamp(&self) -> Result<Timestamp> {
let data = self.buffer.as_ref();
Timestamp::parse(&data[field::TRANSMIT_TIMESTAMP])
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_leap_indicator(&mut self, li: LeapIndicator) {
let data = self.buffer.as_mut();
let li: u8 = li.into();
data[field::LI_VN_MODE] &= !field::LI_MASK;
data[field::LI_VN_MODE] |= li << field::LI_SHIFT;
}
pub fn set_version(&mut self, vn: u8) {
let data = self.buffer.as_mut();
data[field::LI_VN_MODE] &= !field::VN_MASK;
data[field::LI_VN_MODE] |= vn << field::VN_SHIFT;
}
pub fn set_protocol_mode(&mut self, mode: ProtocolMode) {
let data = self.buffer.as_mut();
let mode: u8 = mode.into();
data[field::LI_VN_MODE] &= !field::MODE_MASK;
data[field::LI_VN_MODE] |= mode << field::MODE_SHIFT;
}
pub fn set_stratum(&mut self, stratum: Stratum) {
self.buffer.as_mut()[field::STRATUM] = stratum.into();
}
pub fn set_poll_interval(&mut self, poll: u8) {
self.buffer.as_mut()[field::POLL] = poll;
}
pub fn set_precision(&mut self, precision: i8) {
self.buffer.as_mut()[field::PRECISION] = precision as u8;
}
pub fn set_root_delay(&mut self, delay: i32) {
let data = &mut self.buffer.as_mut()[field::ROOT_DELAY];
NetworkEndian::write_i32(data, delay);
}
pub fn set_root_dispersion(&mut self, disp: u32) {
let data = &mut self.buffer.as_mut()[field::ROOT_DISPERSION];
NetworkEndian::write_u32(data, disp);
}
pub fn set_ref_identifier(&mut self, id: [u8; 4]) {
self.buffer.as_mut()[field::REFERENCE_IDENTIFIER].copy_from_slice(&id[..]);
}
pub fn set_ref_timestamp(&mut self, ts: Timestamp) {
let field = &mut self.buffer.as_mut()[field::REFERENCE_TIMESTAMP];
ts.emit(field);
}
pub fn set_orig_timestamp(&mut self, ts: Timestamp) {
let field = &mut self.buffer.as_mut()[field::ORIGINATE_TIMESTAMP];
ts.emit(field);
}
pub fn set_recv_timestamp(&mut self, ts: Timestamp) {
let field = &mut self.buffer.as_mut()[field::RECEIVE_TIMESTAMP];
ts.emit(field);
}
pub fn set_xmit_timestamp(&mut self, ts: Timestamp) {
let field = &mut self.buffer.as_mut()[field::TRANSMIT_TIMESTAMP];
ts.emit(field);
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Repr {
pub leap_indicator: LeapIndicator,
pub version: u8,
pub protocol_mode: ProtocolMode,
pub stratum: Stratum,
pub poll_interval: u8,
pub precision: i8,
pub root_delay: i32,
pub root_dispersion: u32,
pub ref_identifier: [u8; 4],
pub ref_timestamp: Timestamp,
pub orig_timestamp: Timestamp,
pub recv_timestamp: Timestamp,
pub xmit_timestamp: Timestamp,
}
impl Repr {
pub fn buffer_len(&self) -> usize {
field::KEY_IDENTIFIER.start
}
pub fn parse<T>(packet: &Packet<&T>) -> Result<Self>
where
T: AsRef<[u8]> + ?Sized,
{
Ok(Repr {
leap_indicator: packet.leap_indicator(),
version: packet.version(),
protocol_mode: packet.protocol_mode(),
stratum: packet.stratum(),
poll_interval: packet.poll_interval(),
precision: packet.precision(),
root_delay: packet.root_delay(),
root_dispersion: packet.root_dispersion(),
ref_identifier: packet.ref_identifier(),
ref_timestamp: packet.ref_timestamp()?,
orig_timestamp: packet.orig_timestamp()?,
recv_timestamp: packet.recv_timestamp()?,
xmit_timestamp: packet.xmit_timestamp()?,
})
}
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
packet.set_leap_indicator(self.leap_indicator);
packet.set_version(self.version);
packet.set_protocol_mode(self.protocol_mode);
packet.set_stratum(self.stratum);
packet.set_poll_interval(self.poll_interval);
packet.set_precision(self.precision);
packet.set_root_delay(self.root_delay);
packet.set_root_dispersion(self.root_dispersion);
packet.set_ref_identifier(self.ref_identifier);
packet.set_ref_timestamp(self.ref_timestamp);
packet.set_orig_timestamp(self.orig_timestamp);
packet.set_recv_timestamp(self.recv_timestamp);
packet.set_xmit_timestamp(self.xmit_timestamp);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
static PACKET_BYTES: [u8; 48] = [
0x24, 0x02, 0x00, 0xe6, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x6f, 0x50, 0x42, 0xe0,
0x02, 0xe2, 0x6c, 0x32, 0xf1, 0x0e, 0xd5, 0xfe, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xe2, 0x6c, 0x35, 0x11, 0x6a, 0x8c, 0xe6, 0x47, 0xe2, 0x6c, 0x35, 0x11, 0x6a,
0x8d, 0xf8, 0x8f,
];
#[test]
fn test_deconstruct() {
let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
assert_eq!(packet.leap_indicator(), LeapIndicator::NoWarning);
assert_eq!(packet.version(), 4);
assert_eq!(packet.protocol_mode(), ProtocolMode::Server);
assert_eq!(packet.stratum(), Stratum::Secondary(2));
assert_eq!(packet.poll_interval(), 0);
assert_eq!(packet.precision(), -26);
assert_eq!(packet.root_delay(), 0x120);
assert_eq!(packet.root_dispersion(), 0x6f);
assert_eq!(packet.ref_identifier(), [80, 66, 224, 2]);
assert_eq!(
packet.ref_timestamp(),
Ok(Timestamp {
sec: 0xe26c32f1,
frac: 0x0ed5fea9,
})
);
assert_eq!(
packet.orig_timestamp(),
Ok(Timestamp {
sec: 0x00000000,
frac: 0x00000000
})
);
assert_eq!(
packet.recv_timestamp(),
Ok(Timestamp {
sec: 0xe26c3511,
frac: 0x6a8ce647,
})
);
assert_eq!(
packet.xmit_timestamp(),
Ok(Timestamp {
sec: 0xe26c3511,
frac: 0x6a8df88f
})
)
}
#[test]
fn test_construct() {
let mut bytes = vec![0xa5; 48];
let mut packet = Packet::new_unchecked(&mut bytes);
packet.set_leap_indicator(LeapIndicator::NoWarning);
packet.set_version(4);
packet.set_protocol_mode(ProtocolMode::Server);
packet.set_stratum(Stratum::Secondary(2));
packet.set_poll_interval(0);
packet.set_precision(-26);
packet.set_root_delay(0x120);
packet.set_root_dispersion(0x6f);
packet.set_ref_identifier([80, 66, 224, 2]);
packet.set_ref_timestamp(Timestamp {
sec: 0xe26c32f1,
frac: 0x0ed5fea9,
});
packet.set_orig_timestamp(Timestamp {
sec: 0x00000000,
frac: 0x00000000,
});
packet.set_recv_timestamp(Timestamp {
sec: 0xe26c3511,
frac: 0x6a8ce647,
});
packet.set_xmit_timestamp(Timestamp {
sec: 0xe26c3511,
frac: 0x6a8df88f,
});
assert_eq!(&packet.buffer[..], &PACKET_BYTES[..]);
}
fn packet_repr() -> Repr {
Repr {
leap_indicator: LeapIndicator::NoWarning,
version: 4,
protocol_mode: ProtocolMode::Server,
stratum: Stratum::Secondary(2),
poll_interval: 0,
precision: -26,
root_delay: 0x120,
root_dispersion: 0x6f,
ref_identifier: [80, 66, 224, 2],
ref_timestamp: Timestamp {
sec: 0xe26c32f1,
frac: 0x0ed5fea9,
},
orig_timestamp: Timestamp {
sec: 0x00000000,
frac: 0x00000000,
},
recv_timestamp: Timestamp {
sec: 0xe26c3511,
frac: 0x6a8ce647,
},
xmit_timestamp: Timestamp {
sec: 0xe26c3511,
frac: 0x6a8df88f,
},
}
}
#[test]
fn test_parse() {
let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
let repr = Repr::parse(&packet).unwrap();
assert_eq!(repr, packet_repr());
}
#[test]
fn test_emit() {
let mut bytes = vec![0xa5; 48];
let mut packet = Packet::new_unchecked(&mut bytes);
packet_repr().emit(&mut packet).unwrap();
assert_eq!(&packet.buffer[..], &PACKET_BYTES[..]);
}
}